Skip to content

Commit 75ae913

Browse files
authored
Rollup merge of #129559 - RalfJung:float-nan-semantics, r=thomcc
float types: document NaN bit pattern guarantees Part of #128288: document the guarantees we make for NaN bit patterns. Cc ``@tgross35``
2 parents 8ea70e9 + 0c7d6c4 commit 75ae913

File tree

9 files changed

+150
-56
lines changed

9 files changed

+150
-56
lines changed

library/core/src/num/f128.rs

+18-12
Original file line numberDiff line numberDiff line change
@@ -454,11 +454,14 @@ impl f128 {
454454
}
455455

456456
/// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
457-
/// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
458-
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
459-
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
460-
/// `is_sign_positive` on a NaN might produce an unexpected result in some cases.
461-
/// See [explanation of NaN as a special value](f128) for more info.
457+
/// positive sign bit and positive infinity.
458+
///
459+
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
460+
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
461+
/// conserved over arithmetic operations, the result of `is_sign_positive` on
462+
/// a NaN might produce an unexpected or non-portable result. See the [specification
463+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0`
464+
/// if you need fully portable behavior (will return `false` for all NaNs).
462465
///
463466
/// ```
464467
/// #![feature(f128)]
@@ -477,11 +480,14 @@ impl f128 {
477480
}
478481

479482
/// Returns `true` if `self` has a negative sign, including `-0.0`, NaNs with
480-
/// negative sign bit and negative infinity. Note that IEEE 754 doesn't assign any
481-
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
482-
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
483-
/// `is_sign_negative` on a NaN might produce an unexpected result in some cases.
484-
/// See [explanation of NaN as a special value](f128) for more info.
483+
/// negative sign bit and negative infinity.
484+
///
485+
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
486+
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
487+
/// conserved over arithmetic operations, the result of `is_sign_negative` on
488+
/// a NaN might produce an unexpected or non-portable result. See the [specification
489+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0`
490+
/// if you need fully portable behavior (will return `false` for all NaNs).
485491
///
486492
/// ```
487493
/// #![feature(f128)]
@@ -750,7 +756,7 @@ impl f128 {
750756
/// Note that this follows the semantics specified in IEEE 754-2019.
751757
///
752758
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
753-
/// operand is conserved; see [explanation of NaN as a special value](f128) for more info.
759+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
754760
#[inline]
755761
#[unstable(feature = "f128", issue = "116909")]
756762
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]
@@ -791,7 +797,7 @@ impl f128 {
791797
/// Note that this follows the semantics specified in IEEE 754-2019.
792798
///
793799
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
794-
/// operand is conserved; see [explanation of NaN as a special value](f128) for more info.
800+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
795801
#[inline]
796802
#[unstable(feature = "f128", issue = "116909")]
797803
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]

library/core/src/num/f16.rs

+18-12
Original file line numberDiff line numberDiff line change
@@ -464,11 +464,14 @@ impl f16 {
464464
}
465465

466466
/// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
467-
/// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
468-
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
469-
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
470-
/// `is_sign_positive` on a NaN might produce an unexpected result in some cases.
471-
/// See [explanation of NaN as a special value](f16) for more info.
467+
/// positive sign bit and positive infinity.
468+
///
469+
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
470+
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
471+
/// conserved over arithmetic operations, the result of `is_sign_positive` on
472+
/// a NaN might produce an unexpected or non-portable result. See the [specification
473+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0`
474+
/// if you need fully portable behavior (will return `false` for all NaNs).
472475
///
473476
/// ```
474477
/// #![feature(f16)]
@@ -490,11 +493,14 @@ impl f16 {
490493
}
491494

492495
/// Returns `true` if `self` has a negative sign, including `-0.0`, NaNs with
493-
/// negative sign bit and negative infinity. Note that IEEE 754 doesn't assign any
494-
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
495-
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
496-
/// `is_sign_negative` on a NaN might produce an unexpected result in some cases.
497-
/// See [explanation of NaN as a special value](f16) for more info.
496+
/// negative sign bit and negative infinity.
497+
///
498+
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
499+
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
500+
/// conserved over arithmetic operations, the result of `is_sign_negative` on
501+
/// a NaN might produce an unexpected or non-portable result. See the [specification
502+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0`
503+
/// if you need fully portable behavior (will return `false` for all NaNs).
498504
///
499505
/// ```
500506
/// #![feature(f16)]
@@ -762,7 +768,7 @@ impl f16 {
762768
/// Note that this follows the semantics specified in IEEE 754-2019.
763769
///
764770
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
765-
/// operand is conserved; see [explanation of NaN as a special value](f16) for more info.
771+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
766772
#[inline]
767773
#[unstable(feature = "f16", issue = "116909")]
768774
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]
@@ -802,7 +808,7 @@ impl f16 {
802808
/// Note that this follows the semantics specified in IEEE 754-2019.
803809
///
804810
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
805-
/// operand is conserved; see [explanation of NaN as a special value](f16) for more info.
811+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
806812
#[inline]
807813
#[unstable(feature = "f16", issue = "116909")]
808814
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]

library/core/src/num/f32.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -700,8 +700,9 @@ impl f32 {
700700
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
701701
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
702702
/// conserved over arithmetic operations, the result of `is_sign_positive` on
703-
/// a NaN might produce an unexpected result in some cases. See [explanation
704-
/// of NaN as a special value](f32) for more info.
703+
/// a NaN might produce an unexpected or non-portable result. See the [specification
704+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0`
705+
/// if you need fully portable behavior (will return `false` for all NaNs).
705706
///
706707
/// ```
707708
/// let f = 7.0_f32;
@@ -724,8 +725,9 @@ impl f32 {
724725
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
725726
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
726727
/// conserved over arithmetic operations, the result of `is_sign_negative` on
727-
/// a NaN might produce an unexpected result in some cases. See [explanation
728-
/// of NaN as a special value](f32) for more info.
728+
/// a NaN might produce an unexpected or non-portable result. See the [specification
729+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0`
730+
/// if you need fully portable behavior (will return `false` for all NaNs).
729731
///
730732
/// ```
731733
/// let f = 7.0f32;
@@ -954,7 +956,7 @@ impl f32 {
954956
/// Note that this follows the semantics specified in IEEE 754-2019.
955957
///
956958
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
957-
/// operand is conserved; see [explanation of NaN as a special value](f32) for more info.
959+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
958960
#[must_use = "this returns the result of the comparison, without modifying either input"]
959961
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
960962
#[inline]
@@ -989,7 +991,7 @@ impl f32 {
989991
/// Note that this follows the semantics specified in IEEE 754-2019.
990992
///
991993
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
992-
/// operand is conserved; see [explanation of NaN as a special value](f32) for more info.
994+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
993995
#[must_use = "this returns the result of the comparison, without modifying either input"]
994996
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
995997
#[inline]

library/core/src/num/f64.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -695,8 +695,9 @@ impl f64 {
695695
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
696696
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
697697
/// conserved over arithmetic operations, the result of `is_sign_positive` on
698-
/// a NaN might produce an unexpected result in some cases. See [explanation
699-
/// of NaN as a special value](f32) for more info.
698+
/// a NaN might produce an unexpected or non-portable result. See the [specification
699+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0`
700+
/// if you need fully portable behavior (will return `false` for all NaNs).
700701
///
701702
/// ```
702703
/// let f = 7.0_f64;
@@ -728,8 +729,9 @@ impl f64 {
728729
/// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of
729730
/// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are
730731
/// conserved over arithmetic operations, the result of `is_sign_negative` on
731-
/// a NaN might produce an unexpected result in some cases. See [explanation
732-
/// of NaN as a special value](f32) for more info.
732+
/// a NaN might produce an unexpected or non-portable result. See the [specification
733+
/// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0`
734+
/// if you need fully portable behavior (will return `false` for all NaNs).
733735
///
734736
/// ```
735737
/// let f = 7.0_f64;
@@ -968,7 +970,7 @@ impl f64 {
968970
/// Note that this follows the semantics specified in IEEE 754-2019.
969971
///
970972
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
971-
/// operand is conserved; see [explanation of NaN as a special value](f32) for more info.
973+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
972974
#[must_use = "this returns the result of the comparison, without modifying either input"]
973975
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
974976
#[inline]
@@ -1003,7 +1005,7 @@ impl f64 {
10031005
/// Note that this follows the semantics specified in IEEE 754-2019.
10041006
///
10051007
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
1006-
/// operand is conserved; see [explanation of NaN as a special value](f32) for more info.
1008+
/// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info.
10071009
#[must_use = "this returns the result of the comparison, without modifying either input"]
10081010
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
10091011
#[inline]

library/core/src/primitive_docs.rs

+78
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,11 @@ mod prim_f16 {}
11901190
/// portable or even fully deterministic! This means that there may be some
11911191
/// surprising results upon inspecting the bit patterns,
11921192
/// as the same calculations might produce NaNs with different bit patterns.
1193+
/// This also affects the sign of the NaN: checking `is_sign_positive` or `is_sign_negative` on
1194+
/// a NaN is the most common way to run into these surprising results.
1195+
/// (Checking `x >= 0.0` or `x <= 0.0` avoids those surprises, but also how negative/positive
1196+
/// zero are treated.)
1197+
/// See the section below for what exactly is guaranteed about the bit pattern of a NaN.
11931198
///
11941199
/// When a primitive operation (addition, subtraction, multiplication, or
11951200
/// division) is performed on this type, the result is rounded according to the
@@ -1211,6 +1216,79 @@ mod prim_f16 {}
12111216
/// *[See also the `std::f32::consts` module](crate::f32::consts).*
12121217
///
12131218
/// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format
1219+
///
1220+
/// # NaN bit patterns
1221+
///
1222+
/// This section defines the possible NaN bit patterns returned by non-"bitwise" floating point
1223+
/// operations. The bitwise operations are unary `-`, `abs`, `copysign`; those are guaranteed to
1224+
/// exactly preserve the bit pattern of their input except for possibly changing the sign bit.
1225+
///
1226+
/// A floating-point NaN value consists of:
1227+
/// - a sign bit
1228+
/// - a quiet/signaling bit
1229+
/// - a payload, which makes up the rest of the significand (i.e., the mantissa) except for the
1230+
/// quiet/signaling bit.
1231+
///
1232+
/// Rust assumes that the quiet/signaling bit being set to `1` indicates a quiet NaN (QNaN), and a
1233+
/// value of `0` indicates a signaling NaN (SNaN). In the following we will hence just call it the
1234+
/// "quiet bit".
1235+
///
1236+
/// The following rules apply when a NaN value is returned: the result has a non-deterministic sign.
1237+
/// The quiet bit and payload are non-deterministically chosen from the following set of options:
1238+
///
1239+
/// - **Preferred NaN**: The quiet bit is set and the payload is all-zero.
1240+
/// - **Quieting NaN propagation**: The quiet bit is set and the payload is copied from any input
1241+
/// operand that is a NaN. If the inputs and outputs do not have the same payload size (i.e., for
1242+
/// `as` casts), then
1243+
/// - If the output is smaller than the input, low-order bits of the payload get dropped.
1244+
/// - If the output is larger than the input, the payload gets filled up with 0s in the low-order
1245+
/// bits.
1246+
/// - **Unchanged NaN propagation**: The quiet bit and payload are copied from any input operand
1247+
/// that is a NaN. If the inputs and outputs do not have the same size (i.e., for `as` casts), the
1248+
/// same rules as for "quieting NaN propagation" apply, with one caveat: if the output is smaller
1249+
/// than the input, droppig the low-order bits may result in a payload of 0; a payload of 0 is not
1250+
/// possible with a signaling NaN (the all-0 significand encodes an infinity) so unchanged NaN
1251+
/// propagation cannot occur with some inputs.
1252+
/// - **Target-specific NaN**: The quiet bit is set and the payload is picked from a target-specific
1253+
/// set of "extra" possible NaN payloads. The set can depend on the input operand values.
1254+
/// See the table below for the concrete NaNs this set contains on various targets.
1255+
///
1256+
/// In particular, if all input NaNs are quiet (or if there are no input NaNs), then the output NaN
1257+
/// is definitely quiet. Signaling NaN outputs can only occur if they are provided as an input
1258+
/// value. Similarly, if all input NaNs are preferred (or if there are no input NaNs) and the target
1259+
/// does not have any "extra" NaN payloads, then the output NaN is guaranteed to be preferred.
1260+
///
1261+
/// The non-deterministic choice happens when the operation is executed; i.e., the result of a
1262+
/// NaN-producing floating point operation is a stable bit pattern (looking at these bits multiple
1263+
/// times will yield consistent results), but running the same operation twice with the same inputs
1264+
/// can produce different results.
1265+
///
1266+
/// These guarantees are neither stronger nor weaker than those of IEEE 754: IEEE 754 guarantees
1267+
/// that an operation never returns a signaling NaN, whereas it is possible for operations like
1268+
/// `SNAN * 1.0` to return a signaling NaN in Rust. Conversely, IEEE 754 makes no statement at all
1269+
/// about which quiet NaN is returned, whereas Rust restricts the set of possible results to the
1270+
/// ones listed above.
1271+
///
1272+
/// Unless noted otherwise, the same rules also apply to NaNs returned by other library functions
1273+
/// (e.g. `min`, `minimum`, `max`, `maximum`); other aspects of their semantics and which IEEE 754
1274+
/// operation they correspond to are documented with the respective functions.
1275+
///
1276+
/// When a floating-point operation is executed in `const` context, the same rules apply: no
1277+
/// guarantee is made about which of the NaN bit patterns described above will be returned. The
1278+
/// result does not have to match what happens when executing the same code at runtime, and the
1279+
/// result can vary depending on factors such as compiler version and flags.
1280+
///
1281+
/// ### Target-specific "extra" NaN values
1282+
// FIXME: Is there a better place to put this?
1283+
///
1284+
/// | `target_arch` | Extra payloads possible on this platform |
1285+
/// |---------------|---------|
1286+
/// | `x86`, `x86_64`, `arm`, `aarch64`, `riscv32`, `riscv64` | None |
1287+
/// | `sparc`, `sparc64` | The all-one payload |
1288+
/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.<br> Otherwise: all possible payloads. |
1289+
///
1290+
/// For targets not in this table, all payloads are possible.
1291+
12141292
#[stable(feature = "rust1", since = "1.0.0")]
12151293
mod prim_f32 {}
12161294

library/std/src/f128.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,11 @@ impl f128 {
248248
/// Returns a number composed of the magnitude of `self` and the sign of
249249
/// `sign`.
250250
///
251-
/// Equal to `self` if the sign of `self` and `sign` are the same, otherwise
252-
/// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of
253-
/// `sign` is returned. Note, however, that conserving the sign bit on NaN
254-
/// across arithmetical operations is not generally guaranteed.
255-
/// See [explanation of NaN as a special value](primitive@f128) for more info.
251+
/// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`.
252+
/// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is
253+
/// returned. Note, however, that conserving the sign bit on NaN across arithmetical operations
254+
/// is not generally guaranteed. See [specification of NaN bit
255+
/// patterns](primitive@f32#nan-bit-patterns) for more info.
256256
///
257257
/// # Examples
258258
///

library/std/src/f16.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,11 @@ impl f16 {
247247
/// Returns a number composed of the magnitude of `self` and the sign of
248248
/// `sign`.
249249
///
250-
/// Equal to `self` if the sign of `self` and `sign` are the same, otherwise
251-
/// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of
252-
/// `sign` is returned. Note, however, that conserving the sign bit on NaN
253-
/// across arithmetical operations is not generally guaranteed.
254-
/// See [explanation of NaN as a special value](primitive@f16) for more info.
250+
/// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`.
251+
/// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is
252+
/// returned. Note, however, that conserving the sign bit on NaN across arithmetical operations
253+
/// is not generally guaranteed. See [specification of NaN bit
254+
/// patterns](primitive@f32#nan-bit-patterns) for more info.
255255
///
256256
/// # Examples
257257
///

0 commit comments

Comments
 (0)