+use crate::{
+ f16::F16,
+ ieee754::FloatEncoding,
+ traits::{Compare, Context, ConvertTo, Float, Select},
+};
+
+pub const DEFAULT_NAN_RESULT: i16 = i16::MIN + 1;
+pub const DEFAULT_OVERFLOW_RESULT: i16 = i16::MAX;
+pub const DEFAULT_UNDERFLOW_RESULT: i16 = i16::MIN;
+
+macro_rules! impl_ilogb {
+ (
+ #[prim = $prim:ident]
+ #[prim_signed_bits = $prim_signed_bits:ident]
+ #[ilogb = $ilogb:ident]
+ #[nan = $NAN_RESULT:ident]
+ #[overflow = $OVERFLOW_RESULT:ident]
+ #[underflow = $UNDERFLOW_RESULT:ident]
+ fn $ilogb_extended:ident($vector_float:ident, $scalar_signed_bits:ident) -> $vector_signed_bits:ident;
+ ) => {
+ pub const $NAN_RESULT: $prim_signed_bits = $prim_signed_bits::MIN + 1;
+ pub const $OVERFLOW_RESULT: $prim_signed_bits = $prim_signed_bits::MAX;
+ pub const $UNDERFLOW_RESULT: $prim_signed_bits = $prim_signed_bits::MIN;
+
+ pub fn $ilogb_extended<Ctx: Context>(
+ ctx: Ctx,
+ arg: Ctx::$vector_float,
+ nan_result: Ctx::$scalar_signed_bits,
+ overflow_result: Ctx::$scalar_signed_bits,
+ underflow_result: Ctx::$scalar_signed_bits,
+ ) -> Ctx::$vector_signed_bits {
+ let is_finite = arg.is_finite();
+ let is_zero_subnormal = arg.is_zero_or_subnormal();
+ let is_nan = arg.is_nan();
+ let inf_nan_result: Ctx::$vector_signed_bits =
+ is_nan.select(nan_result.into(), overflow_result.into());
+ let scale_factor: $prim = (1u64 << $prim::MANTISSA_FIELD_WIDTH).to();
+ let scaled = arg * ctx.make(scale_factor);
+ let scaled_exponent = scaled.extract_exponent_unbiased();
+ let exponent = arg.extract_exponent_unbiased();
+ let normal_inf_nan_result = is_finite.select(exponent, inf_nan_result);
+ let is_zero = arg.eq(ctx.make($prim::from(0u8)));
+ let zero_subnormal_result = is_zero.select(
+ underflow_result.into(),
+ scaled_exponent - ctx.make($prim::MANTISSA_FIELD_WIDTH.to()),
+ );
+ is_zero_subnormal.select(zero_subnormal_result, normal_inf_nan_result)
+ }
+
+ pub fn $ilogb<Ctx: Context>(ctx: Ctx, arg: Ctx::$vector_float) -> Ctx::$vector_signed_bits {
+ $ilogb_extended(
+ ctx,
+ arg,
+ ctx.make($NAN_RESULT),
+ ctx.make($OVERFLOW_RESULT),
+ ctx.make($UNDERFLOW_RESULT),
+ )
+ }
+ };
+}
+
+impl_ilogb! {
+ #[prim = F16]
+ #[prim_signed_bits = i16]
+ #[ilogb = ilogb_f16]
+ #[nan = ILOGB_NAN_RESULT_F16]
+ #[overflow = ILOGB_OVERFLOW_RESULT_F16]
+ #[underflow = ILOGB_UNDERFLOW_RESULT_F16]
+ fn ilogb_f16_extended(VecF16, I16) -> VecI16;
+}
+
+impl_ilogb! {
+ #[prim = f32]
+ #[prim_signed_bits = i32]
+ #[ilogb = ilogb_f32]
+ #[nan = ILOGB_NAN_RESULT_F32]
+ #[overflow = ILOGB_OVERFLOW_RESULT_F32]
+ #[underflow = ILOGB_UNDERFLOW_RESULT_F32]
+ fn ilogb_f32_extended(VecF32, I32) -> VecI32;
+}
+
+impl_ilogb! {
+ #[prim = f64]
+ #[prim_signed_bits = i64]
+ #[ilogb = ilogb_f64]
+ #[nan = ILOGB_NAN_RESULT_F64]
+ #[overflow = ILOGB_OVERFLOW_RESULT_F64]
+ #[underflow = ILOGB_UNDERFLOW_RESULT_F64]
+ fn ilogb_f64_extended(VecF64, I64) -> VecI64;
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::scalar::Scalar;
+
+ #[test]
+ fn test_ilogb_f32() {
+ assert_eq!(ilogb_f32(Scalar, 0f32), ILOGB_UNDERFLOW_RESULT_F32);
+ assert_eq!(ilogb_f32(Scalar, 1f32), 0);
+ assert_eq!(ilogb_f32(Scalar, 2f32), 1);
+ assert_eq!(ilogb_f32(Scalar, 3f32), 1);
+ assert_eq!(ilogb_f32(Scalar, 3.99999f32), 1);
+ assert_eq!(ilogb_f32(Scalar, 0.5f32), -1);
+ assert_eq!(ilogb_f32(Scalar, 0.5f32.powi(130)), -130);
+ assert_eq!(ilogb_f32(Scalar, f32::INFINITY), ILOGB_OVERFLOW_RESULT_F32);
+ assert_eq!(ilogb_f32(Scalar, f32::NAN), ILOGB_NAN_RESULT_F32);
+ }
+
+ #[test]
+ fn test_ilogb_f64() {
+ assert_eq!(ilogb_f64(Scalar, 0f64), ILOGB_UNDERFLOW_RESULT_F64);
+ assert_eq!(ilogb_f64(Scalar, 1f64), 0);
+ assert_eq!(ilogb_f64(Scalar, 2f64), 1);
+ assert_eq!(ilogb_f64(Scalar, 3f64), 1);
+ assert_eq!(ilogb_f64(Scalar, 3.99999f64), 1);
+ assert_eq!(ilogb_f64(Scalar, 0.5f64), -1);
+ assert_eq!(ilogb_f64(Scalar, 0.5f64.powi(1030)), -1030);
+ assert_eq!(ilogb_f64(Scalar, f64::INFINITY), ILOGB_OVERFLOW_RESULT_F64);
+ assert_eq!(ilogb_f64(Scalar, f64::NAN), ILOGB_NAN_RESULT_F64);
+ }
+}