6196b2d03d049292ec87ffa720cf4038511c55d6
[vector-math.git] / src / algorithms / ilogb.rs
1 use crate::{
2 f16::F16,
3 ieee754::FloatEncoding,
4 traits::{Compare, Context, ConvertTo, Float, Select},
5 };
6
7 macro_rules! impl_ilogb {
8 (
9 #[prim = $prim:ident]
10 #[prim_signed_bits = $prim_signed_bits:ident]
11 #[ilogb = $ilogb:ident]
12 #[nan = $NAN_RESULT:ident]
13 #[overflow = $OVERFLOW_RESULT:ident]
14 #[underflow = $UNDERFLOW_RESULT:ident]
15 fn $ilogb_extended:ident($vector_float:ident, $scalar_signed_bits:ident) -> $vector_signed_bits:ident;
16 ) => {
17 pub const $NAN_RESULT: $prim_signed_bits = $prim_signed_bits::MIN + 1;
18 pub const $OVERFLOW_RESULT: $prim_signed_bits = $prim_signed_bits::MAX;
19 pub const $UNDERFLOW_RESULT: $prim_signed_bits = $prim_signed_bits::MIN;
20
21 pub fn $ilogb_extended<Ctx: Context>(
22 ctx: Ctx,
23 arg: Ctx::$vector_float,
24 nan_result: Ctx::$scalar_signed_bits,
25 overflow_result: Ctx::$scalar_signed_bits,
26 underflow_result: Ctx::$scalar_signed_bits,
27 ) -> Ctx::$vector_signed_bits {
28 let is_finite = arg.is_finite();
29 let is_zero_subnormal = arg.is_zero_or_subnormal();
30 let is_nan = arg.is_nan();
31 let inf_nan_result: Ctx::$vector_signed_bits =
32 is_nan.select(nan_result.into(), overflow_result.into());
33 let scale_factor: $prim = (1u64 << $prim::MANTISSA_FIELD_WIDTH).to();
34 let scaled = arg * ctx.make(scale_factor);
35 let scaled_exponent = scaled.extract_exponent_unbiased();
36 let exponent = arg.extract_exponent_unbiased();
37 let normal_inf_nan_result = is_finite.select(exponent, inf_nan_result);
38 let is_zero = arg.eq(ctx.make($prim::from(0u8)));
39 let zero_subnormal_result = is_zero.select(
40 underflow_result.into(),
41 scaled_exponent - ctx.make($prim::MANTISSA_FIELD_WIDTH.to()),
42 );
43 is_zero_subnormal.select(zero_subnormal_result, normal_inf_nan_result)
44 }
45
46 pub fn $ilogb<Ctx: Context>(ctx: Ctx, arg: Ctx::$vector_float) -> Ctx::$vector_signed_bits {
47 $ilogb_extended(
48 ctx,
49 arg,
50 ctx.make($NAN_RESULT),
51 ctx.make($OVERFLOW_RESULT),
52 ctx.make($UNDERFLOW_RESULT),
53 )
54 }
55 };
56 }
57
58 impl_ilogb! {
59 #[prim = F16]
60 #[prim_signed_bits = i16]
61 #[ilogb = ilogb_f16]
62 #[nan = ILOGB_NAN_RESULT_F16]
63 #[overflow = ILOGB_OVERFLOW_RESULT_F16]
64 #[underflow = ILOGB_UNDERFLOW_RESULT_F16]
65 fn ilogb_f16_extended(VecF16, I16) -> VecI16;
66 }
67
68 impl_ilogb! {
69 #[prim = f32]
70 #[prim_signed_bits = i32]
71 #[ilogb = ilogb_f32]
72 #[nan = ILOGB_NAN_RESULT_F32]
73 #[overflow = ILOGB_OVERFLOW_RESULT_F32]
74 #[underflow = ILOGB_UNDERFLOW_RESULT_F32]
75 fn ilogb_f32_extended(VecF32, I32) -> VecI32;
76 }
77
78 impl_ilogb! {
79 #[prim = f64]
80 #[prim_signed_bits = i64]
81 #[ilogb = ilogb_f64]
82 #[nan = ILOGB_NAN_RESULT_F64]
83 #[overflow = ILOGB_OVERFLOW_RESULT_F64]
84 #[underflow = ILOGB_UNDERFLOW_RESULT_F64]
85 fn ilogb_f64_extended(VecF64, I64) -> VecI64;
86 }
87
88 #[cfg(test)]
89 mod tests {
90 use super::*;
91 use crate::scalar::Scalar;
92
93 #[test]
94 #[cfg_attr(
95 not(feature = "f16"),
96 should_panic(expected = "f16 feature is not enabled")
97 )]
98 fn test_ilogb_f16() {
99 fn ilogb(arg: f32) -> i16 {
100 let arg: F16 = arg.to();
101 ilogb_f16(Scalar, arg)
102 }
103 assert_eq!(ilogb(0.), ILOGB_UNDERFLOW_RESULT_F16);
104 assert_eq!(ilogb(1.), 0);
105 assert_eq!(ilogb(2.), 1);
106 assert_eq!(ilogb(3.), 1);
107 assert_eq!(ilogb(3.998), 1);
108 assert_eq!(ilogb(0.5), -1);
109 assert_eq!(ilogb(0.5f32.powi(20)), -20);
110 assert_eq!(ilogb(f32::INFINITY), ILOGB_OVERFLOW_RESULT_F16);
111 assert_eq!(ilogb(f32::NAN), ILOGB_NAN_RESULT_F16);
112 }
113
114 #[test]
115 fn test_ilogb_f32() {
116 assert_eq!(ilogb_f32(Scalar, 0f32), ILOGB_UNDERFLOW_RESULT_F32);
117 assert_eq!(ilogb_f32(Scalar, 1f32), 0);
118 assert_eq!(ilogb_f32(Scalar, 2f32), 1);
119 assert_eq!(ilogb_f32(Scalar, 3f32), 1);
120 assert_eq!(ilogb_f32(Scalar, 3.99999f32), 1);
121 assert_eq!(ilogb_f32(Scalar, 0.5f32), -1);
122 assert_eq!(ilogb_f32(Scalar, 0.5f32.powi(130)), -130);
123 assert_eq!(ilogb_f32(Scalar, f32::INFINITY), ILOGB_OVERFLOW_RESULT_F32);
124 assert_eq!(ilogb_f32(Scalar, f32::NAN), ILOGB_NAN_RESULT_F32);
125 }
126
127 #[test]
128 fn test_ilogb_f64() {
129 assert_eq!(ilogb_f64(Scalar, 0f64), ILOGB_UNDERFLOW_RESULT_F64);
130 assert_eq!(ilogb_f64(Scalar, 1f64), 0);
131 assert_eq!(ilogb_f64(Scalar, 2f64), 1);
132 assert_eq!(ilogb_f64(Scalar, 3f64), 1);
133 assert_eq!(ilogb_f64(Scalar, 3.99999f64), 1);
134 assert_eq!(ilogb_f64(Scalar, 0.5f64), -1);
135 assert_eq!(ilogb_f64(Scalar, 0.5f64.powi(1030)), -1030);
136 assert_eq!(ilogb_f64(Scalar, f64::INFINITY), ILOGB_OVERFLOW_RESULT_F64);
137 assert_eq!(ilogb_f64(Scalar, f64::NAN), ILOGB_NAN_RESULT_F64);
138 }
139 }