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 {
39 private:
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 
50 public:
51  // Make it look, smell, and act like a double
52 
53  // Constructors
55  {
56  m_dVal = 0.0;
57  }
58 
59  CSnapValue(const CSnapValue &src)
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 
286 protected:
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 
323 private:
324  double m_dVal;
325 };
326 
327 inline double pow(double x, const CSnapValue &y)
328 {
329  return pow(x,y.GetValue());
330 }
bool operator<=(const CSnapValue &rhs) const
Definition: SnapValue.h:214
bool operator>=(const CSnapValue &rhs) const
Definition: SnapValue.h:219
CSnapValue & operator=(const CSnapValue &rhs)
Definition: SnapValue.h:90
CSnapValue(double val)
Definition: SnapValue.h:65
CSnapValue(const CSnapValue &src)
Definition: SnapValue.h:59
CSnapValue(int val)
Definition: SnapValue.h:77
bool operator==(const CSnapValue &rhs) const
Definition: SnapValue.h:199
const CSnapValue operator++(int)
Definition: SnapValue.h:170
bool operator>(const CSnapValue &rhs) const
Definition: SnapValue.h:209
bool operator>=(const double &rhs) const
Definition: SnapValue.h:251
bool operator>=(const int &rhs) const
Definition: SnapValue.h:281
bool operator!=(const CSnapValue &rhs) const
Definition: SnapValue.h:204
const CSnapValue operator-() const
Definition: SnapValue.h:149
CSnapValue & operator++()
Definition: SnapValue.h:157
const CSnapValue operator+() const
Definition: SnapValue.h:132
CSnapValue & operator+=(const CSnapValue &rhs)
Definition: SnapValue.h:120
CSnapValue & operator/=(const CSnapValue &rhs)
Definition: SnapValue.h:108
bool operator>(const double &rhs) const
Definition: SnapValue.h:241
const CSnapValue operator-(const CSnapValue &rhs) const
Definition: SnapValue.h:144
bool operator<(const CSnapValue &rhs) const
Definition: SnapValue.h:194
double m_dVal
Definition: SnapValue.h:324
CSnapValue & operator-=(const CSnapValue &rhs)
Definition: SnapValue.h:137
bool operator!=(const double &rhs) const
Definition: SnapValue.h:236
const CSnapValue operator*(const CSnapValue &rhs) const
Definition: SnapValue.h:103
bool operator<=(const double &rhs) const
Definition: SnapValue.h:246
CSnapValue()
Definition: SnapValue.h:54
Definition: SnapValue.h:37
const CSnapValue operator--(int)
Definition: SnapValue.h:177
CSnapValue & operator--()
Definition: SnapValue.h:163
CSnapValue(const CSnapValue &src, bool dummyflag)
Definition: SnapValue.h:44
CSnapValue(float val)
Definition: SnapValue.h:71
const CSnapValue operator/(const CSnapValue &rhs) const
Definition: SnapValue.h:115
CSnapValue & operator*=(const CSnapValue &rhs)
Definition: SnapValue.h:96
bool operator<=(const int &rhs) const
Definition: SnapValue.h:276
bool operator>(const int &rhs) const
Definition: SnapValue.h:271
const CSnapValue operator+(const CSnapValue &rhs) const
Definition: SnapValue.h:127
void Snap()
Definition: SnapValue.h:287
const double & GetValue() const
Definition: SnapValue.h:184
bool operator!=(const int &rhs) const
Definition: SnapValue.h:266