1 // rounding-algorithms.hpp
2 //
3 // General Rounding Algorithms
4 // Copyright (c) 2008 Michael Thomas Greer
5 // Boost Software License - Version 1.0 - August 17th, 2003
6 //
7 // Permission is hereby granted, free of charge, to any person or organization
8 // obtaining a copy of the software and accompanying documentation covered by
9 // this license (the "Software") to use, reproduce, display, distribute,
10 // execute, and transmit the Software, and to prepare derivative works of the
11 // Software, and to permit third-parties to whom the Software is furnished to
12 // do so, all subject to the following:
13 //
14 // The copyright notices in the Software and this entire statement, including
15 // the above license grant, this restriction and the following disclaimer,
16 // must be included in all copies of the Software, in whole or in part, and
17 // all derivative works of the Software, unless such copies or derivative
18 // works are solely in the form of machine-executable object code generated by
19 // a source language processor.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24 // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25 // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 // DEALINGS IN THE SOFTWARE.
28 //
29 //----------------------------------------------------------------------------
30 // Reference
31 // <http://www.pldesignline.com/howto/showArticle.jhtml;?articleID=175801189>
32 //
33 //----------------------------------------------------------------------------
34 // In this library, symmetric functions are indicated by a zero at the end
35 // of the function name.
36 //
37 // If you want a different default epsilon make sure to change
38 //
39 //   #define ROUNDING_EPSILON 0.001
40 //
41 // to whatever you want it to be. (I wanted to make it so that you could
42 // define a different default epsilon each time you #included the file, but
43 // I haven't figured out how to get around the template restrictions yet.)
44 //
45 
46 #ifndef ROUNDING_ALGORITHMS_HPP
47 #define ROUNDING_ALGORITHMS_HPP
48 
49 #ifndef ROUNDING_EPSILON
50 #define ROUNDING_EPSILON 0.0000001
51 #endif
52 
53 #include <cmath>
54 #include <cstdlib>
55 #include <ciso646>
56 
57 namespace rounding
58 {
59 
60     //--------------------------------------------------------------------------
61     // round down
62     // Bias: -Infinity
63     using std::floor;
64 
65     //--------------------------------------------------------------------------
66     // round up
67     // Bias: +Infinity
68     using std::ceil;
69 
70     //--------------------------------------------------------------------------
71     // symmetric round down
72     // Bias: towards zero
73     template <typename FloatType>
floor0(const FloatType & value)74     FloatType floor0( const FloatType& value )
75     {
76         FloatType result = std::floor( std::fabs( value ) );
77         return (value < 0.0) ? -result : result;
78     }
79 
80     //--------------------------------------------------------------------------
81     // A common alias for floor0()
82     // (notwithstanding hardware quirks)
83     template <typename FloatType>
84     inline
trunc(const FloatType & value)85     FloatType trunc( const FloatType& value )
86     {
87         return floor0( value );
88     }
89 
90     //--------------------------------------------------------------------------
91     // symmetric round up
92     // Bias: away from zero
93     template <typename FloatType>
ceil0(const FloatType & value)94     FloatType ceil0( const FloatType& value )
95     {
96         FloatType result = std::ceil( std::fabs( value ) );
97         return (value < 0.0) ? -result : result;
98     }
99 
100     //--------------------------------------------------------------------------
101     // Common rounding: round half up
102     // Bias: +Infinity
103     template <typename FloatType>
roundhalfup(const FloatType & value)104     FloatType roundhalfup( const FloatType& value )
105     {
106         return std::floor( value +0.5 );
107     }
108 
109     //--------------------------------------------------------------------------
110     // Round half down
111     // Bias: -Infinity
112     template <typename FloatType>
roundhalfdown(const FloatType & value)113     FloatType roundhalfdown( const FloatType& value )
114     {
115         return std::ceil( value -0.5 );
116     }
117 
118     //--------------------------------------------------------------------------
119     // symmetric round half down
120     // Bias: towards zero
121     template <typename FloatType>
roundhalfdown0(const FloatType & value)122     FloatType roundhalfdown0( const FloatType& value )
123     {
124         FloatType result = roundhalfdown( std::fabs( value ) );
125         return (value < 0.0) ? -result : result;
126     }
127 
128     //--------------------------------------------------------------------------
129     // symmetric round half up
130     // Bias: away from zero
131     template <typename FloatType>
roundhalfup0(const FloatType & value)132     FloatType roundhalfup0( const FloatType& value )
133     {
134         FloatType result = roundhalfup( std::fabs( value ) );
135         return (value < 0.0) ? -result : result;
136     }
137 
138     //--------------------------------------------------------------------------
139     // round half even (banker's rounding)
140     // Bias: none
141     template <typename FloatType>
142     FloatType roundhalfeven(
143                             const FloatType& value,
144                             const FloatType& epsilon = ROUNDING_EPSILON
145                             ) {
146         if (value < 0.0) return -roundhalfeven <FloatType> ( -value, epsilon );
147 
148         FloatType ipart;
149         std::modf( value, &ipart );
150 
151         // If 'value' is exctly halfway between two integers
152         if ((value -(ipart +0.5)) < epsilon)
153         {
154             // If 'ipart' is even then return 'ipart'
155             if (std::fmod( ipart, 2.0 ) < epsilon)
156                 return ipart;
157 
158             // Else return the nearest even integer
159             return ceil0( ipart +0.5 );
160         }
161 
162         // Otherwise use the usual round to closest
163         // (Either symmetric half-up or half-down will do0
164         return roundhalfup0( value );
165     }
166 
167     //--------------------------------------------------------------------------
168     // round random
169     // Bias: generator's bias
170     template <typename FloatType, typename RandValue, typename RandomGenerator>
roundrandom(const FloatType & value,const RandValue & mid,RandomGenerator & g)171     FloatType roundrandom(
172                           const FloatType& value,
173                           const RandValue& mid,
174                           RandomGenerator& g
175                           ) {
176         if (g() < mid)
177             return roundhalfup0( value );
178         return roundhalfdown0( value );
179     }
180 
181     //--------------------------------------------------------------------------
182     // default round random
183     // Bias: rand()
184     template <typename FloatType>
roundrandom(const FloatType & value)185     FloatType roundrandom( const FloatType& value )
186     {
187         return roundrandom <FloatType, int, int(*)()> ( value, RAND_MAX /2, &rand );
188     }
189 }
190 
191 #endif
192