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
8pub(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 || val.is_zero()
18 || !val.is_finite()
20 {
21 return val;
22 }
23 let rng = ecx.machine.rng.get_mut();
24
25 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, FloatRoundingErrorMode::None => unreachable!(),
33 })
34 .value;
35 let err = r.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
38 let err = if rng.random() { -err } else { err };
40 (val + (val * err).value).value
43}
44
45pub(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 if !ecx.machine.float_nondet
55 || matches!(ecx.machine.float_rounding_error, FloatRoundingErrorMode::None)
56 || val.is_zero()
59 || !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 let ulp = (((val.next_up().value - val).value + (val - val.next_down().value).value).value
79 / F::from_u128(2).value)
80 .value;
81 (val + (ulp * F::from_i128(error.into()).value).value).value
83}
84
85pub(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 rustc_apfloat::Category::Zero => x,
112 rustc_apfloat::Category::NaN => x,
114 _ if x.is_negative() => IeeeFloat::NAN,
116 rustc_apfloat::Category::Infinity => IeeeFloat::INFINITY,
118 rustc_apfloat::Category::Normal => {
119 let prec = i32::try_from(S::PRECISION).unwrap() - 1;
121
122 let mut exp = x.ilogb();
126 let mut mant = x.scalbn(prec - exp).to_u128(128).value;
127
128 if exp % 2 != 0 {
129 exp -= 1;
131 mant <<= 1;
132 }
133
134 let mut res = 0u128;
141 let mut rem = mant << 1;
145 let mut s = 0u128;
147 let mut d = 1u128 << (prec + 1);
149
150 while d != 0 {
159 let t = s + d;
162 if rem >= t {
163 res += d;
165 s += d + d;
166 rem -= t;
167 }
168 rem <<= 1;
170 d >>= 1;
172 }
173
174 res = (res + 1) >> 1;
183
184 IeeeFloat::from_u128(res).value.scalbn(exp / 2 - prec)
186 }
187 }
188}
189
190pub 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}