Rollup merge of #144247 - RalfJung:ldexp, r=tgross35

coretests/num: use ldexp instead of hard-coding a power of 2

r? `````@tgross35`````
This commit is contained in:
León Orell Valerian Liehr
2025-07-24 15:08:24 +02:00
committed by GitHub
4 changed files with 38 additions and 32 deletions

View File

@@ -1,13 +1,14 @@
use core::num::dec2flt::float::RawFloat;
use crate::num::{ldexp_f32, ldexp_f64};
// FIXME(f16_f128): enable on all targets once possible.
#[test]
#[cfg(target_has_reliable_f16)]
fn test_f16_integer_decode() {
assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1));
assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1));
#[cfg(not(miri))] // miri doesn't have powf16
assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1));
assert_eq!(crate::num::ldexp_f16(1.0, 14).integer_decode(), (1 << 10, 4, 1));
assert_eq!(0f16.integer_decode(), (0, -25, 1));
assert_eq!((-0f16).integer_decode(), (0, -25, -1));
assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1));
@@ -23,8 +24,7 @@ fn test_f16_integer_decode() {
fn test_f32_integer_decode() {
assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
assert_eq!((-8573.5918555f32).integer_decode(), (8779358, -10, -1));
// Set 2^100 directly instead of using powf, because it doesn't guarantee precision
assert_eq!(1.2676506e30_f32.integer_decode(), (8388608, 77, 1));
assert_eq!(ldexp_f32(1.0, 100).integer_decode(), (8388608, 77, 1));
assert_eq!(0f32.integer_decode(), (0, -150, 1));
assert_eq!((-0f32).integer_decode(), (0, -150, -1));
assert_eq!(f32::INFINITY.integer_decode(), (8388608, 105, 1));
@@ -40,8 +40,7 @@ fn test_f32_integer_decode() {
fn test_f64_integer_decode() {
assert_eq!(3.14159265359f64.integer_decode(), (7074237752028906, -51, 1));
assert_eq!((-8573.5918555f64).integer_decode(), (4713381968463931, -39, -1));
// Set 2^100 directly instead of using powf, because it doesn't guarantee precision
assert_eq!(1.2676506002282294e30_f64.integer_decode(), (4503599627370496, 48, 1));
assert_eq!(ldexp_f64(1.0, 100).integer_decode(), (4503599627370496, 48, 1));
assert_eq!(0f64.integer_decode(), (0, -1075, 1));
assert_eq!((-0f64).integer_decode(), (0, -1075, -1));
assert_eq!(f64::INFINITY.integer_decode(), (4503599627370496, 972, 1));

View File

@@ -1,5 +1,7 @@
use core::num::flt2dec::estimator::*;
use crate::num::ldexp_f64;
#[test]
fn test_estimate_scaling_factor() {
macro_rules! assert_almost_eq {
@@ -56,7 +58,7 @@ fn test_estimate_scaling_factor() {
let step = if cfg!(miri) { 37 } else { 1 };
for i in (-1074..972).step_by(step) {
let expected = super::ldexp_f64(1.0, i).log10().ceil();
let expected = ldexp_f64(1.0, i).log10().ceil();
assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16);
}
}

View File

@@ -6,6 +6,8 @@ use core::num::fmt::{Formatted, Part};
use std::mem::MaybeUninit;
use std::{fmt, str};
use crate::num::{ldexp_f32, ldexp_f64};
mod estimator;
mod strategy {
mod dragon;
@@ -75,24 +77,6 @@ macro_rules! try_fixed {
})
}
#[cfg(target_has_reliable_f16)]
fn ldexp_f16(a: f16, b: i32) -> f16 {
ldexp_f64(a as f64, b) as f16
}
fn ldexp_f32(a: f32, b: i32) -> f32 {
ldexp_f64(a as f64, b) as f32
}
fn ldexp_f64(a: f64, b: i32) -> f64 {
unsafe extern "C" {
fn ldexp(x: f64, n: i32) -> f64;
}
// SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly
// cause undefined behavior
unsafe { ldexp(a, b) }
}
fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16)
where
T: DecodableFloat,
@@ -268,7 +252,7 @@ where
// 10^2 * 0.31984375
// 10^2 * 0.32
// 10^2 * 0.3203125
check_shortest!(f(ldexp_f16(1.0, 5)) => b"32", 2);
check_shortest!(f(crate::num::ldexp_f16(1.0, 5)) => b"32", 2);
// 10^5 * 0.65472
// 10^5 * 0.65504
@@ -283,7 +267,7 @@ where
// 10^-9 * 0
// 10^-9 * 0.59604644775390625
// 10^-8 * 0.11920928955078125
let minf16 = ldexp_f16(1.0, -24);
let minf16 = crate::num::ldexp_f16(1.0, -24);
check_shortest!(f(minf16) => b"6", -7);
}
@@ -292,7 +276,7 @@ pub fn f16_exact_sanity_test<F>(mut f: F)
where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
{
let minf16 = ldexp_f16(1.0, -24);
let minf16 = crate::num::ldexp_f16(1.0, -24);
check_exact!(f(0.1f16) => b"999755859375 ", -1);
check_exact!(f(0.5f16) => b"5 ", 0);
@@ -642,7 +626,7 @@ where
assert_eq!(to_string(f, f16::MAX, Minus, 1), "65500.0");
assert_eq!(to_string(f, f16::MAX, Minus, 8), "65500.00000000");
let minf16 = ldexp_f16(1.0, -24);
let minf16 = crate::num::ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, 0), "0.00000006");
assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006");
assert_eq!(to_string(f, minf16, Minus, 9), "0.000000060");
@@ -766,7 +750,7 @@ where
assert_eq!(to_string(f, f16::MAX, Minus, (-4, 4), false), "6.55e4");
assert_eq!(to_string(f, f16::MAX, Minus, (-5, 5), false), "65500");
let minf16 = ldexp_f16(1.0, -24);
let minf16 = crate::num::ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, (-2, 2), false), "6e-8");
assert_eq!(to_string(f, minf16, Minus, (-7, 7), false), "6e-8");
assert_eq!(to_string(f, minf16, Minus, (-8, 8), false), "0.00000006");
@@ -922,7 +906,7 @@ where
assert_eq!(to_string(f, f16::MAX, Minus, 6, false), "6.55040e4");
assert_eq!(to_string(f, f16::MAX, Minus, 16, false), "6.550400000000000e4");
let minf16 = ldexp_f16(1.0, -24);
let minf16 = crate::num::ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, 1, false), "6e-8");
assert_eq!(to_string(f, minf16, Minus, 2, false), "6.0e-8");
assert_eq!(to_string(f, minf16, Minus, 4, false), "5.960e-8");
@@ -1229,7 +1213,7 @@ where
#[cfg(target_has_reliable_f16)]
{
let minf16 = ldexp_f16(1.0, -24);
let minf16 = crate::num::ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, 0), "0");
assert_eq!(to_string(f, minf16, Minus, 1), "0.0");
assert_eq!(to_string(f, minf16, Minus, 2), "0.00");

View File

@@ -54,6 +54,27 @@ macro_rules! assume_usize_width {
}
}
/// Return `a * 2^b`.
#[cfg(target_has_reliable_f16)]
fn ldexp_f16(a: f16, b: i32) -> f16 {
ldexp_f64(a as f64, b) as f16
}
/// Return `a * 2^b`.
fn ldexp_f32(a: f32, b: i32) -> f32 {
ldexp_f64(a as f64, b) as f32
}
/// Return `a * 2^b`.
fn ldexp_f64(a: f64, b: i32) -> f64 {
unsafe extern "C" {
fn ldexp(x: f64, n: i32) -> f64;
}
// SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly
// cause undefined behavior
unsafe { ldexp(a, b) }
}
/// Helper function for testing numeric operations
pub fn test_num<T>(ten: T, two: T)
where