Skip to content

Commit a5eb61a

Browse files
committed
Auto merge of #902 - RalfJung:ldexp, r=RalfJung
use apfloat for ldexp
2 parents 7b3de39 + 04892d9 commit a5eb61a

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

src/shims/foreign_items.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::convert::TryInto;
2+
3+
use rustc_apfloat::Float;
14
use rustc::ty::layout::{Align, LayoutOf, Size};
25
use rustc::hir::def_id::DefId;
36
use rustc::mir;
@@ -577,7 +580,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
577580
};
578581
this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
579582
}
580-
// underscore case for windows
583+
// underscore case for windows, here and below
584+
// (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
581585
"_hypot" | "hypot" | "atan2" => {
582586
// FIXME: Using host floats.
583587
let f1 = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
@@ -589,16 +593,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
589593
};
590594
this.write_scalar(Scalar::from_u64(n.to_bits()), dest)?;
591595
}
592-
// underscore case for windows
593-
"_ldexp" | "ldexp" => {
594-
// FIXME: Using host floats.
595-
let x = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
596+
// For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
597+
"_ldexp" | "ldexp" | "scalbn" => {
598+
let x = this.read_scalar(args[0])?.to_f64()?;
596599
let exp = this.read_scalar(args[1])?.to_i32()?;
597-
extern {
598-
fn ldexp(x: f64, n: i32) -> f64;
599-
}
600-
let n = unsafe { ldexp(x, exp) };
601-
this.write_scalar(Scalar::from_u64(n.to_bits()), dest)?;
600+
601+
// Saturating cast to i16. Even those are outside the valid exponent range to
602+
// `scalbn` below will do its over/underflow handling.
603+
let exp = if exp > i16::max_value() as i32 {
604+
i16::max_value()
605+
} else if exp < i16::min_value() as i32 {
606+
i16::min_value()
607+
} else {
608+
exp.try_into().unwrap()
609+
};
610+
611+
let res = x.scalbn(exp);
612+
this.write_scalar(Scalar::from_f64(res), dest)?;
602613
}
603614

604615
// Some things needed for `sys::thread` initialization to go through.

tests/run-pass/intrinsics-math.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ macro_rules! assert_approx_eq {
1616
})
1717
}
1818

19+
fn ldexp(a: f64, b: i32) -> f64 {
20+
extern {
21+
fn ldexp(x: f64, n: i32) -> f64;
22+
}
23+
unsafe { ldexp(a, b) }
24+
}
25+
1926
pub fn main() {
2027
use std::f32;
2128
use std::f64;
@@ -88,8 +95,7 @@ pub fn main() {
8895
assert_eq!(3.3_f32.round(), 3.0);
8996
assert_eq!(3.3_f64.round(), 3.0);
9097

91-
extern {
92-
fn ldexp(x: f64, n: i32) -> f64;
93-
}
94-
unsafe { assert_approx_eq!(ldexp(0.65f64, 3i32), 5.2f64); }
98+
assert_eq!(ldexp(0.65f64, 3i32), 5.2f64);
99+
assert_eq!(ldexp(1.42, 0xFFFF), f64::INFINITY);
100+
assert_eq!(ldexp(1.42, -0xFFFF), 0f64);
95101
}

0 commit comments

Comments
 (0)