on a floating-point equality exercise
a reply-to-all from the COMP1511 forums, 2017-09-10

(I’ve been asked several times about how to do this, so here’s my official “all the bells-and-whistles” answer.)

Many of you have found that your apparently-correct myAtoD causes an assertion failure, because the value you’ve produced (typically something like -0.14100000000000001) isn’t exactly -0.141. As is the way with floating-point numbers, you’re off by a tiny, tiny fraction.

The problem with floating-point numbers is that they’re not exact values, but a finite number of approximations of real numbers. Inherently, two numbers that appear the same aren’t guaranteed to have exactly the same pattern of bits set to represent that floating-point number. C’s equality operator, ==, does bit-for-bit comparison, so two floating-point numbers won’t always compare equal.

If I make a quick side-step to COMP1521 material: -0.141 actually becomes -0.140999999999999986455279, which is stored as the bit pattern 0xBFC20C49BA5E353F. The number you’re seeing is somewhere around -0.141000000000000014210855, which is stored as the bit pattern 0xBFC20C49BA5E3540.

0xBFC20C49BA5E353F != 0xBFC20C49BA5E3540

On my system, when I go to compile the extract.c provided, I get a compiler warning for this exact reason:

extract.c:25:19: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
    assert (dat.y == -0.141);
            ~~~~~ ^  ~~~~~~

One student on the forums has suggested that multiplying and dividing by 1000 produces a reasonable result. That might work… if there’s only three decimal places.

The way I’d do it is to explicitly check that the value is within a range – that -0.142 < dat.y && dat.y < -0.140. You might like to write a function to do this.

If you’re interested in how numbers work, I highly recommend: