miri/
math.rs

1use rand::Rng as _;
2use rustc_apfloat::Float as _;
3use rustc_apfloat::ieee::IeeeFloat;
4use rustc_middle::ty::{self, FloatTy, ScalarInt};
5
6use crate::*;
7
8/// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale).
9pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
10    ecx: &mut crate::MiriInterpCx<'_>,
11    val: F,
12    err_scale: i32,
13) -> F {
14    if !ecx.machine.float_nondet
15        || matches!(ecx.machine.float_rounding_error, FloatRoundingErrorMode::None)
16        // relative errors don't do anything to zeros... avoid messing up the sign
17        || val.is_zero()
18        // The logic below makes no sense if the input is already non-finite.
19        || !val.is_finite()
20    {
21        return val;
22    }
23    let rng = ecx.machine.rng.get_mut();
24
25    // Generate a random integer in the range [0, 2^PREC).
26    // (When read as binary, the position of the first `1` determines the exponent,
27    // and the remaining bits fill the mantissa. `PREC` is one plus the size of the mantissa,
28    // so this all works out.)
29    let r = F::from_u128(match ecx.machine.float_rounding_error {
30        FloatRoundingErrorMode::Random => rng.random_range(0..(1 << F::PRECISION)),
31        FloatRoundingErrorMode::Max => (1 << F::PRECISION) - 1, // force max error
32        FloatRoundingErrorMode::None => unreachable!(),
33    })
34    .value;
35    // Multiply this with 2^(scale - PREC). The result is between 0 and
36    // 2^PREC * 2^(scale - PREC) = 2^scale.
37    let err = r.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
38    // give it a random sign
39    let err = if rng.random() { -err } else { err };
40    // Compute `val*(1+err)`, distributed out as `val + val*err` to avoid the imprecise addition
41    // error being amplified by multiplication.
42    (val + (val * err).value).value
43}
44
45/// Applies an error of `[-N, +N]` ULP to the given value.
46pub(crate) fn apply_random_float_error_ulp<F: rustc_apfloat::Float>(
47    ecx: &mut crate::MiriInterpCx<'_>,
48    val: F,
49    max_error: u32,
50) -> F {
51    // We could try to be clever and reuse `apply_random_float_error`, but that is hard to get right
52    // (see <https://github.com/rust-lang/miri/pull/4558#discussion_r2316838085> for why) so we
53    // implement the logic directly instead.
54    if !ecx.machine.float_nondet
55        || matches!(ecx.machine.float_rounding_error, FloatRoundingErrorMode::None)
56        // FIXME: also disturb zeros? That requires a lot more cases in `fixed_float_value`
57        // and might make the std test suite quite unhappy.
58        || val.is_zero()
59        // The logic below makes no sense if the input is already non-finite.
60        || !val.is_finite()
61    {
62        return val;
63    }
64    let rng = ecx.machine.rng.get_mut();
65
66    let max_error = i64::from(max_error);
67    let error = match ecx.machine.float_rounding_error {
68        FloatRoundingErrorMode::Random => rng.random_range(-max_error..=max_error),
69        FloatRoundingErrorMode::Max =>
70            if rng.random() {
71                max_error
72            } else {
73                -max_error
74            },
75        FloatRoundingErrorMode::None => unreachable!(),
76    };
77    // If upwards ULP and downwards ULP differ, we take the average.
78    let ulp = (((val.next_up().value - val).value + (val - val.next_down().value).value).value
79        / F::from_u128(2).value)
80        .value;
81    // Shift the value by N times the ULP
82    (val + (ulp * F::from_i128(error.into()).value).value).value
83}
84
85/// Applies an error of `[-N, +N]` ULP to the given value.
86/// Will fail if `val` is not a floating point number.
87pub(crate) fn apply_random_float_error_to_imm<'tcx>(
88    ecx: &mut MiriInterpCx<'tcx>,
89    val: ImmTy<'tcx>,
90    max_error: u32,
91) -> InterpResult<'tcx, ImmTy<'tcx>> {
92    let scalar = val.to_scalar_int()?;
93    let res: ScalarInt = match val.layout.ty.kind() {
94        ty::Float(FloatTy::F16) =>
95            apply_random_float_error_ulp(ecx, scalar.to_f16(), max_error).into(),
96        ty::Float(FloatTy::F32) =>
97            apply_random_float_error_ulp(ecx, scalar.to_f32(), max_error).into(),
98        ty::Float(FloatTy::F64) =>
99            apply_random_float_error_ulp(ecx, scalar.to_f64(), max_error).into(),
100        ty::Float(FloatTy::F128) =>
101            apply_random_float_error_ulp(ecx, scalar.to_f128(), max_error).into(),
102        _ => bug!("intrinsic called with non-float input type"),
103    };
104
105    interp_ok(ImmTy::from_scalar_int(res, val.layout))
106}
107
108pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> {
109    match x.category() {
110        // preserve zero sign
111        rustc_apfloat::Category::Zero => x,
112        // propagate NaN
113        rustc_apfloat::Category::NaN => x,
114        // sqrt of negative number is NaN
115        _ if x.is_negative() => IeeeFloat::NAN,
116        // sqrt(∞) = ∞
117        rustc_apfloat::Category::Infinity => IeeeFloat::INFINITY,
118        rustc_apfloat::Category::Normal => {
119            // Floating point precision, excluding the integer bit
120            let prec = i32::try_from(S::PRECISION).unwrap() - 1;
121
122            // x = 2^(exp - prec) * mant
123            // where mant is an integer with prec+1 bits
124            // mant is a u128, which should be large enough for the largest prec (112 for f128)
125            let mut exp = x.ilogb();
126            let mut mant = x.scalbn(prec - exp).to_u128(128).value;
127
128            if exp % 2 != 0 {
129                // Make exponent even, so it can be divided by 2
130                exp -= 1;
131                mant <<= 1;
132            }
133
134            // Bit-by-bit (base-2 digit-by-digit) sqrt of mant.
135            // mant is treated here as a fixed point number with prec fractional bits.
136            // mant will be shifted left by one bit to have an extra fractional bit, which
137            // will be used to determine the rounding direction.
138
139            // res is the truncated sqrt of mant, where one bit is added at each iteration.
140            let mut res = 0u128;
141            // rem is the remainder with the current res
142            // rem_i = 2^i * ((mant<<1) - res_i^2)
143            // starting with res = 0, rem = mant<<1
144            let mut rem = mant << 1;
145            // s_i = 2*res_i
146            let mut s = 0u128;
147            // d is used to iterate over bits, from high to low (d_i = 2^(-i))
148            let mut d = 1u128 << (prec + 1);
149
150            // For iteration j=i+1, we need to find largest b_j = 0 or 1 such that
151            //  (res_i + b_j * 2^(-j))^2 <= mant<<1
152            // Expanding (a + b)^2 = a^2 + b^2 + 2*a*b:
153            //  res_i^2 + (b_j * 2^(-j))^2 + 2 * res_i * b_j * 2^(-j) <= mant<<1
154            // And rearranging the terms:
155            //  b_j^2 * 2^(-j) + 2 * res_i * b_j <= 2^j * (mant<<1 - res_i^2)
156            //  b_j^2 * 2^(-j) + 2 * res_i * b_j <= rem_i
157
158            while d != 0 {
159                // Probe b_j^2 * 2^(-j) + 2 * res_i * b_j <= rem_i with b_j = 1:
160                // t = 2*res_i + 2^(-j)
161                let t = s + d;
162                if rem >= t {
163                    // b_j should be 1, so make res_j = res_i + 2^(-j) and adjust rem
164                    res += d;
165                    s += d + d;
166                    rem -= t;
167                }
168                // Adjust rem for next iteration
169                rem <<= 1;
170                // Shift iterator
171                d >>= 1;
172            }
173
174            // Remove extra fractional bit from result, rounding to nearest.
175            // If the last bit is 0, then the nearest neighbor is definitely the lower one.
176            // If the last bit is 1, it sounds like this may either be a tie (if there's
177            // infinitely many 0s after this 1), or the nearest neighbor is the upper one.
178            // However, since square roots are either exact or irrational, and an exact root
179            // would lead to the last "extra" bit being 0, we can exclude a tie in this case.
180            // We therefore always round up if the last bit is 1. When the last bit is 0,
181            // adding 1 will not do anything since the shift will discard it.
182            res = (res + 1) >> 1;
183
184            // Build resulting value with res as mantissa and exp/2 as exponent
185            IeeeFloat::from_u128(res).value.scalbn(exp / 2 - prec)
186        }
187    }
188}
189
190/// Extend functionality of rustc_apfloat softfloats
191pub trait IeeeExt: rustc_apfloat::Float {
192    #[inline]
193    fn one() -> Self {
194        Self::from_u128(1).value
195    }
196
197    #[inline]
198    fn clamp(self, min: Self, max: Self) -> Self {
199        self.maximum(min).minimum(max)
200    }
201}
202impl<S: rustc_apfloat::ieee::Semantics> IeeeExt for IeeeFloat<S> {}
203
204#[cfg(test)]
205mod tests {
206    use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS};
207
208    use super::sqrt;
209
210    #[test]
211    fn test_sqrt() {
212        #[track_caller]
213        fn test<S: rustc_apfloat::ieee::Semantics>(x: &str, expected: &str) {
214            let x: IeeeFloat<S> = x.parse().unwrap();
215            let expected: IeeeFloat<S> = expected.parse().unwrap();
216            let result = sqrt(x);
217            assert_eq!(result, expected);
218        }
219
220        fn exact_tests<S: rustc_apfloat::ieee::Semantics>() {
221            test::<S>("0", "0");
222            test::<S>("1", "1");
223            test::<S>("1.5625", "1.25");
224            test::<S>("2.25", "1.5");
225            test::<S>("4", "2");
226            test::<S>("5.0625", "2.25");
227            test::<S>("9", "3");
228            test::<S>("16", "4");
229            test::<S>("25", "5");
230            test::<S>("36", "6");
231            test::<S>("49", "7");
232            test::<S>("64", "8");
233            test::<S>("81", "9");
234            test::<S>("100", "10");
235
236            test::<S>("0.5625", "0.75");
237            test::<S>("0.25", "0.5");
238            test::<S>("0.0625", "0.25");
239            test::<S>("0.00390625", "0.0625");
240        }
241
242        exact_tests::<HalfS>();
243        exact_tests::<SingleS>();
244        exact_tests::<DoubleS>();
245        exact_tests::<QuadS>();
246
247        test::<SingleS>("2", "1.4142135");
248        test::<DoubleS>("2", "1.4142135623730951");
249
250        test::<SingleS>("1.1", "1.0488088");
251        test::<DoubleS>("1.1", "1.0488088481701516");
252
253        test::<SingleS>("2.2", "1.4832398");
254        test::<DoubleS>("2.2", "1.4832396974191326");
255
256        test::<SingleS>("1.22101e-40", "1.10499205e-20");
257        test::<DoubleS>("1.22101e-310", "1.1049932126488395e-155");
258
259        test::<SingleS>("3.4028235e38", "1.8446743e19");
260        test::<DoubleS>("1.7976931348623157e308", "1.3407807929942596e154");
261    }
262}