diff --git a/crates/libm-macros/src/shared.rs b/crates/libm-macros/src/shared.rs
index 750ed1afb..31ef9c8e2 100644
--- a/crates/libm-macros/src/shared.rs
+++ b/crates/libm-macros/src/shared.rs
@@ -258,6 +258,16 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
         None,
         &["fmaf128"],
     ),
+    (
+        // `(f16) -> i32`
+        FloatTy::F16,
+        Signature {
+            args: &[Ty::F16],
+            returns: &[Ty::I32],
+        },
+        None,
+        &["ilogbf16"],
+    ),
     (
         // `(f32) -> i32`
         FloatTy::F32,
@@ -278,6 +288,16 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
         None,
         &["ilogb"],
     ),
+    (
+        // `(f128) -> i32`
+        FloatTy::F128,
+        Signature {
+            args: &[Ty::F128],
+            returns: &[Ty::I32],
+        },
+        None,
+        &["ilogbf128"],
+    ),
     (
         // `(i32, f32) -> f32`
         FloatTy::F32,
@@ -364,6 +384,19 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
         }),
         &["modf"],
     ),
+    (
+        // `(f16, &mut c_int) -> f16` as `(f16) -> (f16, i32)`
+        FloatTy::F16,
+        Signature {
+            args: &[Ty::F16],
+            returns: &[Ty::F16, Ty::I32],
+        },
+        Some(Signature {
+            args: &[Ty::F16, Ty::MutCInt],
+            returns: &[Ty::F16],
+        }),
+        &["frexpf16"],
+    ),
     (
         // `(f32, &mut c_int) -> f32` as `(f32) -> (f32, i32)`
         FloatTy::F32,
@@ -390,6 +423,19 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
         }),
         &["frexp", "lgamma_r"],
     ),
+    (
+        // `(f128, &mut c_int) -> f128` as `(f128) -> (f128, i32)`
+        FloatTy::F128,
+        Signature {
+            args: &[Ty::F128],
+            returns: &[Ty::F128, Ty::I32],
+        },
+        Some(Signature {
+            args: &[Ty::F128, Ty::MutCInt],
+            returns: &[Ty::F128],
+        }),
+        &["frexpf128"],
+    ),
     (
         // `(f32, f32, &mut c_int) -> f32` as `(f32, f32) -> (f32, i32)`
         FloatTy::F32,
diff --git a/crates/util/src/main.rs b/crates/util/src/main.rs
index e70578699..a7b4a4e6c 100644
--- a/crates/util/src/main.rs
+++ b/crates/util/src/main.rs
@@ -119,6 +119,10 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
             | fminimumf16
             | fmodf128
             | fmodf16
+            | frexpf128
+            | frexpf16
+            | ilogbf128
+            | ilogbf16
             | ldexpf128
             | ldexpf16
             | rintf128
diff --git a/libm-test/benches/random.rs b/libm-test/benches/random.rs
index 81f58e3a6..55a6f4e65 100644
--- a/libm-test/benches/random.rs
+++ b/libm-test/benches/random.rs
@@ -159,6 +159,10 @@ libm_macros::for_each_function! {
         | fminimumf16
         | fmodf128
         | fmodf16
+        | frexpf128
+        | frexpf16
+        | ilogbf128
+        | ilogbf16
         | ldexpf128
         | ldexpf16
         | rintf128
diff --git a/libm-test/src/generate/case_list.rs b/libm-test/src/generate/case_list.rs
index f1e6fcec3..aa90ede41 100644
--- a/libm-test/src/generate/case_list.rs
+++ b/libm-test/src/generate/case_list.rs
@@ -449,7 +449,8 @@ fn fmodf16_cases() -> Vec<TestCase<op::fmodf16::Routine>> {
     vec![]
 }
 
-fn frexp_cases() -> Vec<TestCase<op::frexp::Routine>> {
+#[cfg(f16_enabled)]
+fn frexpf16_cases() -> Vec<TestCase<op::frexpf16::Routine>> {
     vec![]
 }
 
@@ -457,6 +458,15 @@ fn frexpf_cases() -> Vec<TestCase<op::frexpf::Routine>> {
     vec![]
 }
 
+fn frexp_cases() -> Vec<TestCase<op::frexp::Routine>> {
+    vec![]
+}
+
+#[cfg(f128_enabled)]
+fn frexpf128_cases() -> Vec<TestCase<op::frexpf128::Routine>> {
+    vec![]
+}
+
 fn hypot_cases() -> Vec<TestCase<op::hypot::Routine>> {
     vec![]
 }
@@ -465,7 +475,8 @@ fn hypotf_cases() -> Vec<TestCase<op::hypotf::Routine>> {
     vec![]
 }
 
-fn ilogb_cases() -> Vec<TestCase<op::ilogb::Routine>> {
+#[cfg(f16_enabled)]
+fn ilogbf16_cases() -> Vec<TestCase<op::ilogbf16::Routine>> {
     vec![]
 }
 
@@ -473,6 +484,15 @@ fn ilogbf_cases() -> Vec<TestCase<op::ilogbf::Routine>> {
     vec![]
 }
 
+fn ilogb_cases() -> Vec<TestCase<op::ilogb::Routine>> {
+    vec![]
+}
+
+#[cfg(f128_enabled)]
+fn ilogbf128_cases() -> Vec<TestCase<op::ilogbf128::Routine>> {
+    vec![]
+}
+
 fn j0_cases() -> Vec<TestCase<op::j0::Routine>> {
     vec![]
 }
diff --git a/libm-test/src/mpfloat.rs b/libm-test/src/mpfloat.rs
index 9b51dc605..e121ef2bc 100644
--- a/libm-test/src/mpfloat.rs
+++ b/libm-test/src/mpfloat.rs
@@ -162,8 +162,12 @@ libm_macros::for_each_function! {
         fmodf16,
         frexp,
         frexpf,
+        frexpf128,
+        frexpf16,
         ilogb,
         ilogbf,
+        ilogbf128,
+        ilogbf16,
         jn,
         jnf,
         ldexp,
@@ -323,43 +327,6 @@ macro_rules! impl_op_for_ty {
                 }
             }
 
-            impl MpOp for crate::op::[<frexp $suffix>]::Routine {
-                type MpTy = MpFloat;
-
-                fn new_mp() -> Self::MpTy {
-                    new_mpfloat::<Self::FTy>()
-                }
-
-                fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
-                    this.assign(input.0);
-                    let exp = this.frexp_mut();
-                    (prep_retval::<Self::FTy>(this, Ordering::Equal), exp)
-                }
-            }
-
-            impl MpOp for crate::op::[<ilogb $suffix>]::Routine {
-                type MpTy = MpFloat;
-
-                fn new_mp() -> Self::MpTy {
-                    new_mpfloat::<Self::FTy>()
-                }
-
-                fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
-                    this.assign(input.0);
-
-                    // `get_exp` follows `frexp` for `0.5 <= |m| < 1.0`. Adjust the exponent by
-                    // one to scale the significand to `1.0 <= |m| < 2.0`.
-                    this.get_exp().map(|v| v - 1).unwrap_or_else(|| {
-                        if this.is_infinite() {
-                            i32::MAX
-                        } else {
-                            // Zero or NaN
-                            i32::MIN
-                        }
-                    })
-                }
-            }
-
             impl MpOp for crate::op::[<jn $suffix>]::Routine {
                 type MpTy = MpFloat;
 
@@ -504,6 +471,43 @@ macro_rules! impl_op_for_ty_all {
                 }
             }
 
+            impl MpOp for crate::op::[<frexp $suffix>]::Routine {
+                type MpTy = MpFloat;
+
+                fn new_mp() -> Self::MpTy {
+                    new_mpfloat::<Self::FTy>()
+                }
+
+                fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
+                    this.assign(input.0);
+                    let exp = this.frexp_mut();
+                    (prep_retval::<Self::FTy>(this, Ordering::Equal), exp)
+                }
+            }
+
+            impl MpOp for crate::op::[<ilogb $suffix>]::Routine {
+                type MpTy = MpFloat;
+
+                fn new_mp() -> Self::MpTy {
+                    new_mpfloat::<Self::FTy>()
+                }
+
+                fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
+                    this.assign(input.0);
+
+                    // `get_exp` follows `frexp` for `0.5 <= |m| < 1.0`. Adjust the exponent by
+                    // one to scale the significand to `1.0 <= |m| < 2.0`.
+                    this.get_exp().map(|v| v - 1).unwrap_or_else(|| {
+                        if this.is_infinite() {
+                            i32::MAX
+                        } else {
+                            // Zero or NaN
+                            i32::MIN
+                        }
+                    })
+                }
+            }
+
             // `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/libm-test/src/test_traits.rs b/libm-test/src/test_traits.rs
index dbb970161..644d25387 100644
--- a/libm-test/src/test_traits.rs
+++ b/libm-test/src/test_traits.rs
@@ -451,3 +451,15 @@ impl_tuples!(
     (f32, f32);
     (f64, f64);
 );
+
+#[cfg(f16_enabled)]
+impl_tuples!(
+    (f16, i32);
+    (f16, f16);
+);
+
+#[cfg(f128_enabled)]
+impl_tuples!(
+    (f128, i32);
+    (f128, f128);
+);
diff --git a/libm-test/tests/compare_built_musl.rs b/libm-test/tests/compare_built_musl.rs
index cbb4bd49b..24a4d0a64 100644
--- a/libm-test/tests/compare_built_musl.rs
+++ b/libm-test/tests/compare_built_musl.rs
@@ -122,6 +122,10 @@ libm_macros::for_each_function! {
         fminimumf16,
         fmodf128,
         fmodf16,
+        frexpf128,
+        frexpf16,
+        ilogbf128,
+        ilogbf16,
         ldexpf128,
         ldexpf16,
         rintf128,
diff --git a/libm/src/math/frexp.rs b/libm/src/math/frexp.rs
index de7a64fda..102be11db 100644
--- a/libm/src/math/frexp.rs
+++ b/libm/src/math/frexp.rs
@@ -1,21 +1,33 @@
+/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
+///
+/// That is, `x * 2^p` will represent the input value.
+#[cfg(f16_enabled)]
 #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
-pub fn frexp(x: f64) -> (f64, i32) {
-    let mut y = x.to_bits();
-    let ee = ((y >> 52) & 0x7ff) as i32;
+pub fn frexpf16(x: f16) -> (f16, i32) {
+    super::generic::frexp(x)
+}
 
-    if ee == 0 {
-        if x != 0.0 {
-            let x1p64 = f64::from_bits(0x43f0000000000000);
-            let (x, e) = frexp(x * x1p64);
-            return (x, e - 64);
-        }
-        return (x, 0);
-    } else if ee == 0x7ff {
-        return (x, 0);
-    }
+/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
+///
+/// That is, `x * 2^p` will represent the input value.
+#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
+pub fn frexpf(x: f32) -> (f32, i32) {
+    super::generic::frexp(x)
+}
 
-    let e = ee - 0x3fe;
-    y &= 0x800fffffffffffff;
-    y |= 0x3fe0000000000000;
-    return (f64::from_bits(y), e);
+/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
+///
+/// That is, `x * 2^p` will represent the input value.
+#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
+pub fn frexp(x: f64) -> (f64, i32) {
+    super::generic::frexp(x)
+}
+
+/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
+///
+/// That is, `x * 2^p` will represent the input value.
+#[cfg(f128_enabled)]
+#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
+pub fn frexpf128(x: f128) -> (f128, i32) {
+    super::generic::frexp(x)
 }
diff --git a/libm/src/math/frexpf.rs b/libm/src/math/frexpf.rs
deleted file mode 100644
index 0ec91c2d3..000000000
--- a/libm/src/math/frexpf.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
-pub fn frexpf(x: f32) -> (f32, i32) {
-    let mut y = x.to_bits();
-    let ee: i32 = ((y >> 23) & 0xff) as i32;
-
-    if ee == 0 {
-        if x != 0.0 {
-            let x1p64 = f32::from_bits(0x5f800000);
-            let (x, e) = frexpf(x * x1p64);
-            return (x, e - 64);
-        } else {
-            return (x, 0);
-        }
-    } else if ee == 0xff {
-        return (x, 0);
-    }
-
-    let e = ee - 0x7e;
-    y &= 0x807fffff;
-    y |= 0x3f000000;
-    (f32::from_bits(y), e)
-}
diff --git a/libm/src/math/generic/frexp.rs b/libm/src/math/generic/frexp.rs
new file mode 100644
index 000000000..e560e0374
--- /dev/null
+++ b/libm/src/math/generic/frexp.rs
@@ -0,0 +1,25 @@
+use super::super::{CastFrom, Float, MinInt};
+
+pub fn frexp<F: Float>(x: F) -> (F, i32) {
+    let mut ix = x.to_bits();
+    let ee = x.ex();
+
+    if ee == 0 {
+        if x != F::ZERO {
+            // normalize via multiplication; 1p64 for `f64`
+            let magic = F::from_parts(false, F::EXP_BIAS + F::BITS, F::Int::ZERO);
+            magic.to_bits();
+
+            let (x, e) = frexp(x * magic);
+            return (x, e - F::BITS as i32);
+        }
+        return (x, 0);
+    } else if ee == F::EXP_SAT {
+        return (x, 0);
+    }
+
+    let e = ee as i32 - (F::EXP_BIAS as i32 - 1);
+    ix &= F::SIGN_MASK | F::SIG_MASK;
+    ix |= F::Int::cast_from(F::EXP_BIAS - 1) << F::SIG_BITS;
+    (F::from_bits(ix), e)
+}
diff --git a/libm/src/math/generic/ilogb.rs b/libm/src/math/generic/ilogb.rs
new file mode 100644
index 000000000..dcc474592
--- /dev/null
+++ b/libm/src/math/generic/ilogb.rs
@@ -0,0 +1,34 @@
+use super::super::{Float, MinInt};
+
+const FP_ILOGBNAN: i32 = i32::MIN;
+const FP_ILOGB0: i32 = FP_ILOGBNAN;
+
+pub fn ilogb<F: Float>(x: F) -> i32 {
+    let zero = F::Int::ZERO;
+    let mut i = x.to_bits();
+    let e = x.ex() as i32;
+
+    if e == 0 {
+        i <<= F::EXP_BITS + 1;
+        if i == F::Int::ZERO {
+            force_eval!(0.0 / 0.0);
+            return FP_ILOGB0;
+        }
+        /* subnormal x */
+        let mut e = -(F::EXP_BIAS as i32);
+        while i >> (F::BITS - 1) == zero {
+            e -= 1;
+            i <<= 1;
+        }
+        e
+    } else if e == F::EXP_SAT as i32 {
+        force_eval!(0.0 / 0.0);
+        if i << (F::EXP_BITS + 1) != zero {
+            FP_ILOGBNAN
+        } else {
+            i32::MAX
+        }
+    } else {
+        e - F::EXP_BIAS as i32
+    }
+}
diff --git a/libm/src/math/generic/mod.rs b/libm/src/math/generic/mod.rs
index 35846351a..9812452bc 100644
--- a/libm/src/math/generic/mod.rs
+++ b/libm/src/math/generic/mod.rs
@@ -13,6 +13,8 @@ mod fmin;
 mod fminimum;
 mod fminimum_num;
 mod fmod;
+mod frexp;
+mod ilogb;
 mod rint;
 mod round;
 mod scalbn;
@@ -31,6 +33,8 @@ pub use fmin::fmin;
 pub use fminimum::fminimum;
 pub use fminimum_num::fminimum_num;
 pub use fmod::fmod;
+pub use frexp::frexp;
+pub use ilogb::ilogb;
 pub use rint::rint_round;
 pub use round::round;
 pub use scalbn::scalbn;
diff --git a/libm/src/math/ilogb.rs b/libm/src/math/ilogb.rs
index 5b41f7b1d..c1427476f 100644
--- a/libm/src/math/ilogb.rs
+++ b/libm/src/math/ilogb.rs
@@ -1,32 +1,25 @@
-const FP_ILOGBNAN: i32 = -1 - 0x7fffffff;
-const FP_ILOGB0: i32 = FP_ILOGBNAN;
+/// Extract the binary exponent of `x`.
+#[cfg(f16_enabled)]
+#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
+pub fn ilogbf16(x: f16) -> i32 {
+    super::generic::ilogb(x)
+}
 
+/// Extract the binary exponent of `x`.
+#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
+pub fn ilogbf(x: f32) -> i32 {
+    super::generic::ilogb(x)
+}
+
+/// Extract the binary exponent of `x`.
 #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
 pub fn ilogb(x: f64) -> i32 {
-    let mut i: u64 = x.to_bits();
-    let e = ((i >> 52) & 0x7ff) as i32;
+    super::generic::ilogb(x)
+}
 
-    if e == 0 {
-        i <<= 12;
-        if i == 0 {
-            force_eval!(0.0 / 0.0);
-            return FP_ILOGB0;
-        }
-        /* subnormal x */
-        let mut e = -0x3ff;
-        while (i >> 63) == 0 {
-            e -= 1;
-            i <<= 1;
-        }
-        e
-    } else if e == 0x7ff {
-        force_eval!(0.0 / 0.0);
-        if (i << 12) != 0 {
-            FP_ILOGBNAN
-        } else {
-            i32::MAX
-        }
-    } else {
-        e - 0x3ff
-    }
+/// Extract the binary exponent of `x`.
+#[cfg(f128_enabled)]
+#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
+pub fn ilogbf128(x: f128) -> i32 {
+    super::generic::ilogb(x)
 }
diff --git a/libm/src/math/ilogbf.rs b/libm/src/math/ilogbf.rs
deleted file mode 100644
index 3585d6d36..000000000
--- a/libm/src/math/ilogbf.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-const FP_ILOGBNAN: i32 = -1 - 0x7fffffff;
-const FP_ILOGB0: i32 = FP_ILOGBNAN;
-
-#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
-pub fn ilogbf(x: f32) -> i32 {
-    let mut i = x.to_bits();
-    let e = ((i >> 23) & 0xff) as i32;
-
-    if e == 0 {
-        i <<= 9;
-        if i == 0 {
-            force_eval!(0.0 / 0.0);
-            return FP_ILOGB0;
-        }
-        /* subnormal x */
-        let mut e = -0x7f;
-        while (i >> 31) == 0 {
-            e -= 1;
-            i <<= 1;
-        }
-        e
-    } else if e == 0xff {
-        force_eval!(0.0 / 0.0);
-        if (i << 9) != 0 { FP_ILOGBNAN } else { i32::MAX }
-    } else {
-        e - 0x7f
-    }
-}
diff --git a/libm/src/math/mod.rs b/libm/src/math/mod.rs
index 949c18b40..30fc0e0c4 100644
--- a/libm/src/math/mod.rs
+++ b/libm/src/math/mod.rs
@@ -165,11 +165,9 @@ mod fminimum_fmaximum;
 mod fminimum_fmaximum_num;
 mod fmod;
 mod frexp;
-mod frexpf;
 mod hypot;
 mod hypotf;
 mod ilogb;
-mod ilogbf;
 mod j0;
 mod j0f;
 mod j1;
@@ -260,12 +258,10 @@ 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, fmodf};
-pub use self::frexp::frexp;
-pub use self::frexpf::frexpf;
+pub use self::frexp::{frexp, frexpf};
 pub use self::hypot::hypot;
 pub use self::hypotf::hypotf;
-pub use self::ilogb::ilogb;
-pub use self::ilogbf::ilogbf;
+pub use self::ilogb::{ilogb, ilogbf};
 pub use self::j0::{j0, y0};
 pub use self::j0f::{j0f, y0f};
 pub use self::j1::{j1, y1};
@@ -326,6 +322,8 @@ cfg_if! {
         pub use self::fminimum_fmaximum::{fmaximumf16, fminimumf16};
         pub use self::fminimum_fmaximum_num::{fmaximum_numf16, fminimum_numf16};
         pub use self::fmod::fmodf16;
+        pub use self::frexp::frexpf16;
+        pub use self::ilogb::ilogbf16;
         pub use self::ldexp::ldexpf16;
         pub use self::rint::rintf16;
         pub use self::round::roundf16;
@@ -353,6 +351,8 @@ cfg_if! {
         pub use self::fminimum_fmaximum::{fmaximumf128, fminimumf128};
         pub use self::fminimum_fmaximum_num::{fmaximum_numf128, fminimum_numf128};
         pub use self::fmod::fmodf128;
+        pub use self::frexp::frexpf128;
+        pub use self::ilogb::ilogbf128;
         pub use self::ldexp::ldexpf128;
         pub use self::rint::rintf128;
         pub use self::round::roundf128;