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