type Inner = u32;
const K_WIDTH: Inner = Inner::BITS;
const P_PRECISION: Inner = 9 * K_WIDTH / 32 - 2;
const E_EMAX: Inner = 3 * (2 as Inner).pow(K_WIDTH / 16 + 3);
const BIAS: Inner = E_EMAX + P_PRECISION - 2;
const SIGN_OFFSET: Inner = 0;
const SIGN_WIDTH: Inner = 1;
const SIGN_MASK: Inner = 1 << ((K_WIDTH - SIGN_WIDTH) - SIGN_OFFSET);
const SIGN_NEGATIVE: Inner = SIGN_MASK & Inner::MAX;
const SIGN_POSITIVE: Inner = SIGN_MASK & Inner::MIN;
const COMBINATION_FIELD_OFFSET: Inner = SIGN_WIDTH;
const COMBINATION_FIELD_WIDTH: Inner = K_WIDTH / 16 + 9;
const COMBINATION_TOP_BIT_SHIFT_AMOUNT: Inner = TRAILING_FIELD_WIDTH + COMBINATION_FIELD_WIDTH;
const COMBINATION_FIELD_MASK: Inner = ((1 << COMBINATION_FIELD_WIDTH) - 1)
<< ((K_WIDTH - COMBINATION_FIELD_WIDTH) - COMBINATION_FIELD_OFFSET);
const COMBINATION_INFINITY_MASK: Inner = (0b1_1111) << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 5);
const COMBINATION_INFINITY: Inner = 0b01_1110 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 5);
const COMBINATION_NAN_MASK: Inner = 0b011_1111 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 6);
const COMBINATION_NAN_QUIET: Inner = 0b011_1110 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 6);
const COMBINATION_NAN_SIGNALING: Inner = 0b011_1111 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 6);
const COMBINATION_BIG_FIRST_DIGIT_INDICATOR_MASK: Inner =
0b1111 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 4);
const COMBINATION_BIG_FIRST_DIGIT_INDICATOR_V1: Inner =
0b1110 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 4);
const COMBINATION_BIG_FIRST_DIGIT_INDICATOR_V2: Inner =
0b1101 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 4);
const COMBINATION_BIG_FIRST_E_UP_MASK: Inner = 0b0011 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 4);
const COMBINATION_SMALL_FIRST_E_UP_MASK: Inner = 0b11 << (COMBINATION_TOP_BIT_SHIFT_AMOUNT - 2);
const COMBINATION_DOWN_MASK: Inner = ((1 << (COMBINATION_FIELD_WIDTH - 5)) - 1)
<< ((K_WIDTH - COMBINATION_FIELD_WIDTH) - COMBINATION_FIELD_OFFSET);
const TRAILING_FIELD_OFFSET: Inner = COMBINATION_FIELD_OFFSET + COMBINATION_FIELD_WIDTH;
const TRAILING_FIELD_WIDTH: Inner = 15 * K_WIDTH / 16 - 10;
const TRAILING_FIELD_MASK: Inner =
((1 << TRAILING_FIELD_WIDTH) - 1) << ((K_WIDTH - TRAILING_FIELD_WIDTH) - TRAILING_FIELD_OFFSET);
#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
struct D32BPB(Inner);
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DecimalCategory {
QuietNaN,
SignalingNaN,
Infinite,
Finite,
}
impl D32BPB {
pub(crate) fn exponent(self) -> Option<i64> {
self.biased_exponent()
.map(|inner| i64::from(inner) - i64::from(BIAS))
}
pub(crate) const fn biased_exponent(self) -> Option<Inner> {
match self.category() {
DecimalCategory::Infinite
| DecimalCategory::SignalingNaN
| DecimalCategory::QuietNaN => None,
DecimalCategory::Finite => Some(
(if self.is_finite_with_big_first_digit() {
((self.0) & COMBINATION_BIG_FIRST_E_UP_MASK) >> 1
} else {
((self.0) & COMBINATION_SMALL_FIRST_E_UP_MASK) >> 3
} | ((self.0) & COMBINATION_DOWN_MASK))
>> TRAILING_FIELD_WIDTH,
),
}
}
const fn is_finite_with_big_first_digit(self) -> bool {
((self.0 & COMBINATION_BIG_FIRST_DIGIT_INDICATOR_MASK)
== COMBINATION_BIG_FIRST_DIGIT_INDICATOR_V1)
|| ((self.0 & COMBINATION_BIG_FIRST_DIGIT_INDICATOR_MASK)
== COMBINATION_BIG_FIRST_DIGIT_INDICATOR_V2)
}
pub(crate) fn nan_signal(self) -> Option<Inner> {
if self.category() == DecimalCategory::SignalingNaN {
Some(self.0 & TRAILING_FIELD_MASK)
} else {
None
}
}
pub(crate) const fn category(self) -> DecimalCategory {
if (self.0 & COMBINATION_INFINITY_MASK) == COMBINATION_INFINITY {
DecimalCategory::Infinite
} else if (self.0 & COMBINATION_NAN_MASK) == COMBINATION_NAN_QUIET {
DecimalCategory::QuietNaN
} else if (self.0 & COMBINATION_NAN_MASK) == COMBINATION_NAN_SIGNALING {
DecimalCategory::SignalingNaN
} else {
DecimalCategory::Finite
}
}
pub(crate) const fn is_nan(self) -> bool {
matches!(
self.category(),
DecimalCategory::QuietNaN | DecimalCategory::SignalingNaN
)
}
pub(crate) const fn is_infinity(self) -> bool {
matches!(self.category(), DecimalCategory::Infinite)
}
pub(crate) const fn is_positive(self) -> bool {
match self.category() {
DecimalCategory::Infinite | DecimalCategory::Finite => {
self.0 & SIGN_MASK == SIGN_POSITIVE
}
_ => false,
}
}
const fn is_negative(self) -> bool {
match self.category() {
DecimalCategory::Infinite | DecimalCategory::Finite => {
self.0 & SIGN_MASK == SIGN_NEGATIVE
}
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_represents_positive_infinity() {
let result = D32BPB(0b1_1110 << (Inner::BITS - 6));
assert!(result.is_positive());
assert!(!result.is_negative());
assert!(result.is_infinity());
assert!(!result.is_nan());
assert_eq!(result.category(), DecimalCategory::Infinite);
assert_eq!(result.nan_signal(), None);
assert!(result.biased_exponent().is_none());
}
#[test]
fn it_represents_negative_infinity() {
let result = D32BPB(0b11_1110 << (Inner::BITS - 6));
assert!(!result.is_positive());
assert!(result.is_negative());
assert!(result.is_infinity());
assert!(!result.is_nan());
assert_eq!(result.category(), DecimalCategory::Infinite);
assert_eq!(result.nan_signal(), None);
assert!(result.biased_exponent().is_none());
}
#[test]
fn it_represents_negative_finite_numbers() {
let result = D32BPB(1 << (Inner::BITS - 1));
assert!(result.is_negative());
assert!(!result.is_positive());
assert!(!result.is_nan());
assert_eq!(result.category(), DecimalCategory::Finite);
assert_eq!(result.nan_signal(), None);
assert!(result.biased_exponent().is_some());
}
#[test]
fn it_represents_positive_finite_numbers() {
let result = D32BPB(0);
assert!(!result.is_negative());
assert!(result.is_positive());
assert!(!result.is_nan());
assert_eq!(result.category(), DecimalCategory::Finite);
assert_eq!(result.nan_signal(), None);
assert!(result.biased_exponent().is_some());
}
#[test]
fn it_represents_quiet_nan() {
let result = D32BPB(0b011_1110 << (Inner::BITS - 7));
assert!(!result.is_negative());
assert!(!result.is_positive());
assert!(result.is_nan());
assert_eq!(result.biased_exponent(), None);
assert_eq!(result.category(), DecimalCategory::QuietNaN);
assert_eq!(result.nan_signal(), None);
}
#[test]
fn it_represents_signaling_nan() {
let result = D32BPB((0b011_1111 << (Inner::BITS - 7)) | 0xABCDE);
assert_eq!(result.category(), DecimalCategory::SignalingNaN);
assert!(!result.is_negative());
assert!(!result.is_positive());
assert!(result.is_nan());
assert_eq!(result.biased_exponent(), None);
assert_eq!(result.nan_signal(), Some(0xABCDE));
}
#[test]
fn it_can_get_the_biased_exponent_for_a_finite_number_with_big_first_digit_of_significand() {
let bpb = D32BPB(0b0111_0011_1111 << (Inner::BITS - 12));
let result = bpb.biased_exponent();
assert_eq!(result, Some(0b1011_1111));
}
#[test]
fn it_can_get_the_biased_exponent_for_a_finite_number_with_small_first_digit_of_significand() {
let result = D32BPB(0b0100_0011_1111 << (Inner::BITS - 12));
assert_eq!(result.biased_exponent(), Some(0b1011_1111));
}
#[test]
fn it_can_get_the_exponent_for_a_finite_number_with_big_first_digit_of_significand() {
let result = D32BPB(0b0111_0011_1111 << (Inner::BITS - 12));
assert_eq!(result.exponent(), Some(0b1011_1111_i64 - 101));
}
#[test]
fn it_can_get_the_exponent_for_a_finite_number_with_small_first_digit_of_significand() {
let result = D32BPB(0b0100_0011_1111 << (Inner::BITS - 12));
assert_eq!(result.exponent(), Some(0b1011_1111_i64 - 101));
}
#[test]
fn it_has_a_default_of_0() {
assert_eq!(D32BPB::default(), D32BPB(0));
}
}