Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<h3>Equality comparisons rarely succeed on FP values</h3> <hr> <p>The short answer is that the <code>Float#to_d</code> is more accurate in 1.9 and is correctly failing the equality test that should not have succeeded in 1.8.7.</p> <p>The long answer involves a basic rule of floating point programming: <em>never do equality comparisons.</em> Instead, fuzzy comparisons like <code>if (abs(x-y) &lt; epsilon)</code> are recommended, or code is written to avoid the need for equality comparison altogether.</p> <p>Although there are in theory about 2<sup>32</sup> single-precision numbers and 2<sup>64</sup> double-precision numbers that could be exactly compared, there are an infinite number that cannot be so compared. (Note: it <strong><em>is</em></strong> safe to do equality comparisons on FP values that happen to be integral. So, contrary to much advice, they are actually perfectly safe for loop indices and subscripts.)</p> <p>Worse, the way we write fractional numbers makes it unlikely that a comparison with any specific constant will be successful.</p> <p>That's because the fractions are binary, that is 1/2 + 1/4 + 1/8 ... but our constants are decimal. So, for example, consider monetary amounts in the range <code>$1.00, $1.01, $1.02 .. $1.99.</code> There are 100 values in this range and yet only 4 of them have exact FP representations: <code>1.00, 1.25, 1.50, and 1.75.</code></p> <p>So, back to your problem. Your result of <code>0.495</code> has no exact representation and neither does the input constant of <code>0.1.</code> You begin the calculation with a subtraction of two FP numbers with different magnitudes. The smaller number will be denormalized in order to accomplish the subtraction and so it will lose two or three low-order bits. As a result, the calculation will lead to a slightly large number than 0.495, because the entire 0.1 was not subtracted from 10. Your constant is actually slightly smaller (internally) than 0.495. And that's why the comparison fails.</p> <p>Ruby 1.8 must have been accidentally or deliberately losing some low order bits and effectively introducing a rounding step that ended up helping your test. </p> <p>Remember: the rule of thumb is that you must explicitly program in such rounding for floating point comparisons.</p> <hr> <p><sub><strong>Notes.</strong> To answer the question from the comments about simple decimal fraction constants not having exact representations: They don't have exact finite forms because they repeat in binary. Every machine fraction is a rational number of the form x/2<sup>n</sup>. Now, the constants are decimal and every decimal constant is a rational number of the form x/(2<sup>n</sup> * 5<sup>m</sup>). The 5<sup>m</sup> numbers are odd, so there isn't a 2<sup>n</sup> factor for any of them. Only when m == 0 is there a finite representation in both the binary and decimal expansion of the fraction. So, 1.25 is exact because it's 5/(2<sup>2</sup>*5<sup>0</sup>) but 0.1 is not because it's 1/(2<sup>0</sup>*5<sup>1</sup>). <strong><em>There is simply no way to express 0.1 as a finite sum of x/2<sup>n</sup> components.</em></strong></sub></p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload