Floating Point Arithmetic, II
Some answers from the last post...
- Math.Round(2.5) = 3. False.
Math.Round uses banker's rounding, which rounds the fraction .5 to the nearest even number. Traditional arithmetic rounding introduces an upward bias, whereas the heuristic used in banker's rounding cancels out the bias. When aggregating several random rounded numbers, banker's rounding is likely to lead to the same result as adding the unrounded numbers and rounding afterwards. Arithmetic rounding, on the other hand, will result in a growing divergence between the addition of pre-rounded numbers and that of unrounded numbers as more values are added.
- Math.Round returns an int. False.
Because a far greater range of integer values can be represented by a double than by either a long or an int. In addition, certain special floating values such as infinities have no special counterpart in the int or long data type, a value of type double is the only satisfactory return value that will not cause an error.
- Both x and y are expressions of type Double and have the same exact bit representation of a Double in memory. Neither are special cases—ie, NaN, negative zero, denormalized numbers, or infinities. Therefore, x = y.
When comparing two floating point values, even if they are bitwise equal, it is important to check that the difference is smaller than some small magnitude. (It's probably more accurate to check that the ratio of difference to the sum of the magnitudes is less than the desired precision.) The reason is that floating pointer registers typically have greater precision than double offers (which is why C++ also has a 10-byte long double type). If one of the operands of the equality is stored in a register (with extended precision) and the other is read from memory (with standard precision), the "same" value may not compare to true.
- If x is a Double and x = x, then double.Parse(x.ToString()) = x. False.
When serializing floating-point values to xml or some other text format, it's important to use the roundtrip option (x.ToString("R")) which emits decimal values which have a unique binary representation. ToString outputs 1-2 additional digits beyond the standard fifteen levels of decimal precision.
- Is MSDN documentation correct in stating the following about
“Two apparently equivalent floating point numbers might not compare equal because of differences in their least significant digits. For example, the C# expression,
(double)1/3 == (double)0.33333, does not compare equal because the division operation on the left-hand side has maximum precision while the constant on the right-hand side is only precise to the visible digit.
Instead, determine if the two sides of a comparison are close enough to equal for your purposes by comparing whether the absolute value of the difference between the left and right-hand sides is less than Epsilon.”
Double.Epsilon is the smallest double value greater than zero. (Since it's also a denormalized number with a precision of 1 bit, it is dangerous to use in any calculations.) A comparison for an absolute value less than epsilon will always fail unless that value is zero.