Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

`equal(0.3, 0.2 + 0.1); // true`

how is this wizardry possible?



It uses type dispatch to perform an epsilon comparison:

    static int pretty_float_equal (float a, float b) { return fabsf(a - b) < FLT_EPSILON; }
So it’s https://docs.python.org/library/math.html#math.isclose


This code is incorrect, but I don't blame them. :) Probably one of the most common float-related mistakes, even among people who "know how floats work".

FLT_EPSILON is the difference between 1.0 and the next larger float. It's impossible for numbers less than -2.0 or greater than 2.0 to have a difference of FLT_EPSILON, they're spaced too far apart.

You really want the acceptable error margin to be relative to the size of the two numbers you're comparing.

Also, everyone should read the paper "What, if anything, is epsilon?" by Tom7


I would go even further and say that any equality comparison of float numbers has to be a one-off special case. You need to know how much error can arise in your calculations, and you need to know how far apart legitimately different numbers will for your particular data. And of course the former has to be smaller than the latter.


Indeed, FLT_EPSILON is not a one-size-fits-all solution, but it's good enough for frequent case of comparing big enough numbers, which is not covered by regular ==. So it's a convenience/correctness trade-off I'm ready to make.


If the numbers you are comparing are greater than 2, abs(a - b) < FLT_EPSILON is equivalent to a == b. Because it's not possible for two large numbers to not be equal, but also closer together than FLT_EPSILON.


what

    // FLT_EPSILON == 0.01
    equal(4.999, 5); // true
    4.999 == 5;      // false
am i missing?


FLT_EPSILON is not 0.01, it's 0.00000011920929.

But it's impossible to have a number that's 0.00000011920929 less than 5.0, or 0.00000011920929 more than 5.0, because the floats with enough magnitude to represent 5 are spaced further apart than that. Only numbers with magnitude < 2 are spaced close enough together.

In other words, the only 32-bit float that's within ±0.00000011920929 of 5.0 is 5.0 itself.


Oh you're right, thanks for the explanation!

Gotta research now where the 0.00000011920929 number comes from...


It's the distance between 1.0 and the next representable float.


I got that but I am curious how to derive that number.

Is it representable as a non-trivial ratio of integers?


Good question! It's 1/(2**23), because 32 bit floats have 23 bits after the decimal point


Is What Every Computer Scientist Should Know About Floating-Point Arithmetic wrong ??!!

addendum: why are obviously rhetorical questions are taken so literally here?


Because text doesn't convey sarcastic voice tonality, so the intent is far from obvious.


Sarcastic? Okay, if you say so.

Picking out an obvious define function that compares a float with a float sum of that nature should indicate an good understanding of why that might be called wizardry and deserving of a second look.

Hats off to the peer comment that suggested scaling against epsilon rather than simpliy regurging the substitution "as was" from the header.

The scaling is better in general, optional in some specific contexts.


It's meant as both humorous and a nerd snipe :)


it uses absolute difference epsilon equality ('close enough to be considered equal'):

    static int pretty_float_equal (float a, float b) { return fabsf(a - b) < FLT_EPSILON; }
    static int pretty_double_equal (double a, double b) { return fabs(a - b) < DBL_EPSILON; }
    static int pretty_long_double_equal (long double a, long double b) { return fabsl(a - b) < LDBL_EPSILON; }


This is wrong code. It only works somewhat correctly when a and b around 1.


Yeah, should be scaled like |x - y| <= ε * max(|x|, |y|)


Will do.


If both terms are infinites and of same sign, subtraction will give NaN and it will fail.


How is that a problem? infinities shouldn't be considered equal


For IEEE 754, and in Java for example, they are. Only NaN is not equal to itself (and different from itself).


static int pretty_float_equal (float a, float b) { return fabsf(a - b) < FLT_EPSILON; }




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: