Add fminimum, fmaximum, fminimum_num, and fmaximum_num

These functions represent new operations from IEEE 754-2019. Introduce
them for all float sizes.
This commit is contained in:
Trevor Gross 2025-02-10 19:43:49 +00:00 committed by Trevor Gross
parent 86ee1f99c9
commit 669731335e
23 changed files with 997 additions and 31 deletions

View file

@ -47,7 +47,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
FloatTy::F16,
Signature { args: &[Ty::F16, Ty::F16], returns: &[Ty::F16] },
None,
&["copysignf16", "fdimf16", "fmaxf16", "fminf16", "fmodf16"],
&[
"copysignf16",
"fdimf16",
"fmaxf16",
"fmaximum_numf16",
"fmaximumf16",
"fminf16",
"fminimum_numf16",
"fminimumf16",
"fmodf16",
],
),
(
// `(f32, f32) -> f32`
@ -59,7 +69,11 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"copysignf",
"fdimf",
"fmaxf",
"fmaximum_numf",
"fmaximumf",
"fminf",
"fminimum_numf",
"fminimumf",
"fmodf",
"hypotf",
"nextafterf",
@ -77,7 +91,11 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"copysign",
"fdim",
"fmax",
"fmaximum",
"fmaximum_num",
"fmin",
"fminimum",
"fminimum_num",
"fmod",
"hypot",
"nextafter",
@ -90,7 +108,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
FloatTy::F128,
Signature { args: &[Ty::F128, Ty::F128], returns: &[Ty::F128] },
None,
&["copysignf128", "fdimf128", "fmaxf128", "fminf128", "fmodf128"],
&[
"copysignf128",
"fdimf128",
"fmaxf128",
"fmaximum_numf128",
"fmaximumf128",
"fminf128",
"fminimum_numf128",
"fminimumf128",
"fmodf128",
],
),
(
// `(f32, f32, f32) -> f32`

View file

@ -207,10 +207,26 @@ main!(
icount_bench_fmaxf128_group,
icount_bench_fmaxf16_group,
icount_bench_fmaxf_group,
icount_bench_fmaximum_group,
icount_bench_fmaximum_num_group,
icount_bench_fmaximum_numf128_group,
icount_bench_fmaximum_numf16_group,
icount_bench_fmaximum_numf_group,
icount_bench_fmaximumf128_group,
icount_bench_fmaximumf16_group,
icount_bench_fmaximumf_group,
icount_bench_fmin_group,
icount_bench_fminf128_group,
icount_bench_fminf16_group,
icount_bench_fminf_group,
icount_bench_fminimum_group,
icount_bench_fminimum_num_group,
icount_bench_fminimum_numf128_group,
icount_bench_fminimum_numf16_group,
icount_bench_fminimum_numf_group,
icount_bench_fminimumf128_group,
icount_bench_fminimumf16_group,
icount_bench_fminimumf_group,
icount_bench_fmod_group,
icount_bench_fmodf128_group,
icount_bench_fmodf16_group,

View file

@ -130,8 +130,24 @@ libm_macros::for_each_function! {
| fmaf128
| fmaxf128
| fmaxf16
| fmaximum
| fmaximum_num
| fmaximum_numf
| fmaximum_numf128
| fmaximum_numf16
| fmaximumf
| fmaximumf128
| fmaximumf16
| fminf128
| fminf16
| fminimum
| fminimum_num
| fminimum_numf
| fminimum_numf128
| fminimum_numf16
| fminimumf
| fminimumf128
| fminimumf16
| fmodf128
| fmodf16
| ldexpf128

View file

@ -221,7 +221,11 @@ pub fn get_domain<F: Float, I: Int>(
BaseName::Floor => &EitherPrim::UNBOUNDED1[..],
BaseName::Fma => &EitherPrim::UNBOUNDED3[..],
BaseName::Fmax => &EitherPrim::UNBOUNDED2[..],
BaseName::Fmaximum => &EitherPrim::UNBOUNDED2[..],
BaseName::FmaximumNum => &EitherPrim::UNBOUNDED2[..],
BaseName::Fmin => &EitherPrim::UNBOUNDED2[..],
BaseName::Fminimum => &EitherPrim::UNBOUNDED2[..],
BaseName::FminimumNum => &EitherPrim::UNBOUNDED2[..],
BaseName::Fmod => &EitherPrim::UNBOUNDED2[..],
BaseName::Hypot => &EitherPrim::UNBOUNDED2[..],
BaseName::Ilogb => &EitherPrim::UNBOUNDED1[..],

View file

@ -293,7 +293,8 @@ fn fmaf128_cases() -> Vec<TestCase<op::fmaf128::Routine>> {
v
}
fn fmax_cases() -> Vec<TestCase<op::fmax::Routine>> {
#[cfg(f16_enabled)]
fn fmaxf16_cases() -> Vec<TestCase<op::fmaxf16::Routine>> {
vec![]
}
@ -301,17 +302,53 @@ fn fmaxf_cases() -> Vec<TestCase<op::fmaxf::Routine>> {
vec![]
}
fn fmax_cases() -> Vec<TestCase<op::fmax::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fmaxf128_cases() -> Vec<TestCase<op::fmaxf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fmaxf16_cases() -> Vec<TestCase<op::fmaxf16::Routine>> {
fn fmaximumf16_cases() -> Vec<TestCase<op::fmaximumf16::Routine>> {
vec![]
}
fn fmin_cases() -> Vec<TestCase<op::fmin::Routine>> {
fn fmaximumf_cases() -> Vec<TestCase<op::fmaximumf::Routine>> {
vec![]
}
fn fmaximum_cases() -> Vec<TestCase<op::fmaximum::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fmaximumf128_cases() -> Vec<TestCase<op::fmaximumf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fmaximum_numf16_cases() -> Vec<TestCase<op::fmaximum_numf16::Routine>> {
vec![]
}
fn fmaximum_numf_cases() -> Vec<TestCase<op::fmaximum_numf::Routine>> {
vec![]
}
fn fmaximum_num_cases() -> Vec<TestCase<op::fmaximum_num::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fmaximum_numf128_cases() -> Vec<TestCase<op::fmaximum_numf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fminf16_cases() -> Vec<TestCase<op::fminf16::Routine>> {
vec![]
}
@ -319,13 +356,48 @@ fn fminf_cases() -> Vec<TestCase<op::fminf::Routine>> {
vec![]
}
fn fmin_cases() -> Vec<TestCase<op::fmin::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fminf128_cases() -> Vec<TestCase<op::fminf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fminf16_cases() -> Vec<TestCase<op::fminf16::Routine>> {
fn fminimumf16_cases() -> Vec<TestCase<op::fminimumf16::Routine>> {
vec![]
}
fn fminimumf_cases() -> Vec<TestCase<op::fminimumf::Routine>> {
vec![]
}
fn fminimum_cases() -> Vec<TestCase<op::fminimum::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fminimumf128_cases() -> Vec<TestCase<op::fminimumf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fminimum_numf16_cases() -> Vec<TestCase<op::fminimum_numf16::Routine>> {
vec![]
}
fn fminimum_numf_cases() -> Vec<TestCase<op::fminimum_numf::Routine>> {
vec![]
}
fn fminimum_num_cases() -> Vec<TestCase<op::fminimum_num::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fminimum_numf128_cases() -> Vec<TestCase<op::fminimum_numf128::Routine>> {
vec![]
}

View file

@ -148,6 +148,14 @@ libm_macros::for_each_function! {
floorf,
floorf128,
floorf16,
fmaximum,
fmaximumf,
fmaximumf128,
fmaximumf16,
fminimum,
fminimumf,
fminimumf128,
fminimumf16,
fmod,
fmodf,
fmodf128,
@ -197,8 +205,10 @@ libm_macros::for_each_function! {
fabs | fabsf => abs,
fdim | fdimf | fdimf16 | fdimf128 => positive_diff,
fma | fmaf | fmaf128 => mul_add,
fmax | fmaxf | fmaxf16 | fmaxf128 => max,
fmin | fminf | fminf16 | fminf128 => min,
fmax | fmaxf | fmaxf16 | fmaxf128 |
fmaximum_num | fmaximum_numf | fmaximum_numf16 | fmaximum_numf128 => max,
fmin | fminf | fminf16 | fminf128 |
fminimum_num | fminimum_numf | fminimum_numf16 | fminimum_numf128 => min,
lgamma | lgammaf => ln_gamma,
log | logf => ln,
log1p | log1pf => ln_1p,
@ -446,6 +456,46 @@ macro_rules! impl_op_for_ty_all {
}
}
impl MpOp for crate::op::[< fmaximum $suffix >]::Routine {
type MpTy = (MpFloat, MpFloat);
fn new_mp() -> Self::MpTy {
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
let ord = if this.0.is_nan() || this.1.is_nan() {
this.0.assign($fty::NAN);
Ordering::Equal
} else {
this.0.max_round(&this.1, Nearest)
};
prep_retval::<Self::RustRet>(&mut this.0, ord)
}
}
impl MpOp for crate::op::[< fminimum $suffix >]::Routine {
type MpTy = (MpFloat, MpFloat);
fn new_mp() -> Self::MpTy {
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
let ord = if this.0.is_nan() || this.1.is_nan() {
this.0.assign($fty::NAN);
Ordering::Equal
} else {
this.0.min_round(&this.1, Nearest)
};
prep_retval::<Self::RustRet>(&mut this.0, ord)
}
}
// `ldexp` and `scalbn` are the same for binary floating point, so just forward all
// methods.
impl MpOp for crate::op::[<ldexp $suffix>]::Routine {

View file

@ -25,7 +25,11 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 {
| Bn::Floor
| Bn::Fma
| Bn::Fmax
| Bn::Fmaximum
| Bn::FmaximumNum
| Bn::Fmin
| Bn::Fminimum
| Bn::FminimumNum
| Bn::Fmod
| Bn::Frexp
| Bn::Ilogb

View file

@ -102,8 +102,24 @@ libm_macros::for_each_function! {
fmaf128,
fmaxf128,
fmaxf16,
fmaximum,
fmaximum_num,
fmaximum_numf,
fmaximum_numf128,
fmaximum_numf16,
fmaximumf,
fmaximumf128,
fmaximumf16,
fminf128,
fminf16,
fminimum,
fminimum_num,
fminimum_numf,
fminimum_numf128,
fminimum_numf16,
fminimumf,
fminimumf128,
fminimumf16,
fmodf128,
fmodf16,
ldexpf128,

View file

@ -99,8 +99,24 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
| fmaf128
| fmaxf128
| fmaxf16
| fmaximum
| fmaximum_num
| fmaximum_numf
| fmaximum_numf128
| fmaximum_numf16
| fmaximumf
| fmaximumf128
| fmaximumf16
| fminf128
| fminf16
| fminimum
| fminimum_num
| fminimum_numf
| fminimum_numf128
| fminimum_numf16
| fminimumf
| fminimumf128
| fminimumf16
| fmodf128
| fmodf16
| ldexpf128

View file

@ -391,6 +391,62 @@
],
"type": "f16"
},
"fmaximum": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fmaximum.rs"
],
"type": "f64"
},
"fmaximum_num": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fmaximum_num.rs"
],
"type": "f64"
},
"fmaximum_numf": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fmaximum_num.rs"
],
"type": "f32"
},
"fmaximum_numf128": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fmaximum_num.rs"
],
"type": "f128"
},
"fmaximum_numf16": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fmaximum_num.rs"
],
"type": "f16"
},
"fmaximumf": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fmaximum.rs"
],
"type": "f32"
},
"fmaximumf128": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fmaximum.rs"
],
"type": "f128"
},
"fmaximumf16": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fmaximum.rs"
],
"type": "f16"
},
"fmin": {
"sources": [
"src/math/fmin_fmax.rs",
@ -419,6 +475,62 @@
],
"type": "f16"
},
"fminimum": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fminimum.rs"
],
"type": "f64"
},
"fminimum_num": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fminimum_num.rs"
],
"type": "f64"
},
"fminimum_numf": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fminimum_num.rs"
],
"type": "f32"
},
"fminimum_numf128": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fminimum_num.rs"
],
"type": "f128"
},
"fminimum_numf16": {
"sources": [
"src/math/fminimum_fmaximum_num.rs",
"src/math/generic/fminimum_num.rs"
],
"type": "f16"
},
"fminimumf": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fminimum.rs"
],
"type": "f32"
},
"fminimumf128": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fminimum.rs"
],
"type": "f128"
},
"fminimumf16": {
"sources": [
"src/math/fminimum_fmaximum.rs",
"src/math/generic/fminimum.rs"
],
"type": "f16"
},
"fmod": {
"sources": [
"src/math/fmod.rs",

View file

@ -58,10 +58,26 @@ fmax
fmaxf
fmaxf128
fmaxf16
fmaximum
fmaximum_num
fmaximum_numf
fmaximum_numf128
fmaximum_numf16
fmaximumf
fmaximumf128
fmaximumf16
fmin
fminf
fminf128
fminf16
fminimum
fminimum_num
fminimum_numf
fminimum_numf128
fminimum_numf16
fminimumf
fminimumf128
fminimumf16
fmod
fmodf
fmodf128

View file

@ -137,7 +137,15 @@ libm_helper! {
(fn floor(x: f64) -> (f64); => floor);
(fn fma(x: f64, y: f64, z: f64) -> (f64); => fma);
(fn fmax(x: f64, y: f64) -> (f64); => fmax);
(fn fmaximum(x: f64, y: f64) -> (f64); => fmaximum);
(fn fmaximum_num(x: f64, y: f64) -> (f64); => fmaximum_num);
(fn fmaximum_numf(x: f32, y: f32) -> (f32); => fmaximum_numf);
(fn fmaximumf(x: f32, y: f32) -> (f32); => fmaximumf);
(fn fmin(x: f64, y: f64) -> (f64); => fmin);
(fn fminimum(x: f64, y: f64) -> (f64); => fminimum);
(fn fminimum_num(x: f64, y: f64) -> (f64); => fminimum_num);
(fn fminimum_numf(x: f32, y: f32) -> (f32); => fminimum_numf);
(fn fminimumf(x: f32, y: f32) -> (f32); => fminimumf);
(fn fmod(x: f64, y: f64) -> (f64); => fmod);
(fn frexp(x: f64) -> (f64, i32); => frexp);
(fn hypot(x: f64, y: f64) -> (f64); => hypot);
@ -186,7 +194,11 @@ libm_helper! {
(fn fdim(x: f16, y: f16) -> (f16); => fdimf16);
(fn floorf(x: f16) -> (f16); => floorf16);
(fn fmaxf(x: f16, y: f16) -> (f16); => fmaxf16);
(fn fmaximum_numf16(x: f16, y: f16) -> (f16); => fmaximum_numf16);
(fn fmaximumf16(x: f16, y: f16) -> (f16); => fmaximumf16);
(fn fminf(x: f16, y: f16) -> (f16); => fminf16);
(fn fminimum_numf16(x: f16, y: f16) -> (f16); => fminimum_numf16);
(fn fminimumf16(x: f16, y: f16) -> (f16); => fminimumf16);
(fn fmodf(x: f16, y: f16) -> (f16); => fmodf16);
(fn ldexpf16(x: f16, n: i32) -> (f16); => ldexpf16);
(fn rintf(x: f16) -> (f16); => rintf16);
@ -208,9 +220,13 @@ libm_helper! {
(fn fabs(x: f128) -> (f128); => fabsf128);
(fn fdim(x: f128, y: f128) -> (f128); => fdimf128);
(fn floor(x: f128) -> (f128); => floorf128);
(fn fmaf128(x: f128, y: f128, z: f128) -> (f128); => fmaf128);
(fn fmaf128(x: f128, y: f128, z: f128) -> (f128); => fmaf128);
(fn fmax(x: f128, y: f128) -> (f128); => fmaxf128);
(fn fmaximum_numf128(x: f128, y: f128) -> (f128); => fmaximum_numf128);
(fn fmaximumf128(x: f128, y: f128) -> (f128); => fmaximumf128);
(fn fmin(x: f128, y: f128) -> (f128); => fminf128);
(fn fminimum_numf128(x: f128, y: f128) -> (f128); => fminimum_numf128);
(fn fminimumf128(x: f128, y: f128) -> (f128); => fminimumf128);
(fn fmod(x: f128, y: f128) -> (f128); => fmodf128);
(fn ldexpf128(x: f128, n: i32) -> (f128); => ldexpf128);
(fn rint(x: f128) -> (f128); => rintf128);

View file

@ -1,4 +1,7 @@
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `minNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg(f16_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminf16(x: f16, y: f16) -> f16 {
@ -6,18 +9,27 @@ pub fn fminf16(x: f16, y: f16) -> f16 {
}
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `minNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminf(x: f32, y: f32) -> f32 {
super::generic::fmin(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `minNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmin(x: f64, y: f64) -> f64 {
super::generic::fmin(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `minNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg(f128_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminf128(x: f128, y: f128) -> f128 {
@ -25,6 +37,9 @@ pub fn fminf128(x: f128, y: f128) -> f128 {
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `maxNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg(f16_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaxf16(x: f16, y: f16) -> f16 {
@ -32,18 +47,27 @@ pub fn fmaxf16(x: f16, y: f16) -> f16 {
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `maxNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaxf(x: f32, y: f32) -> f32 {
super::generic::fmax(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `maxNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmax(x: f64, y: f64) -> f64 {
super::generic::fmax(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2011 `maxNum`. The result disregards signed zero (meaning if
/// the inputs are -0.0 and +0.0, either may be returned).
#[cfg(f128_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaxf128(x: f128, y: f128) -> f128 {

View file

@ -0,0 +1,67 @@
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `minimum`. The result orders -0.0 < 0.0.
#[cfg(f16_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimumf16(x: f16, y: f16) -> f16 {
super::generic::fminimum(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `minimum`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimum(x: f64, y: f64) -> f64 {
super::generic::fminimum(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `minimum`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimumf(x: f32, y: f32) -> f32 {
super::generic::fminimum(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `minimum`. The result orders -0.0 < 0.0.
#[cfg(f128_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimumf128(x: f128, y: f128) -> f128 {
super::generic::fminimum(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `maximum`. The result orders -0.0 < 0.0.
#[cfg(f16_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximumf16(x: f16, y: f16) -> f16 {
super::generic::fmaximum(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `maximum`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximumf(x: f32, y: f32) -> f32 {
super::generic::fmaximum(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `maximum`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximum(x: f64, y: f64) -> f64 {
super::generic::fmaximum(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, the other argument.
///
/// This coincides with IEEE 754-2019 `maximum`. The result orders -0.0 < 0.0.
#[cfg(f128_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximumf128(x: f128, y: f128) -> f128 {
super::generic::fmaximum(x, y)
}

View file

@ -0,0 +1,67 @@
/// Return the lesser of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `minimumNumber`. The result orders -0.0 < 0.0.
#[cfg(f16_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimum_numf16(x: f16, y: f16) -> f16 {
super::generic::fminimum_num(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `minimumNumber`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimum_numf(x: f32, y: f32) -> f32 {
super::generic::fminimum_num(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `minimumNumber`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimum_num(x: f64, y: f64) -> f64 {
super::generic::fminimum_num(x, y)
}
/// Return the lesser of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `minimumNumber`. The result orders -0.0 < 0.0.
#[cfg(f128_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fminimum_numf128(x: f128, y: f128) -> f128 {
super::generic::fminimum_num(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `maximumNumber`. The result orders -0.0 < 0.0.
#[cfg(f16_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximum_numf16(x: f16, y: f16) -> f16 {
super::generic::fmaximum_num(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `maximumNumber`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximum_numf(x: f32, y: f32) -> f32 {
super::generic::fmaximum_num(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `maximumNumber`. The result orders -0.0 < 0.0.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximum_num(x: f64, y: f64) -> f64 {
super::generic::fmaximum_num(x, y)
}
/// Return the greater of two arguments or, if either argument is NaN, NaN.
///
/// This coincides with IEEE 754-2019 `maximumNumber`. The result orders -0.0 < 0.0.
#[cfg(f128_enabled)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmaximum_numf128(x: f128, y: f128) -> f128 {
super::generic::fmaximum_num(x, y)
}

View file

@ -1,14 +1,73 @@
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
//! IEEE 754-2011 `maxNum`. This has been superseded by IEEE 754-2019 `maximumNumber`.
//!
//! Per the spec, returns the canonicalized result of:
//! - `x` if `x > y`
//! - `y` if `y > x`
//! - The other number if one is NaN
//! - Otherwise, either `x` or `y`, canonicalized
//! - -0.0 and +0.0 may be disregarded (unlike newer operations)
//!
//! Excluded from our implementation is sNaN handling.
//!
//! More on the differences: [link].
//!
//! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
use super::super::Float;
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fmax<F: Float>(x: F, y: F) -> F {
// IEEE754 says: maxNum(x, y) is the canonicalized number y if x < y, x if y < x, the
// canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it
// is either x or y, canonicalized (this means results might differ among implementations).
// When either x or y is a signalingNaN, then the result is according to 6.2.
//
// Since we do not support sNaN in Rust yet, we do not need to handle them.
// FIXME(nagisa): due to https://bugs.llvm.org/show_bug.cgi?id=33303 we canonicalize by
// multiplying by 1.0. Should switch to the `canonicalize` when it works.
(if x.is_nan() || x < y { y } else { x }) * F::ONE
let res = if x.is_nan() || x < y { y } else { x };
// Canonicalize
res * F::ONE
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::{Hexf, Int};
fn spec_test<F: Float>() {
let cases = [
(F::ZERO, F::ZERO, F::ZERO),
(F::ONE, F::ONE, F::ONE),
(F::ZERO, F::ONE, F::ONE),
(F::ONE, F::ZERO, F::ONE),
(F::ZERO, F::NEG_ONE, F::ZERO),
(F::NEG_ONE, F::ZERO, F::ZERO),
(F::INFINITY, F::ZERO, F::INFINITY),
(F::NEG_INFINITY, F::ZERO, F::ZERO),
(F::NAN, F::ZERO, F::ZERO),
(F::ZERO, F::NAN, F::ZERO),
(F::NAN, F::NAN, F::NAN),
];
for (x, y, res) in cases {
let val = fmax(x, y);
assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
}
}
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
spec_test::<f16>();
}
#[test]
fn spec_tests_f32() {
spec_test::<f32>();
}
#[test]
fn spec_tests_f64() {
spec_test::<f64>();
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
spec_test::<f128>();
}
}

View file

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
//! IEEE 754-2019 `maximum`.
//!
//! Per the spec, returns the canonicalized result of:
//! - `x` if `x > y`
//! - `y` if `y > x`
//! - qNaN if either operation is NaN
//! - Logic following +0.0 > -0.0
//!
//! Excluded from our implementation is sNaN handling.
use super::super::Float;
pub fn fmaximum<F: Float>(x: F, y: F) -> F {
let res = if x.is_nan() {
x
} else if y.is_nan() {
y
} else if x > y || (y.to_bits() == F::NEG_ZERO.to_bits() && x.is_sign_positive()) {
x
} else {
y
};
// Canonicalize
res * F::ONE
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::{Hexf, Int};
fn spec_test<F: Float>() {
let cases = [
(F::ZERO, F::ZERO, F::ZERO),
(F::ONE, F::ONE, F::ONE),
(F::ZERO, F::ONE, F::ONE),
(F::ONE, F::ZERO, F::ONE),
(F::ZERO, F::NEG_ONE, F::ZERO),
(F::NEG_ONE, F::ZERO, F::ZERO),
(F::INFINITY, F::ZERO, F::INFINITY),
(F::NEG_INFINITY, F::ZERO, F::ZERO),
(F::NAN, F::ZERO, F::NAN),
(F::ZERO, F::NAN, F::NAN),
(F::NAN, F::NAN, F::NAN),
(F::ZERO, F::NEG_ZERO, F::ZERO),
(F::NEG_ZERO, F::ZERO, F::ZERO),
];
for (x, y, res) in cases {
let val = fmaximum(x, y);
assert_biteq!(val, res, "fmaximum({}, {})", Hexf(x), Hexf(y));
}
}
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
spec_test::<f16>();
}
#[test]
fn spec_tests_f32() {
spec_test::<f32>();
}
#[test]
fn spec_tests_f64() {
spec_test::<f64>();
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
spec_test::<f128>();
}
}

View file

@ -0,0 +1,77 @@
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
//! IEEE 754-2019 `maximumNumber`.
//!
//! Per the spec, returns:
//! - `x` if `x > y`
//! - `y` if `y > x`
//! - Non-NaN if one operand is NaN
//! - Logic following +0.0 > -0.0
//! - Either `x` or `y` if `x == y` and the signs are the same
//! - qNaN if either operand is a NaN
//!
//! Excluded from our implementation is sNaN handling.
use super::super::Float;
pub fn fmaximum_num<F: Float>(x: F, y: F) -> F {
let res =
if x.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) {
y
} else {
x
};
// Canonicalize
res * F::ONE
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::{Hexf, Int};
fn spec_test<F: Float>() {
let cases = [
(F::ZERO, F::ZERO, F::ZERO),
(F::ONE, F::ONE, F::ONE),
(F::ZERO, F::ONE, F::ONE),
(F::ONE, F::ZERO, F::ONE),
(F::ZERO, F::NEG_ONE, F::ZERO),
(F::NEG_ONE, F::ZERO, F::ZERO),
(F::INFINITY, F::ZERO, F::INFINITY),
(F::NEG_INFINITY, F::ZERO, F::ZERO),
(F::NAN, F::ZERO, F::ZERO),
(F::ZERO, F::NAN, F::ZERO),
(F::NAN, F::NAN, F::NAN),
(F::ZERO, F::NEG_ZERO, F::ZERO),
(F::NEG_ZERO, F::ZERO, F::ZERO),
];
for (x, y, res) in cases {
let val = fmaximum_num(x, y);
assert_biteq!(val, res, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
}
}
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
spec_test::<f16>();
}
#[test]
fn spec_tests_f32() {
spec_test::<f32>();
}
#[test]
fn spec_tests_f64() {
spec_test::<f64>();
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
spec_test::<f128>();
}
}

View file

@ -1,13 +1,72 @@
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
//! IEEE 754-2008 `minNum`. This has been superseded by IEEE 754-2019 `minimumNumber`.
//!
//! Per the spec, returns the canonicalized result of:
//! - `x` if `x < y`
//! - `y` if `y < x`
//! - The other number if one is NaN
//! - Otherwise, either `x` or `y`, canonicalized
//! - -0.0 and +0.0 may be disregarded (unlike newer operations)
//!
//! Excluded from our implementation is sNaN handling.
//!
//! More on the differences: [link].
//!
//! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
use super::super::Float;
pub fn fmin<F: Float>(x: F, y: F) -> F {
// IEEE754 says: minNum(x, y) is the canonicalized number x if x < y, y if y < x, the
// canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it
// is either x or y, canonicalized (this means results might differ among implementations).
// When either x or y is a signalingNaN, then the result is according to 6.2.
//
// Since we do not support sNaN in Rust yet, we do not need to handle them.
// FIXME(nagisa): due to https://bugs.llvm.org/show_bug.cgi?id=33303 we canonicalize by
// multiplying by 1.0. Should switch to the `canonicalize` when it works.
(if y.is_nan() || x < y { x } else { y }) * F::ONE
let res = if y.is_nan() || x < y { x } else { y };
// Canonicalize
res * F::ONE
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::{Hexf, Int};
fn spec_test<F: Float>() {
let cases = [
(F::ZERO, F::ZERO, F::ZERO),
(F::ONE, F::ONE, F::ONE),
(F::ZERO, F::ONE, F::ZERO),
(F::ONE, F::ZERO, F::ZERO),
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
(F::INFINITY, F::ZERO, F::ZERO),
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
(F::NAN, F::ZERO, F::ZERO),
(F::ZERO, F::NAN, F::ZERO),
(F::NAN, F::NAN, F::NAN),
];
for (x, y, res) in cases {
let val = fmin(x, y);
assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
}
}
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
spec_test::<f16>();
}
#[test]
fn spec_tests_f32() {
spec_test::<f32>();
}
#[test]
fn spec_tests_f64() {
spec_test::<f64>();
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
spec_test::<f128>();
}
}

View file

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
//! IEEE 754-2019 `minimum`.
//!
//! Per the spec, returns the canonicalized result of:
//! - `x` if `x < y`
//! - `y` if `y < x`
//! - qNaN if either operation is NaN
//! - Logic following +0.0 > -0.0
//!
//! Excluded from our implementation is sNaN handling.
use super::super::Float;
pub fn fminimum<F: Float>(x: F, y: F) -> F {
let res = if x.is_nan() {
x
} else if y.is_nan() {
y
} else if x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) {
x
} else {
y
};
// Canonicalize
res * F::ONE
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::{Hexf, Int};
fn spec_test<F: Float>() {
let cases = [
(F::ZERO, F::ZERO, F::ZERO),
(F::ONE, F::ONE, F::ONE),
(F::ZERO, F::ONE, F::ZERO),
(F::ONE, F::ZERO, F::ZERO),
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
(F::INFINITY, F::ZERO, F::ZERO),
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
(F::NAN, F::ZERO, F::NAN),
(F::ZERO, F::NAN, F::NAN),
(F::NAN, F::NAN, F::NAN),
(F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
];
for (x, y, res) in cases {
let val = fminimum(x, y);
assert_biteq!(val, res, "fminimum({}, {})", Hexf(x), Hexf(y));
}
}
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
spec_test::<f16>();
}
#[test]
fn spec_tests_f32() {
spec_test::<f32>();
}
#[test]
fn spec_tests_f64() {
spec_test::<f64>();
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
spec_test::<f128>();
}
}

View file

@ -0,0 +1,77 @@
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
//! IEEE 754-2019 `minimum`.
//!
//! Per the spec, returns:
//! - `x` if `x < y`
//! - `y` if `y < x`
//! - Non-NaN if one operand is NaN
//! - Logic following +0.0 > -0.0
//! - Either `x` or `y` if `x == y` and the signs are the same
//! - qNaN if either operand is a NaN
//!
//! Excluded from our implementation is sNaN handling.
use super::super::Float;
pub fn fminimum_num<F: Float>(x: F, y: F) -> F {
let res =
if y.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) {
x
} else {
y
};
// Canonicalize
res * F::ONE
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::{Hexf, Int};
fn spec_test<F: Float>() {
let cases = [
(F::ZERO, F::ZERO, F::ZERO),
(F::ONE, F::ONE, F::ONE),
(F::ZERO, F::ONE, F::ZERO),
(F::ONE, F::ZERO, F::ZERO),
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
(F::INFINITY, F::ZERO, F::ZERO),
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
(F::NAN, F::ZERO, F::ZERO),
(F::ZERO, F::NAN, F::ZERO),
(F::NAN, F::NAN, F::NAN),
(F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
];
for (x, y, res) in cases {
let val = fminimum_num(x, y);
assert_biteq!(val, res, "fminimum_num({}, {})", Hexf(x), Hexf(y));
}
}
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
spec_test::<f16>();
}
#[test]
fn spec_tests_f32() {
spec_test::<f32>();
}
#[test]
fn spec_tests_f64() {
spec_test::<f64>();
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
spec_test::<f128>();
}
}

View file

@ -5,7 +5,11 @@ mod fdim;
mod floor;
mod fma;
mod fmax;
mod fmaximum;
mod fmaximum_num;
mod fmin;
mod fminimum;
mod fminimum_num;
mod fmod;
mod rint;
mod round;
@ -20,7 +24,11 @@ pub use fdim::fdim;
pub use floor::floor;
pub use fma::{fma, fma_wide};
pub use fmax::fmax;
pub use fmaximum::fmaximum;
pub use fmaximum_num::fmaximum_num;
pub use fmin::fmin;
pub use fminimum::fminimum;
pub use fminimum_num::fminimum_num;
pub use fmod::fmod;
pub use rint::rint;
pub use round::round;

View file

@ -166,6 +166,8 @@ mod floorf;
mod fma;
mod fmaf;
mod fmin_fmax;
mod fminimum_fmaximum;
mod fminimum_fmaximum_num;
mod fmod;
mod fmodf;
mod frexp;
@ -271,6 +273,8 @@ pub use self::floorf::floorf;
pub use self::fma::fma;
pub use self::fmaf::fmaf;
pub use self::fmin_fmax::{fmax, fmaxf, fmin, fminf};
pub use self::fminimum_fmaximum::{fmaximum, fmaximumf, fminimum, fminimumf};
pub use self::fminimum_fmaximum_num::{fmaximum_num, fmaximum_numf, fminimum_num, fminimum_numf};
pub use self::fmod::fmod;
pub use self::fmodf::fmodf;
pub use self::frexp::frexp;
@ -355,8 +359,9 @@ cfg_if! {
pub use self::fabsf16::fabsf16;
pub use self::fdimf16::fdimf16;
pub use self::floorf16::floorf16;
pub use self::fmin_fmax::fmaxf16;
pub use self::fmin_fmax::fminf16;
pub use self::fmin_fmax::{fmaxf16, fminf16};
pub use self::fminimum_fmaximum::{fmaximumf16, fminimumf16};
pub use self::fminimum_fmaximum_num::{fmaximum_numf16, fminimum_numf16};
pub use self::fmodf16::fmodf16;
pub use self::ldexpf16::ldexpf16;
pub use self::rintf16::rintf16;
@ -393,8 +398,9 @@ cfg_if! {
pub use self::fdimf128::fdimf128;
pub use self::floorf128::floorf128;
pub use self::fmaf128::fmaf128;
pub use self::fmin_fmax::fmaxf128;
pub use self::fmin_fmax::fminf128;
pub use self::fmin_fmax::{fmaxf128, fminf128};
pub use self::fminimum_fmaximum::{fmaximumf128, fminimumf128};
pub use self::fminimum_fmaximum_num::{fmaximum_numf128, fminimum_numf128};
pub use self::fmodf128::fmodf128;
pub use self::ldexpf128::ldexpf128;
pub use self::rintf128::rintf128;