SnapValue.h
1/* Distributed under the Apache License, Version 2.0.
2 See accompanying NOTICE file for details.*/
3
4//----------------------------------------------------------------------------
8// This class acts like an ordinary double precision floating point value, except
9// that it "snaps" to the nearest integer whenever it is close enough to an
10// integer that we can presume that the difference is due to rounding error.
11// The purpose of having this is that we want to be able to represent fractional
12// exponents of a unit symbol in a CompoundUnitElement, but we want to make sure
13// that when doing conversions to other units, we don't spuriously find a mismatch
14// in the corresponding UnitDimension objects of the two CompoundUnit objects, simply
15// because, for example, one has a meters exponent of 1.0 and the other has a
16// meters exponent of 0.9999998 due to some roundabout computation in which we
17// took the square root of an acre.
18//----------------------------------------------------------------------------
19#pragma once
20#include <float.h>
21
22// The use of FLT_EPSILON instead of
23// DBL_EPSILON is just in case our values are the result of some
24// funky compiler intrinsic that does stuff in single precision. This should
25// be fine because the only thing we're trying to do, really, is represent
26// rational numbers of the form [m / n] where n is small, e.g. 1/2, 1/3, etc.
27// the scale factor (16) is to account for the fact that the rounding error
28// will be larger for values with a larger integer portion (due to the finite
29// number of mantissa bits). Ideally, we should scale-up FLT_EPSILON by the
30// size of the value being snapped, and compare that to the error, but then we
31// have to do that extra multiplication even though most values will be within
32// small range anyway, and we'd have to consider when the original value is close
33// to zero, the multiplication by FLT_EPSILON might underflow. I don't think it
34// will, but I don't trust my intuition on IEEE 754.
35#define SNAP_TOLERANCE (FLT_EPSILON * 16.0)
36
38{
39private:
40 // A negating constructor for use by unary-minus only. Add in a dummy arg
41 // so that the compiler can resolve to the correct constructor. This
42 // skips the snapping because we're negating the value from an existing
43 // CSnapValue
44 CSnapValue(const CSnapValue &src, bool /*dummyflag*/)
45 :m_dVal(-src.m_dVal)
46 {
47 // nothing
48 }
49
50public:
51 // Make it look, smell, and act like a double
52
53 // Constructors
55 {
56 m_dVal = 0.0;
57 }
58
60 :m_dVal(src.m_dVal)
61 {
62 // nothing
63 }
64
65 CSnapValue(double val)
66 :m_dVal(val)
67 {
68 Snap();
69 }
70
71 CSnapValue(float val)
72 :m_dVal(val)
73 {
74 Snap();
75 }
76
77 CSnapValue(int val)
78 :m_dVal(val)
79 {
80 // No need to snap if we're initializing with an int
81 }
82
83 operator double() const
84 {
85 return m_dVal;
86 }
87
88 // Assignment and arithmetic operators
89 // See Myers, "More Effective C++", Addison-Wesley
91 {
92 m_dVal = rhs.m_dVal;
93 return *this;
94 }
95
97 {
98 m_dVal *= rhs.m_dVal;
99 Snap();
100 return *this;
101 }
102
103 const CSnapValue operator*(const CSnapValue &rhs) const
104 {
105 return CSnapValue(*this) *= rhs;
106 }
107
109 {
110 m_dVal /= rhs.m_dVal;
111 Snap();
112 return *this;
113 }
114
115 const CSnapValue operator/(const CSnapValue &rhs) const
116 {
117 return CSnapValue(*this) /= rhs;
118 }
119
121 {
122 m_dVal += rhs.m_dVal;
123 Snap();
124 return *this;
125 }
126
127 const CSnapValue operator+(const CSnapValue &rhs) const
128 {
129 return CSnapValue(*this) += rhs;
130 }
131
132 const CSnapValue operator+() const
133 {
134 return CSnapValue(*this);
135 }
136
138 {
139 m_dVal -= rhs.m_dVal;
140 Snap();
141 return *this;
142 }
143
144 const CSnapValue operator-(const CSnapValue &rhs) const
145 {
146 return CSnapValue(*this) -= rhs;
147 }
148
149 const CSnapValue operator-() const
150 {
151 // Invoke the special negating constructor!!!
152 return CSnapValue(*this,true);
153 }
154
155 // Increment and decrement
156 // prefix
158 {
159 *this += CSnapValue(1.0);
160 return *this;
161 }
162
164 {
165 *this -= CSnapValue(1.0);
166 return *this;
167 }
168
169 // postfix
171 {
172 CSnapValue oldValue = *this;
173 ++(*this);
174 return oldValue;
175 }
176
178 {
179 CSnapValue oldValue = *this;
180 --(*this);
181 return oldValue;
182 }
183
184 const double &GetValue() const
185 {
186 return m_dVal;
187 };
188
189
190 // Relational operators
191
192 // http://support.microsoft.com/kb/168958 says we need to define operators < and ==
193 // for this if we want to export the vector of these contained in UnitDimension.
194 bool operator< (const CSnapValue &rhs) const
195 {
196 return (m_dVal < rhs.m_dVal);
197 }
198
199 bool operator== (const CSnapValue &rhs) const
200 {
201 return (m_dVal == rhs.m_dVal);
202 }
203
204 bool operator!=(const CSnapValue &rhs) const
205 {
206 return (m_dVal != rhs.m_dVal);
207 }
208
209 bool operator>(const CSnapValue &rhs) const
210 {
211 return (m_dVal > rhs.m_dVal);
212 }
213
214 bool operator<=(const CSnapValue &rhs) const
215 {
216 return (m_dVal <= rhs.m_dVal);
217 }
218
219 bool operator>=(const CSnapValue &rhs) const
220 {
221 return (m_dVal >= rhs.m_dVal);
222 }
223
224 // Provide versions of relational operators that take other types to avoid
225 // method resolution ambiguity
226 bool operator< (const double &rhs) const
227 {
228 return (m_dVal < rhs);
229 }
230
231 bool operator== (const double &rhs) const
232 {
233 return (m_dVal == rhs);
234 }
235
236 bool operator!=(const double &rhs) const
237 {
238 return (m_dVal != rhs);
239 }
240
241 bool operator>(const double &rhs) const
242 {
243 return (m_dVal > rhs);
244 }
245
246 bool operator<=(const double &rhs) const
247 {
248 return (m_dVal <= rhs);
249 }
250
251 bool operator>=(const double &rhs) const
252 {
253 return (m_dVal >= rhs);
254 }
255
256 bool operator< (const int &rhs) const
257 {
258 return (m_dVal < rhs);
259 }
260
261 bool operator== (const int &rhs) const
262 {
263 return (m_dVal == rhs);
264 }
265
266 bool operator!=(const int &rhs) const
267 {
268 return (m_dVal != rhs);
269 }
270
271 bool operator>(const int &rhs) const
272 {
273 return (m_dVal > rhs);
274 }
275
276 bool operator<=(const int &rhs) const
277 {
278 return (m_dVal <= rhs);
279 }
280
281 bool operator>=(const int &rhs) const
282 {
283 return (m_dVal >= rhs);
284 }
285
286protected:
287 void Snap()
288 {
289
290 // Need to be careful here, because the int and frac portions
291 // that modf returns are THE SAME SIGN, and we need to snap
292 // when the fractional value is close to 0 or to +/- 1.
293 double intval;
294 double bump = 1.0;
295 double frac = modf(m_dVal,&intval);
296 if (frac < 0)
297 {
298 frac = -frac;
299 bump = -1.0;
300 }
301
302 // frac is positive now. We need to snap differently
303 // depending on whether it's close to zero or close to 1
304 if (frac < 0.5)
305 {
306 // See if it's close to zero
307 if (frac < SNAP_TOLERANCE)
308 {
309 m_dVal = intval;
310 }
311 }
312 else
313 {
314 // See if it's close to one
315 if ((1.0 - frac) < SNAP_TOLERANCE)
316 {
317 m_dVal = intval + bump;
318 };
319 }
320
321 }
322
323private:
324 double m_dVal;
325};
326
327inline double pow(double x, const CSnapValue &y)
328{
329 return pow(x,y.GetValue());
330}
Definition: SnapValue.h:38
bool operator!=(const double &rhs) const
Definition: SnapValue.h:236
void Snap()
Definition: SnapValue.h:287
bool operator<=(const int &rhs) const
Definition: SnapValue.h:276
CSnapValue & operator/=(const CSnapValue &rhs)
Definition: SnapValue.h:108
CSnapValue(const CSnapValue &src)
Definition: SnapValue.h:59
CSnapValue(const CSnapValue &src, bool)
Definition: SnapValue.h:44
bool operator>=(const int &rhs) const
Definition: SnapValue.h:281
const CSnapValue operator-(const CSnapValue &rhs) const
Definition: SnapValue.h:144
CSnapValue & operator=(const CSnapValue &rhs)
Definition: SnapValue.h:90
bool operator!=(const CSnapValue &rhs) const
Definition: SnapValue.h:204
bool operator>=(const CSnapValue &rhs) const
Definition: SnapValue.h:219
CSnapValue()
Definition: SnapValue.h:54
CSnapValue(int val)
Definition: SnapValue.h:77
const double & GetValue() const
Definition: SnapValue.h:184
CSnapValue(float val)
Definition: SnapValue.h:71
bool operator!=(const int &rhs) const
Definition: SnapValue.h:266
double m_dVal
Definition: SnapValue.h:324
bool operator>(const int &rhs) const
Definition: SnapValue.h:271
CSnapValue & operator-=(const CSnapValue &rhs)
Definition: SnapValue.h:137
const CSnapValue operator*(const CSnapValue &rhs) const
Definition: SnapValue.h:103
bool operator<(const CSnapValue &rhs) const
Definition: SnapValue.h:194
bool operator>(const CSnapValue &rhs) const
Definition: SnapValue.h:209
CSnapValue & operator++()
Definition: SnapValue.h:157
const CSnapValue operator+() const
Definition: SnapValue.h:132
CSnapValue(double val)
Definition: SnapValue.h:65
bool operator>(const double &rhs) const
Definition: SnapValue.h:241
bool operator<=(const CSnapValue &rhs) const
Definition: SnapValue.h:214
CSnapValue & operator--()
Definition: SnapValue.h:163
bool operator==(const CSnapValue &rhs) const
Definition: SnapValue.h:199
const CSnapValue operator-() const
Definition: SnapValue.h:149
const CSnapValue operator/(const CSnapValue &rhs) const
Definition: SnapValue.h:115
bool operator>=(const double &rhs) const
Definition: SnapValue.h:251
const CSnapValue operator+(const CSnapValue &rhs) const
Definition: SnapValue.h:127
CSnapValue & operator+=(const CSnapValue &rhs)
Definition: SnapValue.h:120
CSnapValue & operator*=(const CSnapValue &rhs)
Definition: SnapValue.h:96
const CSnapValue operator++(int)
Definition: SnapValue.h:170
const CSnapValue operator--(int)
Definition: SnapValue.h:177
bool operator<=(const double &rhs) const
Definition: SnapValue.h:246

Distributed under the Apache License, Version 2.0.

See accompanying NOTICE file for details.