Add math functions for f16 and f128

This adds missing functions for math operations on the new float types.

Platform support is pretty spotty at this point, since even platforms
with generally good support can be missing math functions.
`std/build.rs` is updated to reflect this.
This commit is contained in:
Trevor Gross 2024-06-27 04:13:25 -05:00
parent 82b40c4d8e
commit fc43c01417
7 changed files with 3450 additions and 90 deletions

View File

@ -85,6 +85,11 @@ fn main() {
println!("cargo:rustc-check-cfg=cfg(reliable_f16)");
println!("cargo:rustc-check-cfg=cfg(reliable_f128)");
// This is a step beyond only having the types and basic functions available. Math functions
// aren't consistently available or correct.
println!("cargo:rustc-check-cfg=cfg(reliable_f16_math)");
println!("cargo:rustc-check-cfg=cfg(reliable_f128_math)");
let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) {
// Selection failure until recent LLVM <https://github.com/llvm/llvm-project/issues/93894>
// FIXME(llvm19): can probably be removed at the version bump
@ -130,10 +135,42 @@ fn main() {
_ => false,
};
// These are currently empty, but will fill up as some platforms move from completely
// unreliable to reliable basics but unreliable math.
// LLVM is currenlty adding missing routines, <https://github.com/llvm/llvm-project/issues/93566>
let has_reliable_f16_math = has_reliable_f16
&& match (target_arch.as_str(), target_os.as_str()) {
// Currently nothing special. Hooray!
// This will change as platforms gain better better support for standard ops but math
// lags behind.
_ => true,
};
let has_reliable_f128_math = has_reliable_f128
&& match (target_arch.as_str(), target_os.as_str()) {
// LLVM lowers `fp128` math to `long double` symbols even on platforms where
// `long double` is not IEEE binary128. See
// <https://github.com/llvm/llvm-project/issues/44744>.
//
// This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits
// (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86`
// (ld is 80-bit extended precision).
("x86_64", _) => false,
(_, "linux") if target_pointer_width == 64 => true,
_ => false,
};
if has_reliable_f16 {
println!("cargo:rustc-cfg=reliable_f16");
}
if has_reliable_f128 {
println!("cargo:rustc-cfg=reliable_f128");
}
if has_reliable_f16_math {
println!("cargo:rustc-cfg=reliable_f16_math");
}
if has_reliable_f128_math {
println!("cargo:rustc-cfg=reliable_f128_math");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,21 @@
use crate::f128::consts;
use crate::num::{FpCategory as Fp, *};
// Note these tolerances make sense around zero, but not for more extreme exponents.
/// For operations that are near exact, usually not involving math of different
/// signs.
const TOL_PRECISE: f128 = 1e-28;
/// Default tolerances. Works for values that should be near precise but not exact. Roughly
/// the precision carried by `100 * 100`.
const TOL: f128 = 1e-12;
/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained
/// operations.
#[cfg(reliable_f128_math)]
const TOL_IMPR: f128 = 1e-10;
/// Smallest number
const TINY_BITS: u128 = 0x1;
@ -191,9 +206,100 @@ fn test_classify() {
assert_eq!(1e-4932f128.classify(), Fp::Subnormal);
}
// FIXME(f16_f128): add missing math functions when available
#[test]
#[cfg(reliable_f128_math)]
fn test_floor() {
assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE);
assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE);
assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE);
assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE);
assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_ceil() {
assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE);
assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE);
assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_round() {
assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE);
assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE);
assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE);
assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE);
assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_round_ties_even() {
assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE);
assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE);
assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE);
assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE);
assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_trunc() {
assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE);
assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE);
assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE);
assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE);
assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_fract() {
assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE);
assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE);
assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE);
assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE);
assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE);
assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE);
assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE);
assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE);
assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE);
assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_abs() {
assert_eq!(f128::INFINITY.abs(), f128::INFINITY);
assert_eq!(1f128.abs(), 1f128);
@ -293,6 +399,24 @@ fn test_next_down() {
}
#[test]
#[cfg(reliable_f128_math)]
fn test_mul_add() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE);
assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE);
assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE);
assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_eq!(inf.mul_add(7.8, 9.0), inf);
assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_eq!(8.9f128.mul_add(inf, 3.2), inf);
assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_recip() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
@ -301,11 +425,161 @@ fn test_recip() {
assert_eq!(2.0f128.recip(), 0.5);
assert_eq!((-0.4f128).recip(), -2.5);
assert_eq!(0.0f128.recip(), inf);
assert_approx_eq!(
f128::MAX.recip(),
8.40525785778023376565669454330438228902076605e-4933,
1e-4900
);
assert!(nan.recip().is_nan());
assert_eq!(inf.recip(), 0.0);
assert_eq!(neg_inf.recip(), 0.0);
}
// Many math functions allow for less accurate results, so the next tolerance up is used
#[test]
#[cfg(reliable_f128_math)]
fn test_powi() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_eq!(1.0f128.powi(1), 1.0);
assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL);
assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL);
assert_eq!(8.3f128.powi(0), 1.0);
assert!(nan.powi(2).is_nan());
assert_eq!(inf.powi(3), inf);
assert_eq!(neg_inf.powi(2), inf);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_powf() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_eq!(1.0f128.powf(1.0), 1.0);
assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR);
assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR);
assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR);
assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR);
assert_eq!(8.3f128.powf(0.0), 1.0);
assert!(nan.powf(2.0).is_nan());
assert_eq!(inf.powf(2.0), inf);
assert_eq!(neg_inf.powf(3.0), neg_inf);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_sqrt_domain() {
assert!(f128::NAN.sqrt().is_nan());
assert!(f128::NEG_INFINITY.sqrt().is_nan());
assert!((-1.0f128).sqrt().is_nan());
assert_eq!((-0.0f128).sqrt(), -0.0);
assert_eq!(0.0f128.sqrt(), 0.0);
assert_eq!(1.0f128.sqrt(), 1.0);
assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_exp() {
assert_eq!(1.0, 0.0f128.exp());
assert_approx_eq!(consts::E, 1.0f128.exp(), TOL);
assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL);
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
let nan: f128 = f128::NAN;
assert_eq!(inf, inf.exp());
assert_eq!(0.0, neg_inf.exp());
assert!(nan.exp().is_nan());
}
#[test]
#[cfg(reliable_f128_math)]
fn test_exp2() {
assert_eq!(32.0, 5.0f128.exp2());
assert_eq!(1.0, 0.0f128.exp2());
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
let nan: f128 = f128::NAN;
assert_eq!(inf, inf.exp2());
assert_eq!(0.0, neg_inf.exp2());
assert!(nan.exp2().is_nan());
}
#[test]
#[cfg(reliable_f128_math)]
fn test_ln() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL);
assert!(nan.ln().is_nan());
assert_eq!(inf.ln(), inf);
assert!(neg_inf.ln().is_nan());
assert!((-2.3f128).ln().is_nan());
assert_eq!((-0.0f128).ln(), neg_inf);
assert_eq!(0.0f128.ln(), neg_inf);
assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_log() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_eq!(10.0f128.log(10.0), 1.0);
assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL);
assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0);
assert!(1.0f128.log(1.0).is_nan());
assert!(1.0f128.log(-13.9).is_nan());
assert!(nan.log(2.3).is_nan());
assert_eq!(inf.log(10.0), inf);
assert!(neg_inf.log(8.8).is_nan());
assert!((-2.3f128).log(0.1).is_nan());
assert_eq!((-0.0f128).log(2.0), neg_inf);
assert_eq!(0.0f128.log(7.0), neg_inf);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_log2() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL);
assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL);
assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL);
assert!(nan.log2().is_nan());
assert_eq!(inf.log2(), inf);
assert!(neg_inf.log2().is_nan());
assert!((-2.3f128).log2().is_nan());
assert_eq!((-0.0f128).log2(), neg_inf);
assert_eq!(0.0f128.log2(), neg_inf);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_log10() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_eq!(10.0f128.log10(), 1.0);
assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL);
assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL);
assert_eq!(1.0f128.log10(), 0.0);
assert!(nan.log10().is_nan());
assert_eq!(inf.log10(), inf);
assert!(neg_inf.log10().is_nan());
assert!((-2.3f128).log10().is_nan());
assert_eq!((-0.0f128).log10(), neg_inf);
assert_eq!(0.0f128.log10(), neg_inf);
}
#[test]
fn test_to_degrees() {
let pi: f128 = consts::PI;
@ -313,8 +587,8 @@ fn test_to_degrees() {
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_eq!(0.0f128.to_degrees(), 0.0);
assert_approx_eq!((-5.8f128).to_degrees(), -332.315521);
assert_eq!(pi.to_degrees(), 180.0);
assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL);
assert_approx_eq!(pi.to_degrees(), 180.0, TOL);
assert!(nan.to_degrees().is_nan());
assert_eq!(inf.to_degrees(), inf);
assert_eq!(neg_inf.to_degrees(), neg_inf);
@ -328,19 +602,122 @@ fn test_to_radians() {
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_eq!(0.0f128.to_radians(), 0.0);
assert_approx_eq!(154.6f128.to_radians(), 2.698279);
assert_approx_eq!((-332.31f128).to_radians(), -5.799903);
assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL);
assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL);
// check approx rather than exact because round trip for pi doesn't fall on an exactly
// representable value (unlike `f32` and `f64`).
assert_approx_eq!(180.0f128.to_radians(), pi);
assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE);
assert!(nan.to_radians().is_nan());
assert_eq!(inf.to_radians(), inf);
assert_eq!(neg_inf.to_radians(), neg_inf);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_asinh() {
// Lower accuracy results are allowed, use increased tolerances
assert_eq!(0.0f128.asinh(), 0.0f128);
assert_eq!((-0.0f128).asinh(), -0.0f128);
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
let nan: f128 = f128::NAN;
assert_eq!(inf.asinh(), inf);
assert_eq!(neg_inf.asinh(), neg_inf);
assert!(nan.asinh().is_nan());
assert!((-0.0f128).asinh().is_sign_negative());
// issue 63271
assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR);
assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR);
// regression test for the catastrophic cancellation fixed in 72486
assert_approx_eq!(
(-67452098.07139316f128).asinh(),
-18.720075426274544393985484294000831757220,
TOL_IMPR
);
// test for low accuracy from issue 104548
assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR);
// mul needed for approximate comparison to be meaningful
assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_acosh() {
assert_eq!(1.0f128.acosh(), 0.0f128);
assert!(0.999f128.acosh().is_nan());
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
let nan: f128 = f128::NAN;
assert_eq!(inf.acosh(), inf);
assert!(neg_inf.acosh().is_nan());
assert!(nan.acosh().is_nan());
assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR);
assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR);
// test for low accuracy from issue 104548
assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_atanh() {
assert_eq!(0.0f128.atanh(), 0.0f128);
assert_eq!((-0.0f128).atanh(), -0.0f128);
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
let nan: f128 = f128::NAN;
assert_eq!(1.0f128.atanh(), inf);
assert_eq!((-1.0f128).atanh(), neg_inf);
assert!(2f128.atanh().atanh().is_nan());
assert!((-2f128).atanh().atanh().is_nan());
assert!(inf.atanh().is_nan());
assert!(neg_inf.atanh().is_nan());
assert!(nan.atanh().is_nan());
assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR);
assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_gamma() {
// precision can differ among platforms
assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR);
assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR);
assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR);
assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR);
assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR);
assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR);
assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR);
assert_eq!(0.0f128.gamma(), f128::INFINITY);
assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY);
assert!((-1.0f128).gamma().is_nan());
assert!((-2.0f128).gamma().is_nan());
assert!(f128::NAN.gamma().is_nan());
assert!(f128::NEG_INFINITY.gamma().is_nan());
assert_eq!(f128::INFINITY.gamma(), f128::INFINITY);
assert_eq!(1760.9f128.gamma(), f128::INFINITY);
}
#[test]
#[cfg(reliable_f128_math)]
fn test_ln_gamma() {
assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR);
assert_eq!(1.0f128.ln_gamma().1, 1);
assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR);
assert_eq!(2.0f128.ln_gamma().1, 1);
assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR);
assert_eq!(3.0f128.ln_gamma().1, 1);
assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR);
assert_eq!((-0.5f128).ln_gamma().1, -1);
}
#[test]
fn test_real_consts() {
// FIXME(f16_f128): add math tests when available
use super::consts;
let pi: f128 = consts::PI;
@ -351,29 +728,34 @@ fn test_real_consts() {
let frac_pi_8: f128 = consts::FRAC_PI_8;
let frac_1_pi: f128 = consts::FRAC_1_PI;
let frac_2_pi: f128 = consts::FRAC_2_PI;
// let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI;
// let sqrt2: f128 = consts::SQRT_2;
// let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2;
// let e: f128 = consts::E;
// let log2_e: f128 = consts::LOG2_E;
// let log10_e: f128 = consts::LOG10_E;
// let ln_2: f128 = consts::LN_2;
// let ln_10: f128 = consts::LN_10;
assert_approx_eq!(frac_pi_2, pi / 2f128);
assert_approx_eq!(frac_pi_3, pi / 3f128);
assert_approx_eq!(frac_pi_4, pi / 4f128);
assert_approx_eq!(frac_pi_6, pi / 6f128);
assert_approx_eq!(frac_pi_8, pi / 8f128);
assert_approx_eq!(frac_1_pi, 1f128 / pi);
assert_approx_eq!(frac_2_pi, 2f128 / pi);
// assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt());
// assert_approx_eq!(sqrt2, 2f128.sqrt());
// assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt());
// assert_approx_eq!(log2_e, e.log2());
// assert_approx_eq!(log10_e, e.log10());
// assert_approx_eq!(ln_2, 2f128.ln());
// assert_approx_eq!(ln_10, 10f128.ln());
assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE);
assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE);
assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE);
assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE);
assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE);
assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE);
assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE);
#[cfg(reliable_f128_math)]
{
let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI;
let sqrt2: f128 = consts::SQRT_2;
let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2;
let e: f128 = consts::E;
let log2_e: f128 = consts::LOG2_E;
let log10_e: f128 = consts::LOG10_E;
let ln_2: f128 = consts::LN_2;
let ln_10: f128 = consts::LN_10;
assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE);
assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE);
assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE);
assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE);
assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE);
assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE);
assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE);
}
}
#[test]
@ -382,10 +764,10 @@ fn test_float_bits_conv() {
assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000);
assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000);
assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000);
assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0);
assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5);
assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0);
assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25);
assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE);
assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE);
assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE);
assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE);
// Check that NaNs roundtrip their bits regardless of signaling-ness
// 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,21 @@
use crate::f16::consts;
use crate::num::{FpCategory as Fp, *};
// We run out of precision pretty quickly with f16
// const F16_APPROX_L1: f16 = 0.001;
const F16_APPROX_L2: f16 = 0.01;
// const F16_APPROX_L3: f16 = 0.1;
const F16_APPROX_L4: f16 = 0.5;
/// Tolerance for results on the order of 10.0e-2;
#[cfg(reliable_f16_math)]
const TOL_N2: f16 = 0.0001;
/// Tolerance for results on the order of 10.0e+0
#[cfg(reliable_f16_math)]
const TOL_0: f16 = 0.01;
/// Tolerance for results on the order of 10.0e+2
#[cfg(reliable_f16_math)]
const TOL_P2: f16 = 0.5;
/// Tolerance for results on the order of 10.0e+4
#[cfg(reliable_f16_math)]
const TOL_P4: f16 = 10.0;
/// Smallest number
const TINY_BITS: u16 = 0x1;
@ -197,9 +207,100 @@ fn test_classify() {
assert_eq!(1e-5f16.classify(), Fp::Subnormal);
}
// FIXME(f16_f128): add missing math functions when available
#[test]
#[cfg(reliable_f16_math)]
fn test_floor() {
assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0);
assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0);
assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0);
assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0);
assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0);
assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0);
assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0);
assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0);
assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0);
assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_ceil() {
assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0);
assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0);
assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0);
assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0);
assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0);
assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0);
assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0);
assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0);
assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0);
assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_round() {
assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0);
assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0);
assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0);
assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0);
assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0);
assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0);
assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0);
assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0);
assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0);
assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0);
assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_round_ties_even() {
assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0);
assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0);
assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0);
assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0);
assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0);
assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0);
assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0);
assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0);
assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0);
assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0);
assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_trunc() {
assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0);
assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0);
assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0);
assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0);
assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0);
assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0);
assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0);
assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0);
assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0);
assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_fract() {
assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0);
assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0);
assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0);
assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0);
assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0);
assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0);
assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0);
assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0);
assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0);
assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_abs() {
assert_eq!(f16::INFINITY.abs(), f16::INFINITY);
assert_eq!(1f16.abs(), 1f16);
@ -299,6 +400,24 @@ fn test_next_down() {
}
#[test]
#[cfg(reliable_f16_math)]
fn test_mul_add() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2);
assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2);
assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0);
assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_eq!(inf.mul_add(7.8, 9.0), inf);
assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_eq!(8.9f16.mul_add(inf, 3.2), inf);
assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_recip() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
@ -307,11 +426,157 @@ fn test_recip() {
assert_eq!(2.0f16.recip(), 0.5);
assert_eq!((-0.4f16).recip(), -2.5);
assert_eq!(0.0f16.recip(), inf);
assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4);
assert!(nan.recip().is_nan());
assert_eq!(inf.recip(), 0.0);
assert_eq!(neg_inf.recip(), 0.0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_powi() {
// FIXME(llvm19): LLVM misoptimizes `powi.f16`
// <https://github.com/llvm/llvm-project/issues/98665>
// let nan: f16 = f16::NAN;
// let inf: f16 = f16::INFINITY;
// let neg_inf: f16 = f16::NEG_INFINITY;
// assert_eq!(1.0f16.powi(1), 1.0);
// assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0);
// assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2);
// assert_eq!(8.3f16.powi(0), 1.0);
// assert!(nan.powi(2).is_nan());
// assert_eq!(inf.powi(3), inf);
// assert_eq!(neg_inf.powi(2), inf);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_powf() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_eq!(1.0f16.powf(1.0), 1.0);
assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2);
assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2);
assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2);
assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2);
assert_eq!(8.3f16.powf(0.0), 1.0);
assert!(nan.powf(2.0).is_nan());
assert_eq!(inf.powf(2.0), inf);
assert_eq!(neg_inf.powf(3.0), neg_inf);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_sqrt_domain() {
assert!(f16::NAN.sqrt().is_nan());
assert!(f16::NEG_INFINITY.sqrt().is_nan());
assert!((-1.0f16).sqrt().is_nan());
assert_eq!((-0.0f16).sqrt(), -0.0);
assert_eq!(0.0f16.sqrt(), 0.0);
assert_eq!(1.0f16.sqrt(), 1.0);
assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_exp() {
assert_eq!(1.0, 0.0f16.exp());
assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0);
assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0);
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
let nan: f16 = f16::NAN;
assert_eq!(inf, inf.exp());
assert_eq!(0.0, neg_inf.exp());
assert!(nan.exp().is_nan());
}
#[test]
#[cfg(reliable_f16_math)]
fn test_exp2() {
assert_eq!(32.0, 5.0f16.exp2());
assert_eq!(1.0, 0.0f16.exp2());
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
let nan: f16 = f16::NAN;
assert_eq!(inf, inf.exp2());
assert_eq!(0.0, neg_inf.exp2());
assert!(nan.exp2().is_nan());
}
#[test]
#[cfg(reliable_f16_math)]
fn test_ln() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0);
assert!(nan.ln().is_nan());
assert_eq!(inf.ln(), inf);
assert!(neg_inf.ln().is_nan());
assert!((-2.3f16).ln().is_nan());
assert_eq!((-0.0f16).ln(), neg_inf);
assert_eq!(0.0f16.ln(), neg_inf);
assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_log() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_eq!(10.0f16.log(10.0), 1.0);
assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0);
assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0);
assert!(1.0f16.log(1.0).is_nan());
assert!(1.0f16.log(-13.9).is_nan());
assert!(nan.log(2.3).is_nan());
assert_eq!(inf.log(10.0), inf);
assert!(neg_inf.log(8.8).is_nan());
assert!((-2.3f16).log(0.1).is_nan());
assert_eq!((-0.0f16).log(2.0), neg_inf);
assert_eq!(0.0f16.log(7.0), neg_inf);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_log2() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0);
assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0);
assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0);
assert!(nan.log2().is_nan());
assert_eq!(inf.log2(), inf);
assert!(neg_inf.log2().is_nan());
assert!((-2.3f16).log2().is_nan());
assert_eq!((-0.0f16).log2(), neg_inf);
assert_eq!(0.0f16.log2(), neg_inf);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_log10() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_eq!(10.0f16.log10(), 1.0);
assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0);
assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0);
assert_eq!(1.0f16.log10(), 0.0);
assert!(nan.log10().is_nan());
assert_eq!(inf.log10(), inf);
assert!(neg_inf.log10().is_nan());
assert!((-2.3f16).log10().is_nan());
assert_eq!((-0.0f16).log10(), neg_inf);
assert_eq!(0.0f16.log10(), neg_inf);
}
#[test]
fn test_to_degrees() {
let pi: f16 = consts::PI;
@ -319,8 +584,8 @@ fn test_to_degrees() {
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_eq!(0.0f16.to_degrees(), 0.0);
assert_approx_eq!((-5.8f16).to_degrees(), -332.315521);
assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4);
assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2);
assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2);
assert!(nan.to_degrees().is_nan());
assert_eq!(inf.to_degrees(), inf);
assert_eq!(neg_inf.to_degrees(), neg_inf);
@ -334,14 +599,112 @@ fn test_to_radians() {
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_eq!(0.0f16.to_radians(), 0.0);
assert_approx_eq!(154.6f16.to_radians(), 2.698279);
assert_approx_eq!((-332.31f16).to_radians(), -5.799903);
assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2);
assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0);
assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0);
assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0);
assert!(nan.to_radians().is_nan());
assert_eq!(inf.to_radians(), inf);
assert_eq!(neg_inf.to_radians(), neg_inf);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_asinh() {
assert_eq!(0.0f16.asinh(), 0.0f16);
assert_eq!((-0.0f16).asinh(), -0.0f16);
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
let nan: f16 = f16::NAN;
assert_eq!(inf.asinh(), inf);
assert_eq!(neg_inf.asinh(), neg_inf);
assert!(nan.asinh().is_nan());
assert!((-0.0f16).asinh().is_sign_negative());
// issue 63271
assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0);
assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0);
// regression test for the catastrophic cancellation fixed in 72486
assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0);
// test for low accuracy from issue 104548
assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0);
// mul needed for approximate comparison to be meaningful
assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_acosh() {
assert_eq!(1.0f16.acosh(), 0.0f16);
assert!(0.999f16.acosh().is_nan());
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
let nan: f16 = f16::NAN;
assert_eq!(inf.acosh(), inf);
assert!(neg_inf.acosh().is_nan());
assert!(nan.acosh().is_nan());
assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0);
assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0);
// test for low accuracy from issue 104548
assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_atanh() {
assert_eq!(0.0f16.atanh(), 0.0f16);
assert_eq!((-0.0f16).atanh(), -0.0f16);
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
let nan: f16 = f16::NAN;
assert_eq!(1.0f16.atanh(), inf);
assert_eq!((-1.0f16).atanh(), neg_inf);
assert!(2f16.atanh().atanh().is_nan());
assert!((-2f16).atanh().atanh().is_nan());
assert!(inf.atanh().is_nan());
assert!(neg_inf.atanh().is_nan());
assert!(nan.atanh().is_nan());
assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0);
assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_gamma() {
// precision can differ among platforms
assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0);
assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0);
assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0);
assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0);
assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0);
assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0);
assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0);
assert_eq!(0.0f16.gamma(), f16::INFINITY);
assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY);
assert!((-1.0f16).gamma().is_nan());
assert!((-2.0f16).gamma().is_nan());
assert!(f16::NAN.gamma().is_nan());
assert!(f16::NEG_INFINITY.gamma().is_nan());
assert_eq!(f16::INFINITY.gamma(), f16::INFINITY);
assert_eq!(171.71f16.gamma(), f16::INFINITY);
}
#[test]
#[cfg(reliable_f16_math)]
fn test_ln_gamma() {
assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0);
assert_eq!(1.0f16.ln_gamma().1, 1);
assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0);
assert_eq!(2.0f16.ln_gamma().1, 1);
assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0);
assert_eq!(3.0f16.ln_gamma().1, 1);
assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0);
assert_eq!((-0.5f16).ln_gamma().1, -1);
}
#[test]
fn test_real_consts() {
// FIXME(f16_f128): add math tests when available
@ -355,29 +718,34 @@ fn test_real_consts() {
let frac_pi_8: f16 = consts::FRAC_PI_8;
let frac_1_pi: f16 = consts::FRAC_1_PI;
let frac_2_pi: f16 = consts::FRAC_2_PI;
// let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI;
// let sqrt2: f16 = consts::SQRT_2;
// let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2;
// let e: f16 = consts::E;
// let log2_e: f16 = consts::LOG2_E;
// let log10_e: f16 = consts::LOG10_E;
// let ln_2: f16 = consts::LN_2;
// let ln_10: f16 = consts::LN_10;
assert_approx_eq!(frac_pi_2, pi / 2f16);
assert_approx_eq!(frac_pi_3, pi / 3f16);
assert_approx_eq!(frac_pi_4, pi / 4f16);
assert_approx_eq!(frac_pi_6, pi / 6f16);
assert_approx_eq!(frac_pi_8, pi / 8f16);
assert_approx_eq!(frac_1_pi, 1f16 / pi);
assert_approx_eq!(frac_2_pi, 2f16 / pi);
// assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt());
// assert_approx_eq!(sqrt2, 2f16.sqrt());
// assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt());
// assert_approx_eq!(log2_e, e.log2());
// assert_approx_eq!(log10_e, e.log10());
// assert_approx_eq!(ln_2, 2f16.ln());
// assert_approx_eq!(ln_10, 10f16.ln());
assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0);
assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0);
assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0);
assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0);
assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0);
assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0);
assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0);
#[cfg(reliable_f16_math)]
{
let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI;
let sqrt2: f16 = consts::SQRT_2;
let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2;
let e: f16 = consts::E;
let log2_e: f16 = consts::LOG2_E;
let log10_e: f16 = consts::LOG10_E;
let ln_2: f16 = consts::LN_2;
let ln_10: f16 = consts::LN_10;
assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0);
assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0);
assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0);
assert_approx_eq!(log2_e, e.log2(), TOL_0);
assert_approx_eq!(log10_e, e.log10(), TOL_0);
assert_approx_eq!(ln_2, 2f16.ln(), TOL_0);
assert_approx_eq!(ln_10, 10f16.ln(), TOL_0);
}
}
#[test]
@ -386,10 +754,10 @@ fn test_float_bits_conv() {
assert_eq!((12.5f16).to_bits(), 0x4a40);
assert_eq!((1337f16).to_bits(), 0x6539);
assert_eq!((-14.25f16).to_bits(), 0xcb20);
assert_approx_eq!(f16::from_bits(0x3c00), 1.0);
assert_approx_eq!(f16::from_bits(0x4a40), 12.5);
assert_approx_eq!(f16::from_bits(0x6539), 1337.0);
assert_approx_eq!(f16::from_bits(0xcb20), -14.25);
assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0);
assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0);
assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4);
assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0);
// Check that NaNs roundtrip their bits regardless of signaling-ness
let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1;

View File

@ -382,7 +382,7 @@ macro_rules! assert_approx_eq {
let diff = (*a - *b).abs();
assert!(
diff < $lim,
"{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})",
"{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})",
lim = $lim
);
}};

View File

@ -28,6 +28,21 @@ extern "C" {
pub fn lgamma_r(n: f64, s: &mut i32) -> f64;
pub fn lgammaf_r(n: f32, s: &mut i32) -> f32;
pub fn acosf128(n: f128) -> f128;
pub fn asinf128(n: f128) -> f128;
pub fn atanf128(n: f128) -> f128;
pub fn atan2f128(a: f128, b: f128) -> f128;
pub fn cbrtf128(n: f128) -> f128;
pub fn coshf128(n: f128) -> f128;
pub fn expm1f128(n: f128) -> f128;
pub fn hypotf128(x: f128, y: f128) -> f128;
pub fn log1pf128(n: f128) -> f128;
pub fn sinhf128(n: f128) -> f128;
pub fn tanf128(n: f128) -> f128;
pub fn tanhf128(n: f128) -> f128;
pub fn tgammaf128(n: f128) -> f128;
pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128;
cfg_if::cfg_if! {
if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] {
pub fn acosf(n: f32) -> f32;