use crate::{
binary::{
BinaryBuf,
BinaryExponent,
MostSignificantDigit,
},
num::Integer,
};
use core::iter;
const SIGN_NEGATIVE: u8 = 0b1000_0000u8;
const INFINITY: u8 = 0b0111_1000;
const INFINITY_COMBINATION: u8 = 0b0111_1110u8;
const SIGNALING: u8 = 0b0000_0010u8;
const NAN: u8 = 0b0111_1100u8;
const NAN_COMBINATION: u8 = NAN | SIGNALING;
const FINITE_COMBINATION: u8 = 0b0111_1000u8;
pub fn encode_combination_infinity<D: BinaryBuf>(decimal: &mut D, is_infinity_negative: bool) {
let buf = decimal.bytes_mut();
if is_infinity_negative {
buf[buf.len() - 1] = INFINITY | SIGN_NEGATIVE;
} else {
buf[buf.len() - 1] = INFINITY;
}
}
pub fn encode_combination_nan<D: BinaryBuf>(
decimal: &mut D,
is_nan_negative: bool,
is_nan_signaling: bool,
) {
let buf = decimal.bytes_mut();
let sign_bit = if is_nan_negative { SIGN_NEGATIVE } else { 0 };
let signaling_bit = if is_nan_signaling { SIGNALING } else { 0 };
buf[buf.len() - 1] = NAN | sign_bit | signaling_bit;
}
pub fn encode_combination_finite<D: BinaryBuf>(
decimal: &mut D,
significand_is_negative: bool,
unbiased_integer_exponent: D::Exponent,
most_significant_digit: MostSignificantDigit,
) {
let biased_exponent = unbiased_integer_exponent.bias(&*decimal);
debug_assert!(
!biased_exponent.is_negative(),
"biased exponents must always be positive"
);
let exponent = biased_exponent.to_le_bytes();
let exponent_bits = decimal.exponent_width_bits();
let decimal_bit_index = decimal.trailing_significand_width_bits();
let buf = decimal.bytes_mut();
let decimal_byte_shift = (decimal_bit_index % 8) as u32;
let mut decimal_byte_index = decimal_bit_index / 8;
let mut exponent_byte_index = 0;
let max_decimal_byte_index = buf.len() - 1;
if decimal_byte_shift == 0 {
while decimal_byte_index < max_decimal_byte_index {
buf[decimal_byte_index] = exponent[exponent_byte_index];
decimal_byte_index += 1;
exponent_byte_index += 1;
}
}
else {
let decimal_byte_plus_1_shift = 8 - decimal_byte_shift;
while decimal_byte_index < max_decimal_byte_index {
buf[decimal_byte_index] |= exponent[exponent_byte_index] << decimal_byte_shift;
buf[decimal_byte_index + 1] |=
exponent[exponent_byte_index] >> decimal_byte_plus_1_shift;
decimal_byte_index += 1;
exponent_byte_index += 1;
}
}
buf[decimal_byte_index] |= exponent[exponent_byte_index] << decimal_byte_shift;
let (most_significant_exponent_offset, most_significant_exponent_index) =
most_significant_exponent_offset(exponent_bits);
let most_significant_exponent =
exponent[most_significant_exponent_index] >> (most_significant_exponent_offset - 2);
let most_significant_digit = most_significant_digit.get_bcd();
debug_assert_ne!(
0b0000_0011, most_significant_exponent,
"an in-range exponent should never have `11` as its most significant bits ({}bit decimal with exponent {})",
decimal.storage_width_bits(),
unbiased_integer_exponent.as_display(),
);
const C0: u8 = 0b0000_0001;
const C1: u8 = 0b0000_0010;
const C2: u8 = 0b0000_0100;
const C3: u8 = 0b0000_1000;
const COMBINATION_MASK: u8 = 0b1000_0011;
let combination = match most_significant_digit & C3 {
0 => {
let a = (most_significant_exponent & C1) << 5;
let b = (most_significant_exponent & C0) << 5;
let c = (most_significant_digit & C2) << 2;
let d = (most_significant_digit & C1) << 2;
let e = (most_significant_digit & C0) << 2;
a | b | c | d | e
}
C3 => {
let a = C0 << 6;
let b = C0 << 5;
let c = (most_significant_exponent & C1) << 3;
let d = (most_significant_exponent & C0) << 3;
let e = (most_significant_digit & C0) << 2;
a | b | c | d | e
}
_ => unreachable!(),
};
buf[decimal_byte_index] = (buf[decimal_byte_index] & COMBINATION_MASK) | combination;
if significand_is_negative {
buf[decimal_byte_index] |= SIGN_NEGATIVE;
}
}
pub fn decode_combination_finite<D: BinaryBuf>(decimal: &D) -> (D::Exponent, MostSignificantDigit) {
let exponent_bits = decimal.exponent_width_bits();
let decimal_bit_index = decimal.trailing_significand_width_bits();
let buf = decimal.bytes();
let decimal_byte_shift = (decimal_bit_index % 8) as u32;
let mut decimal_byte_index = decimal_bit_index / 8;
let mut exponent_byte_index = 0;
let max_decimal_byte_index = buf.len() - 1;
let (most_significant_exponent, max_exponent_byte_index, most_significant_digit) = {
let (most_significant_exponent_offset, most_significant_exponent_index) =
most_significant_exponent_offset(exponent_bits);
const C0: u8 = 0b0100_0000;
const C1: u8 = 0b0010_0000;
const C2: u8 = 0b0001_0000;
const C3: u8 = 0b0000_1000;
const C4: u8 = 0b0000_0100;
const COMBINATION_MASK: u8 = 0b0110_0000;
let combination = buf[max_decimal_byte_index];
let (most_significant_exponent, most_significant_digit_bcd) =
match combination & COMBINATION_MASK {
COMBINATION_MASK => {
let e0 = (combination & C2) >> 3;
let e1 = (combination & C3) >> 3;
let d0 = C3;
let d1 = 0;
let d2 = 0;
let d3 = (combination & C4) >> 2;
let e = e0 | e1;
let d = d0 | d1 | d2 | d3;
(e, d)
}
_ => {
let e0 = (combination & C0) >> 5;
let e1 = (combination & C1) >> 5;
let d0 = 0;
let d1 = (combination & C2) >> 2;
let d2 = (combination & C3) >> 2;
let d3 = (combination & C4) >> 2;
let e = e0 | e1;
let d = d0 | d1 | d2 | d3;
(e, d)
}
};
(
most_significant_exponent << (most_significant_exponent_offset - 2),
most_significant_exponent_index,
most_significant_digit_bcd,
)
};
const COMBINATION_MASK: u8 = 0b0000_0011;
let biased_exponent = if decimal_byte_shift == 0 {
D::Exponent::from_le_bytes(iter::from_fn(|| {
if decimal_byte_index < max_decimal_byte_index {
let e0 = buf[decimal_byte_index];
decimal_byte_index += 1;
Some(e0)
}
else if decimal_byte_index == max_decimal_byte_index {
let e0 = buf[decimal_byte_index] & COMBINATION_MASK;
let e1 = most_significant_exponent;
decimal_byte_index += 1;
Some(e0 | e1)
}
else {
None
}
}))
}
else {
let decimal_byte_plus_1_shift = 8 - decimal_byte_shift;
D::Exponent::from_le_bytes(iter::from_fn(|| {
if decimal_byte_index + 1 < max_decimal_byte_index {
let e0 = buf[decimal_byte_index] >> decimal_byte_shift;
let e1 = buf[decimal_byte_index + 1] << decimal_byte_plus_1_shift;
decimal_byte_index += 1;
exponent_byte_index += 1;
Some(e0 | e1)
}
else if decimal_byte_index + 1 == max_decimal_byte_index {
let e0 = buf[decimal_byte_index] >> decimal_byte_shift;
let mut e1 =
(buf[decimal_byte_index + 1] & COMBINATION_MASK) << decimal_byte_plus_1_shift;
if exponent_byte_index == max_exponent_byte_index {
e1 |= most_significant_exponent;
}
decimal_byte_index += 1;
exponent_byte_index += 1;
Some(e0 | e1)
}
else if exponent_byte_index == max_exponent_byte_index {
exponent_byte_index += 1;
Some(most_significant_exponent)
}
else {
None
}
}))
};
debug_assert!(
!biased_exponent.is_negative(),
"biased exponents must always be positive"
);
(
biased_exponent.unbias(decimal),
MostSignificantDigit::from_bcd(most_significant_digit),
)
}
fn most_significant_exponent_offset(exponent_bits: usize) -> (u32, usize) {
let offset = exponent_bits % 8;
if offset == 0 {
(8u32, (exponent_bits / 8) - 1)
} else {
(offset as u32, exponent_bits / 8)
}
}
pub fn is_finite<D: BinaryBuf>(decimal: &D) -> bool {
let buf = decimal.bytes();
buf[buf.len() - 1] & FINITE_COMBINATION != FINITE_COMBINATION
}
pub fn is_infinite<D: BinaryBuf>(decimal: &D) -> bool {
let buf = decimal.bytes();
buf[buf.len() - 1] & INFINITY_COMBINATION == INFINITY
}
pub fn is_nan<D: BinaryBuf>(decimal: &D) -> bool {
let buf = decimal.bytes();
buf[buf.len() - 1] & NAN == NAN
}
pub fn is_quiet_nan<D: BinaryBuf>(decimal: &D) -> bool {
let buf = decimal.bytes();
buf[buf.len() - 1] & NAN_COMBINATION == NAN
}
pub fn is_signaling_nan<D: BinaryBuf>(decimal: &D) -> bool {
let buf = decimal.bytes();
buf[buf.len() - 1] & NAN_COMBINATION == NAN_COMBINATION
}
pub fn is_sign_negative<D: BinaryBuf>(decimal: &D) -> bool {
let buf = decimal.bytes();
buf[buf.len() - 1] & SIGN_NEGATIVE == SIGN_NEGATIVE
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binary::{
emax,
DynamicBinaryBuf,
};
fn encode_decode_case<D: BinaryBuf + Clone>(decimal: D) {
let emin = 1i32
- emax::<i32>(decimal.precision_digits())
- (decimal.precision_digits() as i32 - 1);
let emax = emax::<i32>(decimal.precision_digits()) - (decimal.precision_digits() as i32);
for exp in emin..=emax {
for msd in b'0'..=b'9' {
let mut decimal = decimal.clone();
encode_combination_finite(
&mut decimal,
false,
D::Exponent::from_i32(exp),
MostSignificantDigit::from_ascii(msd),
);
let (decoded_exponent, decoded_msd) = decode_combination_finite(&decimal);
assert_eq!(
msd as char,
decoded_msd.get_ascii() as char,
"most significant digit"
);
assert_eq!(
exp,
decoded_exponent
.to_i32()
.expect("exponent is outside i32 range"),
"exponent"
);
}
}
}
#[test]
fn encode_decode_combination_decimal32_all() {
encode_decode_case(DynamicBinaryBuf::<4>::ZERO);
}
#[test]
fn encode_decode_combination_decimal64_all() {
encode_decode_case(DynamicBinaryBuf::<8>::ZERO);
}
#[test]
fn encode_decode_combination_decimal96_all() {
encode_decode_case(DynamicBinaryBuf::<12>::ZERO);
}
#[test]
fn encode_decode_combination_decimal128_all() {
encode_decode_case(DynamicBinaryBuf::<16>::ZERO);
}
#[test]
fn encode_decode_combination_decimal160_all() {
encode_decode_case(DynamicBinaryBuf::<20>::ZERO);
}
}