diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 9b9cc80a606c6..20353c65d491e 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1800,6 +1800,11 @@ impl f128 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f128::powi(f128::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index ab765ebcb7fa7..823bd4917b3d5 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1775,6 +1775,11 @@ impl f16 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f16::powi(f16::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index 3b787713afa24..6f1fd2975b714 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -19,6 +19,11 @@ use crate::sys::cmath; impl f128 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f128::powf(f128::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 4af21c95c9baf..20d0b4e1e552b 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -19,6 +19,11 @@ use crate::sys::cmath; impl f16 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f16::powf(f16::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 09ced388a3399..97ae334e19946 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -295,6 +295,11 @@ impl f32 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f32::powi(f32::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -320,6 +325,11 @@ impl f32 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f32::powf(f32::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 79adf076e4b1a..aaeaa86ca77e1 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -295,6 +295,11 @@ impl f64 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f64::powi(f64::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -320,6 +325,11 @@ impl f64 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f64::powf(f64::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 50472ed3638e9..f9ff4c84c129d 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -272,8 +272,9 @@ where ("pow", [base, exp]) if *base == one => { let rng = this.machine.rng.get_mut(); // SNaN exponents get special treatment: they might return 1, or a NaN. + // This is non-deterministic because LLVM can treat SNaN as QNaN, and because + // implementation behavior differs between glibc and musl. let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random(); - // Handle both the musl and glibc cases non-deterministically. if return_nan { this.generate_nan(args) } else { one } } @@ -281,8 +282,9 @@ where ("pow", [base, exp]) if exp.is_zero() => { let rng = this.machine.rng.get_mut(); // SNaN bases get special treatment: they might return 1, or a NaN. + // This is non-deterministic because LLVM can treat SNaN as QNaN, and because + // implementation behavior differs between glibc and musl. let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random(); - // Handle both the musl and glibc cases non-deterministically. if return_nan { this.generate_nan(args) } else { one } } @@ -306,10 +308,9 @@ where 0 => { let one = IeeeFloat::::one(); let rng = ecx.machine.rng.get_mut(); - let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); - // For SNaN treatment, we are consistent with `powf`above. - // (We wouldn't have two, unlike powf all implementations seem to agree for powi, - // but for now we are maximally conservative.) + // SNaN bases get special treatment: they might return 1, or a NaN. + // This is non-deterministic because LLVM can treat SNaN as QNaN. + let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random(); Some(if return_nan { ecx.generate_nan(&[base]) } else { one }) }