5 ms·
I don't know much about the C standard, but the article states that implementation details are platform-specific. If that's the case, it's a very sensible sugge
by wyattpeak 8y ago
I don't know much about the C standard, but the article states that implementation details are platform-specific. If that's the case, it's a very sensible suggestion.
Sure, if the same system runs the same operation twice, the two values will be equal. But what if you're comparing to a stored value generated on a different system?
- gizmo686 8y agoIt doesn't even need to be a different system. Different compilations of the same program on the same system can result in different floating point results (either because of internal high-precision floating point registers being utilized differently, or the compiler making algebraically valid re-arrangements that change the floating point result). Additionally, many usages of == assume basic algebraic properties of numbers to hold. It is possible to use equality with floats, but require far more care than is typically worth it.
- MaulingMonkey 8y agoOne option is to have an ExactlyEquals function or macro that wraps the == in pragmas to supress the warning for the scope. At work some of our types don't even define == just to force the issue.
- olliej 8y agoIf the optimization level changes the behaviour the compiler is broken. “Optimizing” by assuming commutivity or associativity of floating point is no less incorrect than optimizing by assuming string addition was commutativeand associative.
- gizmo686 8y agoThere is no requirement that optimization does not change behaviour. The only requirement is that the optimized behavior remains consistent with the standards. The point of the point on floating point equality is that the standards give enough room for variance that you should not use literal equality unless you know exactly what you are doing and have a good reason for doing it. As an aside, if your concern is that the standards impose too much restrictions, another popular option is the ffast-math option. We see this come up with strings fairly often in higher level languages where, sometimes, logical equal strings will happen to be equal because the runtime's interning system made them point to the same location, but there is no guarantee this would happen and the implementation is free to change it at any point. I believe I have also seen this behaviour come up with interning on some Integer wrapper types.
- loeg 8y ago> I believe I have also seen this behaviour come up with interning on some Integer wrapper types. Python, for example: >>> 8 is 8 True >>> 8**1000 is 8**1000 False
- olliej 8y agoBecause python is broken in this respect -- JS engines do exactly the same object optimisations and get comparisons correct.
- loeg 8y agoMeh. They're different languages with different expectations. "is" is distinct from "==" in both, and Python gets "==" right (obviously). Javascript doesn't really have any high ground here[0]. (In contrast, Python:) >>> [] == False False >>> object() == False False >>> "" == False False >>> {} == False False They're just different. [0]: https://www.youtube.com/watch?v=et8xNAc2ic8 https://www.youtube.com/watch?v=et8xNAc2ic8
- viraptor 8y agoJS has "strict equality" comparison. It's not the same as "identity" comparison.
- olliej 8y agoNo, optimizations are allowed to change the ordering or exact implementations, but they cannot change observable behavior unless the behavior depends on undefined (e.g the compiler is the adversary) or unspecified (in which the compiler is required to be self-consistent, e.g parameter evaluation order is (was?) unspecified so a compiler could choose left to right or right to left evaluation, but it must pick one and use that everywhere). The C abstract machine defines the behavior, and optimization passes aren't allowed to change the behavior. -ffast-math is the option that allows incorrect semantics (e.g commutative floating point addition), but compilers have a desire to do incorrect things for "performance". I would hope it is obvious that changing behavior for performance isn't ok, but lets say it was. A compiler could change y=1.0/fsqrt(x) from (v. approximate, untested, and written in a text box :D) movsd xmm0, $(wherever x is) sqrtsd xmm0 movsd xmm1, $(location of 1.0 constant) divsd xmm1, xmm0 movsd $(addr of y), xmm1 to something like movsd xmm0, $(wherever x is) rsqrtsd xmm0 movsd $(addr of y), xmm0 This would be much much faster, and vastly incorrect (IIRC rsqrt is only 12bits of precision) Or even more dumb, remove calls to printf, because printf is slow. A compiler does not have absolute leeway in all things, it is still restricted to ensuring that observable results of the C abstract machine are correct.
- jeremysalwen 8y ago(Well string addition should be associative)
- olliej 8y agoHurrr derp :D You are of course right :)
- olliej 8y agoNo, you will get the same results on all systems - ieee754 defines exact semantics and behavior. The problems stem from running under different rounding modes on different systems, I believe at this point literally all platforms default to RTE, and if you’re working with a different rounding mode your errors are going to be much more significant than “compare to epsilon”. The historic problems people have had with C compilers and floating point is that compiler vendors seem hell bent on “improving” performance by effectively changing clearly intended semantics (eg the compiler is an adversary). These include performing calculations at an incorrect precision (eg host precision, “convenient” precision) rather than the specified precision, or bizarrely treating floating point arithmetic as commutative and associative. That bit of behavior isn’t specific to floating point - c compilers routinely optimize out security checks because they taken “undefined” to mean “free to change clear intent”.
- loeg 8y agoC doesn't require IEEE754. It's strongly suggested as normative, Appendix F. But not required; after all, C17/18 still does not require two's complement for signed integers. C's model allows for very weird underlying machines. x86 is extremely popular and 80 bit long doubles using the old FPU model can introduce weirdness on that platform. And yes, reducing the 80 bit long doubles to 64 bits after every operation significantly impacts performance. Or used to.
- olliej 8y agoWhile f80 had some quirks it is an ieee754 floating point unit, and has all the ieee precision and semantics. But my point was that saying floating point was somehow a magical non-deterministic beast is incorrect, and saying == shouldn't be allowed because of that is incorrect. Pretty much the same arguments can be made for integer arithmetic (after all x/y is undefined behavior if it overflows or y is zero even for integers, and just as with floating point you need to understand the behavior of arithmetic)
- telendram 8y agoThat's a huge stretch. Integer arithmetic is perfectly portable. Defined operations have bit-exact defined results, reproducible on any platform. In contrast, the result of basic floating point operations is not. You may live in a world were you use a single cpu type, and a single OS, and a single compiler, in which case, it might appear to you as defined. But anyone involved in some portability across multiple systems know how improbable it is to rely on floating point for anything exact. Actually, don't. ever. For anything which requires exact results, use integer instead.