sure, it sounds like there is a difference here. I'm not aware of any stance by the Zig core team on the subject.
I am genuinely interested in a place that this matters for a program, or any practical consequence this has for an end user of the language.
no idea why your response was flagged originally.
Floating point hardware on different CPU architectures don’t produce bitwise identical results, unfortunately. That’s the big issue.
As far as I’m aware, they do produce semantically identical results, but something like the specific bit pattern of a NaN value can theoretically vary, and people might do fun things like encoding extra information in those bits.
About NaNs, you are right, because the standard does not mandate the use of a specific NaN for each kind of invalid operation.
Nonetheless, if you use a comparison function for which all NaNs are equivalent, different CPU architectures that are standard-compliant must produce bitwise identical results for the same sequence of operations.
Differences appear mainly when the compilers generate different operations or in a different order. Moreover, transcendental functions are computed using various approximations by the standard library, so if you use different libraries on different computers, you will get different results.
However, these are differences caused by software, not by hardware, and they happen even on the same computer when you use different versions of a compiler or of a standard library. Therefore such differences can be eliminated, if desired.
rsqrt differs between intel and amd, out-of-bounds conversions differ between x64 and arm (saturating vs sentinel), denormal handling also varies iirc (not 100% sure about that), x64 has FTZ/DAZ separately, ARM has a combined FZ flag and this list is probably not exhaustive...
The practical value is that moving a computation from runtime to compile time can then be purely an optimization, rather than potentially changing its semantics. Think generated lookup tables or numeric constants. Ideally f(x) means the same thing whether evaluated by the compiler or by the generated program.
> Ideally f(x) means the same thing whether evaluated by the compiler or by the generated program.
Absolutely, that’s why I wanted to know what types of things aren’t covered yet, and that’s also why I don’t share your certainty that they won’t be resolved later.
You'd have to read the discussions the Rust community has about this to get a picture. It's complicated.
From what I understand, there is hope for sqrt but there is no consensus on transcendentals.
I think there's some misconceptions floating around here regarding both Rust and Zig's const-evaluation philosophies.
Rust is concerned about memory-safety, yes, but the only strict requirement for memory-safety when it comes to const-evaluation is as follows: "The only guarantee the type system needs is that evaluating `some_crate::SOME_CONST` will produce consistent results if evaluation is repeated in different compilation units" ( https://rust-lang.github.io/rfcs/3514-float-semantics.html ).
Beyond that, from a philosophical standpoint, Rust takes great pains to ensure that const functions produce identical results regardless of whether or not those functions are called at compile-time or at runtime. Rust has adopted this stance because it wants to reserve the right to opportunistically evaluate const-capable functions at compile time, as a performance optimization, even if the user has not explicitly asked for it (for that matter, Rust also does its best to const-evaluate non-const functions when it can). Because of this, Rust's assumption is that users would be annoyed if their program's visible behavior depends on whether or not the optimizer has exercised its discretion to evaluate a specific function at compile-time.
However, this is only a guideline, not a strict guarantee. There is one exception to the above rule: "when a floating-point operation produces a NaN result, the resulting NaN bit pattern is some deterministic function of the operation’s inputs that satisfies the constraints placed on run-time floating point semantics. However, the exact function is not specified, and it is allowed to change across targets and Rust versions, and even with compiler flags. In particular, there is no guarantee that the choice made in const evaluation is consistent with the choice made at runtime."
In other words, calling the `.to_bits()` function on a floating-point value that happens to be NaN is allowed to produce a different result at runtime than it does at compile-time (note that all compile-time evaluations are guaranteed to always produce the same result for a given toolchain version for a given target, as required above).
This exception is made because otherwise otherwise it would be basically impossible to support floating-point math at all, thanks to the way various platforms have implemented their floating-point functions in practice.
In contrast, Zig doesn't have such a philosophical compunction against a function's result being determined by whether or not it's being evaluated at compile-time, as shown by the existence of the `@inComptime` builtin. But Zig does still broadly attempt to make comptime deterministic, including going so far as to forbid I/O, though I don't see where any specific guarantees are documented in the Zig reference.