Skip to content

Commit 5219ac5

Browse files
committed
update C comparison
1 parent e3cbc64 commit 5219ac5

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

text/0000-float-semantics.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,25 @@ The RFC author considers the downsides of unspecified NaN bit patterns being obs
241241
# Prior art
242242
[prior-art]: #prior-art
243243

244-
C and C++ generally avoid saying anything about signaling NaNs, and also don't talk about how NaNs are chosen. I can only assume this means they are chosen non-deterministically; that is certainly the interpretation chosen by GCC and LLVM.
244+
C23 clarifies its stance on signaling NaNs:
245245

246-
GCC [says](https://gcc.gnu.org/wiki/FloatingPointMath) "Without any explicit options, GCC assumes round to nearest or even and does not care about signalling NaNs". It is unclear whether "does not care" also means "guarantees to never produce by itself", i.e. whether `0.0 / 0.0` is ever allowed to evaluate to a signaling NaN or not. If it *is* allowed to evaluate to a signaling NaN, that is probably a violation of the C standard, which guarantees that `pow(1, 0.0/0.0)` returns `1` -- but in practice, `pow(1, sNaN)` returns a NaN.
246+
> Where specification of signaling NaNs is not provided, the behavior of signaling NaNs is implementation-defined (either treated as an IEC 60559 quiet NaN or treated as an IEC 60559 signaling NaN).
247+
248+
It doesn't say anything about the bit patterns inside NaNs.
249+
This means that strictly speaking, any form of NaN boxing has to re-normalize NaNs after every single operation.
250+
To the author's knowledge, this is not actually done in practice; code instead relies on compilers implementing a more strict semantics.
251+
The C standard also does not state explicitly whether signaling NaNs can ever be produced by arithmetic operations, though given that the IEEE 754 semantics do not permit this to happen, presumably C does not permit this, either.
252+
At least [two major C implementations violate this part of the spec](https://godbolt.org/z/6veef68xE).
247253

248254
LLVM [recently adopted](https://github.com/llvm/llvm-project/pull/66579) new NaN rules that this RFC copies exactly into Rust.
255+
This means that even though arithmetic operations can produce signaling NaNs, there is a guarantee that signaling NaNs will never appear "out of thin air".
249256
LLVM does not actually document that they are using IEEE float semantics, but de-facto they do on almost all targets (the exception are targets that use x87 instructions, as noted above).
250257

258+
GCC [says](https://gcc.gnu.org/wiki/FloatingPointMath) "Without any explicit options, GCC assumes round to nearest or even and does not care about signalling NaNs".
259+
It is unclear whether "does not care" also means "guarantees to never produce by itself", i.e. whether `0.0 / 0.0` is ever allowed to evaluate to a signaling NaN or not.
260+
While IEEE 754 forbids such behavior, GCC does [return signaling NaNs from some arithmetic operations](https://godbolt.org/z/6veef68xE), so it is fair to ask under which exact conditions such signaling NaNs may occur.
261+
If `0.0 / 0.0` *is* allowed to evaluate to a signaling NaN, that is concerning for cases like `pow(1, 0.0/0.0)`, which should return `1` -- but in practice, `pow(1, sNaN)` returns a NaN.
262+
251263
Java requires exact IEEE 754-2008 compliance and goes through a lot of effort to realize that on 32bit x86 without SSE (see [here](https://open-std.org/jtc1/sc22/jsg/docs/m3/docs/jsgn325.pdf) and [here](https://open-std.org/JTC1/SC22/JSG/docs/m3/docs/jsgn326.pdf)). However, they do not seem to tackle the issue of specifying NaN payload bits, even though those bits [can be observed](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Float.html#floatToRawIntBits(float)).
252264

253265
wasm [guarantees](https://webassembly.github.io/spec/core/exec/numerics.html#nan-propagation) that "if all input NaNs are canonical, then any output NaN is canonical". Here ["canonical"](https://webassembly.github.io/spec/core/syntax/values.html#canonical-nan) is defined as a NaN with a "payload whose most significant bit 1 is while all others are 0", i.e. it matches what we call "preferred" above.

0 commit comments

Comments
 (0)