Live data from Hacker News

It's OK to compare floating-points for equality

lisyarus.github.io

61–70 of 150 posts

Re: It's OK to compare floating-points for equality

#61
post #2

There is another way to compare floats for rough equality that I haven't seen much explored anywhere: bit-cast to integer, strip few least significant bits and then compare for equality. This is agnostic to magnitude, unlike epsilon which has to be tuned for range of values you expect to get a meaningful result.

Completely worked out at least 20 years ago: https://www.lomont.org/papers/2005/CompareFloat.pdf

Great reading, thanks. Describes how to handle ±0, works with difference to avoid truncation errors. First half of the paper is arriving at this correct snippet, second part of the paper is about optimizing it.

    bool DawsonCompare(float af, float bf, int maxDiff)
    {
        int ai = *reinterpret_cast(&af);
        int bi = *reinterpret_cast(&bf);
        if (ai 

Re: It's OK to compare floating-points for equality

#62
> In reality it is a pretty deterministic (modulo compiler options, CPU flags, etc)

IIRC this was not ALWAYS the case, on x86 not too long ago the CPU might choose to put your operation in an 80-bit fp register, and if due to multitasking the CPU state got evicted, it would only be able to store it in a 32-bit slot while it's waiting to be scheduled back in?

It might not be the case now in a modern system if based on load patterns the software decides to schedule some math operations or another on the GPU vs the CPU, or maybe some sort of corner case where you are horizontally load balancing on two different GPUs (one AMD, one Nvidia) -- I'm speculating here.

Re: It's OK to compare floating-points for equality

#63

Earlier quoted context omitted.

Wait this doesn't make sense. Yes you'd get smaller absolute error in radians, but it doesn't really help because it's different units. Relative error is the same in degrees and radians, that's the whole point of exponential representation. All you're doing is adding a fixed offset to the exponent, but it doesn't give you any more precision when converting to radians

Having a constant relative error is indeed the reason for using floating-point numbers. However, for angles the relative error is completely irrelevant. For angles only the absolute error matters. For angles the optimum representation is as fixed-point numbers, not as floating-point numbers.

Yep, it was a long time ago but I think that's exactly what we ended up with, eventually: An int type of unit 2π/(int range). I believe we used unsigned because signed int overflow is undefined behavior.

Re: It's OK to compare floating-points for equality

#64

Earlier quoted context omitted.

Wait this doesn't make sense. Yes you'd get smaller absolute error in radians, but it doesn't really help because it's different units. Relative error is the same in degrees and radians, that's the whole point of exponential representation. All you're doing is adding a fixed offset to the exponent, but it doesn't give you any more precision when converting to radians

Having a constant relative error is indeed the reason for using floating-point numbers. However, for angles the relative error is completely irrelevant. For angles only the absolute error matters. For angles the optimum representation is as fixed-point numbers, not as floating-point numbers.

With -π to π radians you get absolute error of approximately 4e-16 radians. With -180 to 180 degrees you get absolute error of approximately 2e-14 degrees.

Even though the first number is smaller than the 2nd one, they actually represent the same angle once you consider that they are different units. So there's no precision advantage (absolute or relative) to converting degrees to radians.

Note that I'm not saying anything about fixed vs floating point, only responding to an earlier comment that radians give more precision in floating point representation.

Re: It's OK to compare floating-points for equality

#65

Earlier quoted context omitted.

I remember having convincing a few coworkers that the number of distinct floating point values between 0.0 and 1.0 is the same as the number of values between 1.0 and infinity. They must not be teaching this properly anymore. Are there no longer courses that explain the basics of floating point representation? I was arguing that we could squeeze a tiny bit more precision out of our angle types by storing angles in ra…

What you say was true exactly only in most floating-point formats that were used before 1980. In those old FP formats, the product of the smallest normalized and non-null FP number with the biggest normalized and non-infinite FP number was approximately equal to 1. However in the IEEE standard for FP arithmetic, it was decided that overflows are more dangerous than underflows, so the range of numbers greater than 1 h…

> With IEEE FP numbers, the product of the smallest and biggest non-null non-infinite numbers is no longer approximately 1, but it is approximately 4.

This is just wrong? The largest Float64 is 1.7976931348623157e308 and the smallest is 5.0e-324 They multiply to ~1e-16.

Re: It's OK to compare floating-points for equality

#67
This is mostly about game logic, where I can understand the reliance on floating point numbers. I've also seen these epsilon comparisons in code that had nothing to do with game engines or positions in continuous space, and it has always hurt my eyes.

I think if you want to work with values that might be exactly equal to other values, floating point is simply not the right choice. For money, use BigDecimal or something like that. For lots of purposes, int might be more appropriate. If you do need floating point, maybe compare whether the value is larger than the other value.

Re: It's OK to compare floating-points for equality

#68
post #7

I have this floating-point problem at scale and will donate $100 to the author, or to anyone here, who can improve my code the most. The Rust code in the assert_f64_eq macro is: if (a >= b && a - b I'm the author of the Rust assertables crate. It provides floating-point assert macros much as described in the article. https://github.com/SixArm/assertables-rust-crate/blob/main/s... If there's a way to make it more prec…

I think a key you may want is ε which scales with the actual local floating point increment. C++ implements this https://en.cppreference.com/cpp/numeric/math/nextafter Rust does not https://rust-lang.github.io/rfcs/3173-float-next-up-down.htm... but people have in various places.

Um, what? You've linked an RFC for Rust, but the CPP Reference article for C++ So yeah, the Rust RFC documents a proposed change, and the C++ reference documents an implemented feature, but you could equally link the C++ Proposal document and the Rust library docs to make the opposite point if you wanted.

Rust's https://doc.rust-lang.org/std/primitive.f32.html#method.next... https://doc.rust-lang.org/std/primitive.f32.html#method.next... of course exist, they're even actually constant expressions (the C++ functions are constexpr since 2023 but of course you're not promised they actually work as constant expressions because C++ is a stupid language and "constexpr" means almost nothing)

You can also rely on the fact (not promised in C++) that these are actually the IEEE floats and so they have all the resulting properties you can (entirely in safe Rust) just ask for the integers with the same bit pattern, compare integers and because of how IEEE is designed that tells you how far away in some proportional sense, the two values are.

On an actual CPU manufactured this century that's almost free because the type system evaporates during compilation -- for example f32::to_bits is literally zero CPU instructions.

Re: It's OK to compare floating-points for equality

#69

Earlier quoted context omitted.

Yeah, I'm not sure how widespread the knowledge is that floating point trades precision for magnitude. Its obvious if you know the implementation, but I'm not sure most folks do.

I remember having convincing a few coworkers that the number of distinct floating point values between 0.0 and 1.0 is the same as the number of values between 1.0 and infinity. They must not be teaching this properly anymore. Are there no longer courses that explain the basics of floating point representation? I was arguing that we could squeeze a tiny bit more precision out of our angle types by storing angles in ra…

Wouldn’t you want to use “turns” for that sort of thing?

Re: teaching floats; when I was working with students, we touch on floats slightly, but mostly just to reinforce the idea that they aren’t always exact. I think, realistically, it can be hard. You don’t want to put an “intro to numerical analysis” class into the first couple lectures of your “intro to programming” class, where you introduce the data-types.

Then, if you are going to do a sort of numerical analysis or scientific computing class… I dunno, that bit of information could end up being a bit of trivia or easily forgotten, right?

Re: It's OK to compare floating-points for equality

#70
So they say you should use epsilons, then their solution to the first problem is to use an epsilon. There may be some cases when you can get by without using epsilon comparison, but in many cases epsilon comparison is the right thing to do, but you need to choose a good value for it.

This is especially true in cases where the number comes from some kind of input (user controls, sensor reading, etc.) or random number generation.

Post reply on HN