-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
float types: document NaN bit pattern guarantees #129559
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1190,6 +1190,11 @@ mod prim_f16 {} | |
/// portable or even fully deterministic! This means that there may be some | ||
/// surprising results upon inspecting the bit patterns, | ||
/// as the same calculations might produce NaNs with different bit patterns. | ||
/// This also affects the sign of the NaN: checking `is_sign_positive` or `is_sign_negative` on | ||
/// a NaN is the most common way to run into these surprising results. | ||
/// (Checking `x >= 0.0` or `x <= 0.0` avoids those surprises, but also how negative/positive | ||
/// zero are treated.) | ||
/// See the section below for what exactly is guaranteed about the bit pattern of a NaN. | ||
/// | ||
/// When a primitive operation (addition, subtraction, multiplication, or | ||
/// division) is performed on this type, the result is rounded according to the | ||
|
@@ -1211,6 +1216,79 @@ mod prim_f16 {} | |
/// *[See also the `std::f32::consts` module](crate::f32::consts).* | ||
/// | ||
/// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format | ||
/// | ||
/// # NaN bit patterns | ||
/// | ||
/// This section defines the possible NaN bit patterns returned by non-"bitwise" floating point | ||
/// operations. The bitwise operations are unary `-`, `abs`, `copysign`; those are guaranteed to | ||
/// exactly preserve the bit pattern of their input except for possibly changing the sign bit. | ||
/// | ||
/// A floating-point NaN value consists of: | ||
/// - a sign bit | ||
/// - a quiet/signaling bit | ||
/// - a payload, which makes up the rest of the significand (i.e., the mantissa) except for the | ||
/// quiet/signaling bit. | ||
/// | ||
/// Rust assumes that the quiet/signaling bit being set to `1` indicates a quiet NaN (QNaN), and a | ||
/// value of `0` indicates a signaling NaN (SNaN). In the following we will hence just call it the | ||
/// "quiet bit". | ||
/// | ||
/// The following rules apply when a NaN value is returned: the result has a non-deterministic sign. | ||
/// The quiet bit and payload are non-deterministically chosen from the following set of options: | ||
/// | ||
/// - **Preferred NaN**: The quiet bit is set and the payload is all-zero. | ||
/// - **Quieting NaN propagation**: The quiet bit is set and the payload is copied from any input | ||
/// operand that is a NaN. If the inputs and outputs do not have the same payload size (i.e., for | ||
/// `as` casts), then | ||
/// - If the output is smaller than the input, low-order bits of the payload get dropped. | ||
/// - If the output is larger than the input, the payload gets filled up with 0s in the low-order | ||
/// bits. | ||
/// - **Unchanged NaN propagation**: The quiet bit and payload are copied from any input operand | ||
/// that is a NaN. If the inputs and outputs do not have the same size (i.e., for `as` casts), the | ||
/// same rules as for "quieting NaN propagation" apply, with one caveat: if the output is smaller | ||
/// than the input, droppig the low-order bits may result in a payload of 0; a payload of 0 is not | ||
/// possible with a signaling NaN (the all-0 significand encodes an infinity) so unchanged NaN | ||
/// propagation cannot occur with some inputs. | ||
/// - **Target-specific NaN**: The quiet bit is set and the payload is picked from a target-specific | ||
/// set of "extra" possible NaN payloads. The set can depend on the input operand values. | ||
/// See the table below for the concrete NaNs this set contains on various targets. | ||
/// | ||
/// In particular, if all input NaNs are quiet (or if there are no input NaNs), then the output NaN | ||
/// is definitely quiet. Signaling NaN outputs can only occur if they are provided as an input | ||
/// value. Similarly, if all input NaNs are preferred (or if there are no input NaNs) and the target | ||
/// does not have any "extra" NaN payloads, then the output NaN is guaranteed to be preferred. | ||
/// | ||
/// The non-deterministic choice happens when the operation is executed; i.e., the result of a | ||
/// NaN-producing floating point operation is a stable bit pattern (looking at these bits multiple | ||
/// times will yield consistent results), but running the same operation twice with the same inputs | ||
/// can produce different results. | ||
/// | ||
/// These guarantees are neither stronger nor weaker than those of IEEE 754: IEEE 754 guarantees | ||
/// that an operation never returns a signaling NaN, whereas it is possible for operations like | ||
/// `SNAN * 1.0` to return a signaling NaN in Rust. Conversely, IEEE 754 makes no statement at all | ||
/// about which quiet NaN is returned, whereas Rust restricts the set of possible results to the | ||
/// ones listed above. | ||
/// | ||
/// Unless noted otherwise, the same rules also apply to NaNs returned by other library functions | ||
/// (e.g. `min`, `minimum`, `max`, `maximum`); other aspects of their semantics and which IEEE 754 | ||
/// operation they correspond to are documented with the respective functions. | ||
/// | ||
/// When a floating-point operation is executed in `const` context, the same rules apply: no | ||
/// guarantee is made about which of the NaN bit patterns described above will be returned. The | ||
/// result does not have to match what happens when executing the same code at runtime, and the | ||
/// result can vary depending on factors such as compiler version and flags. | ||
/// | ||
/// ### Target-specific "extra" NaN values | ||
// FIXME: Is there a better place to put this? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like this question could be asked of most of the generic floating-point docs currently attached to the (In summary, solving this can be left to a future PR.) |
||
/// | ||
/// | `target_arch` | Extra payloads possible on this platform | | ||
/// |---------------|---------| | ||
/// | `x86`, `x86_64`, `arm`, `aarch64`, `riscv32`, `riscv64` | None | | ||
/// | `sparc`, `sparc64` | The all-one payload | | ||
/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.<br> Otherwise: all possible payloads. | | ||
/// | ||
/// For targets not in this table, all payloads are possible. | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
mod prim_f32 {} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should mention the exponent part too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should it say there? The point of this section is to document the degrees of freedom of a NaN value, and the exponent is fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but the "consists of" list isn't complete if it doesn't specify how the NaN is tagged as a NaN, which happens in the exponent bits (combined with at least one nonzero mantissa bit). Otherwise it's like describing Rust enums without mentioning the discriminator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A Some value "consists of" the
T
that it carries in its field. Similarly, a NaN consists of the ingredients listed here. The discriminator is what decides whether an "Option" value is a "Some" value, and whether a float value is a NaN value, but it is not part of the Some/Option value.