New blog post! I really hope I'll get a decent amount of people mad with this one ššš
It's OK to compare floating-points for equality:
https://lisyarus.github.io/blog/posts/its-ok-to-compare-floating-points-for-equality.html
New blog post! I really hope I'll get a decent amount of people mad with this one ššš
It's OK to compare floating-points for equality:
https://lisyarus.github.io/blog/posts/its-ok-to-compare-floating-points-for-equality.html
@lisyarus Thanks a lot! This is one of my larger pet peeves. In the last 10 years I probably needed exact comparisons 95%+ of the time (if you exclude test code). And I mostly regretted any epsilon comparison in time. This is all with the exception of tests, where "approximate equal to the expected result" is fine.
But basically "load-bearing" comparisons with eps didn't bear any load in the end...
@lisyarus I had one client who had operator== on their Vector3 class overloaded with a non-zero epsilon.
Can you imagine how a std::unordered_map behaves if the equality operator might pick up the wrong element? That's fun to debug.
@lisyarus but whatās it about to justify leaving the Fedi client for a webbrowser? For all I know it could be about, idk, reproduction of snails (Fedi is weird soā¦).
No description, no teaser, not even a title, and no speaking URL either (if it is speaking, donāt truncate it when you post).
This doesnāt entice me, I have to say:
It's just that the tone of your reply looked rather...ill-intentioned to me? You could say "Tip: in some clients you don't see a link preview, so the users don't know what the post is about", but you said (with intentional emotional paraphrasing to make it clear what I'm trying to convey) "I don't like it, you made a bad thing, I'm not gonna read it, it's bad".
@lisyarus
> In reality it is a pretty deterministic (modulo compiler options, CPU flags, etc)
and then det(v, v) suddenly doesn't return 0.0 anymore because the compiler now targets a newer CPU with FMA and uses regular multiplication for `a.y * b.x` and FMA for `a.x * b.y - prev_result`, and FMA uses infinite precision so `a.x * b.y` can be slightly different from the result of the regular multiplication -_-
It annoys me that IEEE754 allows this and compilers (ab)use it
@zeux @lisyarus
IEEE 754 at least seems to allow this as long as it can be switched off, doesn't require it to be disabled by default - or at least that's what the C/C++ standards and/or compilerwriters think
(GCC is extra fun because it only allows using -ffp-contract but ignores the FP_CONTRACT pragma)
@Doomed_Daniel @zeux @lisyarus again, that's not IEEE 754.
But it's not like the FP standard gets a vote on this. The language standards do whatever they want.
See also Kahan's rants on Java's choices wrt FP implementation in the 90s and early 2000s. Mind, in a lot of that he's railing against the idea of trying to aim for exact reproducibility since it was at the time impractical. By now it wouldn't be, though.
@rygorous @zeux @lisyarus
IIRC in a discussion about this years ago (and/or GCC bugtracker?) compiler-adjacent people argued that it does not violate IEEE 754, because the standard allows it as long as it can be controlled with a flag, or sth like that.
The language standard can do whatever it wants, but not claim that it conforms to IEEE 754 then?
@Doomed_Daniel @zeux @lisyarus Again, I don't understand what power you think the standard has here, though.
They can write all kinds of things into IEEE-754 that are then summarily ignored.
This is pretty much exactly how IEEE 754s traps have been handled by most langs for the past 40 years.
@Doomed_Daniel @zeux @lisyarus See also langs and environments that always treat subnormals as 0 and so forth.
It's just words on a page. What do you think the enforcement mechanism is?
The reality of the matter is that anything written in IEEE-754 is exactly as important as its users think it is. Historically speaking, very good track record on the basic stuff (correctly rounded fundamental operations, RN default, certain math lib fns being available), beyond that, very touch and go.
@Doomed_Daniel @zeux @lisyarus at this point, #3 is functionally dead, and most things that used to be in category #1 by now have moved to #2, i.e. FMAs used to be a "depends on the platform" thing and are now almost everywhere.
The side effect being that a thing that used to show up on ports to a different platform (i.e. going non-FMA <-> FMA targets) is now something that is showing up within targets, and the numerical env in general is much more homogeneous.
@Doomed_Daniel @zeux @lisyarus My point being, legislating any particular evaluation strategy was wholly impractical until at least the 2010s or so.
At this point it would be a lot more reasonable. It's just that we have a path-dependency where both the compiler options and the standards come from a world where insisting that things be nailed down in a particular way was simply impractical, and what compiler support there is for turning this off is also what used to be a total niche feature.
@Doomed_Daniel @zeux @lisyarus Because in the 90s, if you're porting numerical SW to say a PPC, you wouldn't want to not use FMAs, for the most part.
That's making a lot of things 2x more expensive because even just basic FP mul and FP add on that HW would execute as FMAs, and their was only one FP arithmetic unit which was the FMA unit!
Standards have more recently started to try to tighten down on things but it's just a hard retro-fit due to many historical accidents.
@rygorous @zeux @lisyarus
my assumption was that if a language/compiler says it implements IEEE 754 that it then by default conforms to that standard - but apparently that was optimistic/naive.
either way, this is a massive footgun that has become more relevant recently with x86-64-v3 becoming a popular target
@lisyarus OK, let's get started 8-D
1. when an epsilon check *is* made, the epsilon should never be arbitrary, but at the very least be based on machine epsilon and the the magnitude of the numbers involved (e.g. something like fabs(x - y) < (x+y)*FLT_EPSILON/2 rather than fabs(x-y) < 1.e-4, unless additional information is known about the process by which the numbers are obtained.
2. I like the recommendation to use hypot for the vector length, but it might be worth mentioning that for maximum accuracy it's preferrable to sort the components, or at the very least identify which one is largest. Computationally, this spares one (expensive) division and one multiplication at the expense of two swaps, but adding the lower terms squared to 1 is more consistently accurate than the three squared terms added in arbitrary order.
2bis. also I don't think anyone ever brought that up as an example in which to use an arbitrary epsilon, but that's a different matter 8-D
3. the GaussāJordan elimination argument is *really* flaky. GJ is just an extremely poor algorithm in general, and should not be used at all, but replacing the epsilon check with a 0 check doesn't really help in its application. āVery impreciseā can be quite catastrophic depending on applications.
4. the user input case really brings out the fact that main pain point with epsilons isn't even the epsilons themselves, but the fact that they are chosen *arbitrarily*; however, this is a very different matter compared to ādon't use them when they aren't neededā. (yes this connects to point 1. above)
THE END.
@lisyarus I learned "use epsilon" before IEEE-754 existed, when you didn't necessarily know how your floats behaved at boundary conditions. and I never really thought about it much after floats became universally standardized.
this feels like a case of a best-practice principle losing its context and getting misapplied in a changed environment
