about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTrevor Gross <tmgross@umich.edu>2025-02-10 19:43:49 +0000
committerTrevor Gross <t.gross35@gmail.com>2025-02-10 16:17:33 -0600
commit669731335e05705eae35f6ba16bdcce9fabf1e90 (patch)
treed657b446adfc03c5f351ab176f28ca4e0cd39f65
parent86ee1f99c9d361338017282810966efc33901c3d (diff)
downloadrust-669731335e05705eae35f6ba16bdcce9fabf1e90.tar.gz
rust-669731335e05705eae35f6ba16bdcce9fabf1e90.zip
Add `fminimum`, `fmaximum`, `fminimum_num`, and `fmaximum_num`
These functions represent new operations from IEEE 754-2019. Introduce
them for all float sizes.
-rw-r--r--library/compiler-builtins/libm/crates/libm-macros/src/shared.rs32
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/benches/icount.rs16
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/benches/random.rs16
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/domain.rs4
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/gen/case_list.rs80
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs54
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/precision.rs4
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs16
-rw-r--r--library/compiler-builtins/libm/crates/util/src/main.rs16
-rw-r--r--library/compiler-builtins/libm/etc/function-definitions.json112
-rw-r--r--library/compiler-builtins/libm/etc/function-list.txt16
-rw-r--r--library/compiler-builtins/libm/src/libm_helper.rs18
-rw-r--r--library/compiler-builtins/libm/src/math/fmin_fmax.rs24
-rw-r--r--library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs67
-rw-r--r--library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs67
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmax.rs77
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmaximum.rs78
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs77
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmin.rs77
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fminimum.rs78
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fminimum_num.rs77
-rw-r--r--library/compiler-builtins/libm/src/math/generic/mod.rs8
-rw-r--r--library/compiler-builtins/libm/src/math/mod.rs14
23 files changed, 997 insertions, 31 deletions
diff --git a/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs b/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs
index 48d19c50d19..cb5a1d18734 100644
--- a/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs
+++ b/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs
@@ -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`
diff --git a/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs b/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs
index be85dd5676c..e28f4973cb0 100644
--- a/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs
@@ -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,
diff --git a/library/compiler-builtins/libm/crates/libm-test/benches/random.rs b/library/compiler-builtins/libm/crates/libm-test/benches/random.rs
index 6e8a334795a..6f6b05d9596 100644
--- a/library/compiler-builtins/libm/crates/libm-test/benches/random.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/benches/random.rs
@@ -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
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/domain.rs b/library/compiler-builtins/libm/crates/libm-test/src/domain.rs
index 5d650c00a3b..c662e95b4b4 100644
--- a/library/compiler-builtins/libm/crates/libm-test/src/domain.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/src/domain.rs
@@ -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[..],
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/gen/case_list.rs b/library/compiler-builtins/libm/crates/libm-test/src/gen/case_list.rs
index 23226d5c251..49e731b88b7 100644
--- a/library/compiler-builtins/libm/crates/libm-test/src/gen/case_list.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/src/gen/case_list.rs
@@ -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![]
 }
 
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs b/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs
index f4a9ff7ffd5..63cdebe4eee 100644
--- a/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs
@@ -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 {
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/precision.rs b/library/compiler-builtins/libm/crates/libm-test/src/precision.rs
index 2f55ad22ec7..1d916e5726a 100644
--- a/library/compiler-builtins/libm/crates/libm-test/src/precision.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/src/precision.rs
@@ -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
diff --git a/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs b/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs
index 7fa77e832b1..ffd7f1f60c6 100644
--- a/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs
@@ -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,
diff --git a/library/compiler-builtins/libm/crates/util/src/main.rs b/library/compiler-builtins/libm/crates/util/src/main.rs
index 0f845a1c4d0..a519713c0b1 100644
--- a/library/compiler-builtins/libm/crates/util/src/main.rs
+++ b/library/compiler-builtins/libm/crates/util/src/main.rs
@@ -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
diff --git a/library/compiler-builtins/libm/etc/function-definitions.json b/library/compiler-builtins/libm/etc/function-definitions.json
index d3e51f29a07..008a47df219 100644
--- a/library/compiler-builtins/libm/etc/function-definitions.json
+++ b/library/compiler-builtins/libm/etc/function-definitions.json
@@ -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",
diff --git a/library/compiler-builtins/libm/etc/function-list.txt b/library/compiler-builtins/libm/etc/function-list.txt
index 1c9c5e3bc33..90ca8f34e62 100644
--- a/library/compiler-builtins/libm/etc/function-list.txt
+++ b/library/compiler-builtins/libm/etc/function-list.txt
@@ -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
diff --git a/library/compiler-builtins/libm/src/libm_helper.rs b/library/compiler-builtins/libm/src/libm_helper.rs
index 68f1fb36266..489dbc0d4df 100644
--- a/library/compiler-builtins/libm/src/libm_helper.rs
+++ b/library/compiler-builtins/libm/src/libm_helper.rs
@@ -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);
diff --git a/library/compiler-builtins/libm/src/math/fmin_fmax.rs b/library/compiler-builtins/libm/src/math/fmin_fmax.rs
index 97912e75826..4f9136dbbcd 100644
--- a/library/compiler-builtins/libm/src/math/fmin_fmax.rs
+++ b/library/compiler-builtins/libm/src/math/fmin_fmax.rs
@@ -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 {
diff --git a/library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs b/library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs
new file mode 100644
index 00000000000..fd3c5ed1039
--- /dev/null
+++ b/library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs
@@ -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)
+}
diff --git a/library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs b/library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs
new file mode 100644
index 00000000000..640ddfc9b66
--- /dev/null
+++ b/library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs
@@ -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)
+}
diff --git a/library/compiler-builtins/libm/src/math/generic/fmax.rs b/library/compiler-builtins/libm/src/math/generic/fmax.rs
index 97803052bc2..32613a46b93 100644
--- a/library/compiler-builtins/libm/src/math/generic/fmax.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fmax.rs
@@ -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>();
+    }
 }
diff --git a/library/compiler-builtins/libm/src/math/generic/fmaximum.rs b/library/compiler-builtins/libm/src/math/generic/fmaximum.rs
new file mode 100644
index 00000000000..5f653ce94e2
--- /dev/null
+++ b/library/compiler-builtins/libm/src/math/generic/fmaximum.rs
@@ -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>();
+    }
+}
diff --git a/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs b/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs
new file mode 100644
index 00000000000..22466012392
--- /dev/null
+++ b/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs
@@ -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>();
+    }
+}
diff --git a/library/compiler-builtins/libm/src/math/generic/fmin.rs b/library/compiler-builtins/libm/src/math/generic/fmin.rs
index 697f7200486..5cc33e904ed 100644
--- a/library/compiler-builtins/libm/src/math/generic/fmin.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fmin.rs
@@ -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>();
+    }
 }
diff --git a/library/compiler-builtins/libm/src/math/generic/fminimum.rs b/library/compiler-builtins/libm/src/math/generic/fminimum.rs
new file mode 100644
index 00000000000..f566d9631d3
--- /dev/null
+++ b/library/compiler-builtins/libm/src/math/generic/fminimum.rs
@@ -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>();
+    }
+}
diff --git a/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs b/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs
new file mode 100644
index 00000000000..e58a585c329
--- /dev/null
+++ b/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs
@@ -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>();
+    }
+}
diff --git a/library/compiler-builtins/libm/src/math/generic/mod.rs b/library/compiler-builtins/libm/src/math/generic/mod.rs
index b34d3dfae94..092f9317bc5 100644
--- a/library/compiler-builtins/libm/src/math/generic/mod.rs
+++ b/library/compiler-builtins/libm/src/math/generic/mod.rs
@@ -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;
diff --git a/library/compiler-builtins/libm/src/math/mod.rs b/library/compiler-builtins/libm/src/math/mod.rs
index ba0b933f17c..4e75292a6ef 100644
--- a/library/compiler-builtins/libm/src/math/mod.rs
+++ b/library/compiler-builtins/libm/src/math/mod.rs
@@ -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;