|
1 | 1 | use std::ops::Neg;
|
| 2 | +use std::{f32, f64}; |
2 | 3 |
|
3 | 4 | use rand::Rng as _;
|
4 | 5 | use rustc_apfloat::Float as _;
|
5 |
| -use rustc_apfloat::ieee::{IeeeFloat, Semantics}; |
| 6 | +use rustc_apfloat::ieee::{DoubleS, IeeeFloat, Semantics, SingleS}; |
6 | 7 | use rustc_middle::ty::{self, FloatTy, ScalarInt};
|
7 | 8 |
|
8 | 9 | use crate::*;
|
@@ -107,29 +108,69 @@ pub(crate) fn apply_random_float_error_to_imm<'tcx>(
|
107 | 108 | interp_ok(ImmTy::from_scalar_int(res, val.layout))
|
108 | 109 | }
|
109 | 110 |
|
110 |
| -/// Given an floating-point operation and a floating-point value, clamps the result to the output |
111 |
| -/// range of the given operation. |
| 111 | +/// Given a floating-point operation and a floating-point value, clamps the result to the output |
| 112 | +/// range of the given operation according to the C standard, if any. |
112 | 113 | pub(crate) fn clamp_float_value<S: Semantics>(
|
113 | 114 | intrinsic_name: &str,
|
114 | 115 | val: IeeeFloat<S>,
|
115 |
| -) -> IeeeFloat<S> { |
| 116 | +) -> IeeeFloat<S> |
| 117 | +where |
| 118 | + IeeeFloat<S>: IeeeExt, |
| 119 | +{ |
| 120 | + let zero = IeeeFloat::<S>::ZERO; |
| 121 | + let one = IeeeFloat::<S>::one(); |
| 122 | + let two = IeeeFloat::<S>::two(); |
| 123 | + let pi = IeeeFloat::<S>::pi(); |
| 124 | + let pi_over_2 = (pi / two).value; |
| 125 | + |
116 | 126 | match intrinsic_name {
|
117 |
| - // sin and cos: [-1, 1] |
118 |
| - "sinf32" | "cosf32" | "sinf64" | "cosf64" => |
119 |
| - val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()), |
120 |
| - // exp: [0, +INF] |
121 |
| - "expf32" | "exp2f32" | "expf64" | "exp2f64" => |
122 |
| - IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO), |
| 127 | + // sin, cos, tanh: [-1, 1] |
| 128 | + #[rustfmt::skip] |
| 129 | + | "sinf32" |
| 130 | + | "sinf64" |
| 131 | + | "cosf32" |
| 132 | + | "cosf64" |
| 133 | + | "tanhf" |
| 134 | + | "tanh" |
| 135 | + => val.clamp(one.neg(), one), |
| 136 | + |
| 137 | + // exp: [0, +INF) |
| 138 | + "expf32" | "exp2f32" | "expf64" | "exp2f64" => val.maximum(zero), |
| 139 | + |
| 140 | + // cosh: [1, +INF) |
| 141 | + "coshf" | "cosh" => val.maximum(one), |
| 142 | + |
| 143 | + // acos: [0, π] |
| 144 | + "acosf" | "acos" => val.clamp(zero, pi), |
| 145 | + |
| 146 | + // asin: [-π, +π] |
| 147 | + "asinf" | "asin" => val.clamp(pi.neg(), pi), |
| 148 | + |
| 149 | + // atan: (-π/2, +π/2) |
| 150 | + "atanf" | "atan" => val.clamp(pi_over_2.neg(), pi_over_2), |
| 151 | + |
| 152 | + // erfc: (-1, 1) |
| 153 | + "erff" | "erf" => val.clamp(one.neg(), one), |
| 154 | + |
| 155 | + // erfc: (0, 2) |
| 156 | + "erfcf" | "erfc" => val.clamp(zero, two), |
| 157 | + |
| 158 | + // atan2(y, x): arctan(y/x) in [−π, +π] |
| 159 | + "atan2f" | "atan2" => val.clamp(pi.neg(), pi), |
| 160 | + |
123 | 161 | _ => val,
|
124 | 162 | }
|
125 | 163 | }
|
126 | 164 |
|
127 | 165 | /// For the intrinsics:
|
128 |
| -/// - sinf32, sinf64 |
129 |
| -/// - cosf32, cosf64 |
| 166 | +/// - sinf32, sinf64, sinhf, sinh |
| 167 | +/// - cosf32, cosf64, coshf, cosh |
| 168 | +/// - tanhf, tanh, atanf, atan, atan2f, atan2 |
130 | 169 | /// - expf32, expf64, exp2f32, exp2f64
|
131 | 170 | /// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
|
132 | 171 | /// - powf32, powf64
|
| 172 | +/// - erff, erf, erfcf, erfc |
| 173 | +/// - hypotf, hypot |
133 | 174 | ///
|
134 | 175 | /// # Return
|
135 | 176 | ///
|
@@ -157,16 +198,68 @@ pub(crate) fn fixed_float_value<S: Semantics>(
|
157 | 198 | ecx: &mut MiriInterpCx<'_>,
|
158 | 199 | intrinsic_name: &str,
|
159 | 200 | args: &[IeeeFloat<S>],
|
160 |
| -) -> Option<IeeeFloat<S>> { |
| 201 | +) -> Option<IeeeFloat<S>> |
| 202 | +where |
| 203 | + IeeeFloat<S>: IeeeExt, |
| 204 | +{ |
161 | 205 | let this = ecx.eval_context_mut();
|
162 | 206 | let one = IeeeFloat::<S>::one();
|
| 207 | + let two = IeeeFloat::<S>::two(); |
| 208 | + let three = IeeeFloat::<S>::three(); |
| 209 | + let pi = IeeeFloat::<S>::pi(); |
| 210 | + let pi_over_2 = (pi / two).value; |
| 211 | + let pi_over_4 = (pi_over_2 / two).value; |
| 212 | + |
163 | 213 | Some(match (intrinsic_name, args) {
|
164 |
| - // cos(+- 0) = 1 |
165 |
| - ("cosf32" | "cosf64", [input]) if input.is_zero() => one, |
| 214 | + // cos(±0) and cosh(±0)= 1 |
| 215 | + ("cosf32" | "cosf64" | "coshf" | "cosh", [input]) if input.is_zero() => one, |
166 | 216 |
|
167 | 217 | // e^0 = 1
|
168 | 218 | ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one,
|
169 | 219 |
|
| 220 | + // tanh(±INF) = ±1 |
| 221 | + ("tanhf" | "tanh", [input]) if input.is_infinite() => one.copy_sign(*input), |
| 222 | + |
| 223 | + // atan(±INF) = ±π/2 |
| 224 | + ("atanf" | "atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input), |
| 225 | + |
| 226 | + // erf(±INF) = ±1 |
| 227 | + ("erff" | "erf", [input]) if input.is_infinite() => one.copy_sign(*input), |
| 228 | + |
| 229 | + // erfc(-INF) = 2 |
| 230 | + ("erfcf" | "erfc", [input]) if input.is_neg_infinity() => (one + one).value, |
| 231 | + |
| 232 | + // hypot(x, ±0) = abs(x), if x is not a NaN. |
| 233 | + ("_hypotf" | "hypotf" | "_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => |
| 234 | + x.abs(), |
| 235 | + |
| 236 | + // atan2(±0,−0) = ±π. |
| 237 | + // atan2(±0, y) = ±π for y < 0. |
| 238 | + // Must check for non NaN because `y.is_negative()` also applies to NaN. |
| 239 | + ("atan2f" | "atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => |
| 240 | + pi.copy_sign(*x), |
| 241 | + |
| 242 | + // atan2(±x,−∞) = ±π for finite x > 0. |
| 243 | + ("atan2f" | "atan2", [x, y]) |
| 244 | + if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => |
| 245 | + pi.copy_sign(*x), |
| 246 | + |
| 247 | + // atan2(x, ±0) = −π/2 for x < 0. |
| 248 | + // atan2(x, ±0) = π/2 for x > 0. |
| 249 | + ("atan2f" | "atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x), |
| 250 | + |
| 251 | + //atan2(±∞, −∞) = ±3π/4 |
| 252 | + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() => |
| 253 | + (pi_over_4 * three).value.copy_sign(*x), |
| 254 | + |
| 255 | + //atan2(±∞, +∞) = ±π/4 |
| 256 | + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => |
| 257 | + pi_over_4.copy_sign(*x), |
| 258 | + |
| 259 | + // atan2(±∞, y) returns ±π/2 for finite y. |
| 260 | + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => |
| 261 | + pi_over_2.copy_sign(*x), |
| 262 | + |
170 | 263 | // (-1)^(±INF) = 1
|
171 | 264 | ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one,
|
172 | 265 |
|
@@ -196,25 +289,27 @@ pub(crate) fn fixed_float_value<S: Semantics>(
|
196 | 289 |
|
197 | 290 | /// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
|
198 | 291 | /// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
|
199 |
| -pub(crate) fn fixed_powi_float_value<S: Semantics>( |
| 292 | +pub(crate) fn fixed_powi_value<S: Semantics>( |
200 | 293 | ecx: &mut MiriInterpCx<'_>,
|
201 | 294 | base: IeeeFloat<S>,
|
202 | 295 | exp: i32,
|
203 |
| -) -> Option<IeeeFloat<S>> { |
204 |
| - let this = ecx.eval_context_mut(); |
205 |
| - Some(match exp { |
| 296 | +) -> Option<IeeeFloat<S>> |
| 297 | +where |
| 298 | + IeeeFloat<S>: IeeeExt, |
| 299 | +{ |
| 300 | + match exp { |
206 | 301 | 0 => {
|
207 | 302 | let one = IeeeFloat::<S>::one();
|
208 |
| - let rng = this.machine.rng.get_mut(); |
209 |
| - let return_nan = this.machine.float_nondet && rng.random() && base.is_signaling(); |
| 303 | + let rng = ecx.machine.rng.get_mut(); |
| 304 | + let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); |
210 | 305 | // For SNaN treatment, we are consistent with `powf`above.
|
211 | 306 | // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
|
212 | 307 | // but for now we are maximally conservative.)
|
213 |
| - if return_nan { this.generate_nan(&[base]) } else { one } |
| 308 | + Some(if return_nan { ecx.generate_nan(&[base]) } else { one }) |
214 | 309 | }
|
215 | 310 |
|
216 | 311 | _ => return None,
|
217 |
| - }) |
| 312 | + } |
218 | 313 | }
|
219 | 314 |
|
220 | 315 | pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> {
|
@@ -299,19 +394,47 @@ pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFl
|
299 | 394 | }
|
300 | 395 | }
|
301 | 396 |
|
302 |
| -/// Extend functionality of rustc_apfloat softfloats |
| 397 | +/// Extend functionality of `rustc_apfloat` softfloats for IEEE float types. |
303 | 398 | pub trait IeeeExt: rustc_apfloat::Float {
|
| 399 | + // Some values we use: |
| 400 | + |
304 | 401 | #[inline]
|
305 | 402 | fn one() -> Self {
|
306 | 403 | Self::from_u128(1).value
|
307 | 404 | }
|
308 | 405 |
|
| 406 | + #[inline] |
| 407 | + fn two() -> Self { |
| 408 | + Self::from_u128(2).value |
| 409 | + } |
| 410 | + |
| 411 | + #[inline] |
| 412 | + fn three() -> Self { |
| 413 | + Self::from_u128(3).value |
| 414 | + } |
| 415 | + |
| 416 | + fn pi() -> Self; |
| 417 | + |
309 | 418 | #[inline]
|
310 | 419 | fn clamp(self, min: Self, max: Self) -> Self {
|
311 | 420 | self.maximum(min).minimum(max)
|
312 | 421 | }
|
313 | 422 | }
|
314 |
| -impl<S: rustc_apfloat::ieee::Semantics> IeeeExt for IeeeFloat<S> {} |
| 423 | + |
| 424 | +macro_rules! impl_ieee_pi { |
| 425 | + ($float_ty:ident, $semantic:ty) => { |
| 426 | + impl IeeeExt for IeeeFloat<$semantic> { |
| 427 | + #[inline] |
| 428 | + fn pi() -> Self { |
| 429 | + // We take the value from the standard library as the most reasonable source for an exact π here. |
| 430 | + Self::from_bits($float_ty::consts::PI.to_bits().into()) |
| 431 | + } |
| 432 | + } |
| 433 | + }; |
| 434 | +} |
| 435 | + |
| 436 | +impl_ieee_pi!(f32, SingleS); |
| 437 | +impl_ieee_pi!(f64, DoubleS); |
315 | 438 |
|
316 | 439 | #[cfg(test)]
|
317 | 440 | mod tests {
|
|
0 commit comments