#[cfg(not(feature = "std"))]
use core::{
mem::size_of,
ops::{BitAnd, Not, Shl, Shr, Sub},
};
#[cfg(feature = "std")]
use std::{
mem::size_of,
ops::{BitAnd, Not, Shl, Shr, Sub},
};
trait Uint:
Copy
+ Sized
+ BitAnd<Self, Output = Self>
+ Shr<u8, Output = Self>
+ Shl<u8, Output = Self>
+ Not<Output = Self>
+ Sub<Self, Output = Self>
+ From<u8>
{
const MAX: Self;
}
impl Uint for u32 {
const MAX: u32 = u32::max_value();
}
impl Uint for u64 {
const MAX: u64 = u64::max_value();
}
#[inline]
fn split<U: Uint>(ibm: U) -> (U, U, U) {
let sign_bit_mask = U::MAX & !(U::MAX >> 1);
let fraction_mask = U::MAX >> 8;
let exponent_mask = !sign_bit_mask & !fraction_mask;
let exponent_shift = (size_of::<U>() * 8 - 8) as u8;
(
ibm & sign_bit_mask,
(ibm & exponent_mask) >> exponent_shift,
ibm & fraction_mask,
)
}
pub fn ibm32ieee32(ibm: u32) -> u32 {
let (sign, ibm_exponent, ibm_fraction) = split(ibm);
if ibm_fraction == 0 {
return sign;
}
let (ibm_exponent, ibm_fraction) = {
let shift = ibm_fraction.leading_zeros() as i32 - 8;
((ibm_exponent << 2) as i32 - shift, ibm_fraction << shift)
};
let ieee_exponent = ibm_exponent - 131;
if ieee_exponent >= 254 {
sign.wrapping_add(0x7f80_0000)
} else if ieee_exponent >= 0 {
let ieee_frac = ibm_fraction;
sign.wrapping_add((ieee_exponent as u32) << 23)
.wrapping_add(ieee_frac)
} else if ieee_exponent >= -32 {
let mask = !((0xffff_fffd) << (-1 - ieee_exponent) as u32);
let round_up = if ibm_fraction & mask > 0 { 1 } else { 0 };
let ieee_frac =
(ibm_fraction >> ((-1i32) - ieee_exponent) as u32).wrapping_add(round_up) >> 1;
sign.wrapping_add(ieee_frac)
} else {
sign
}
}
pub fn ibm32ieee64(ibm: u32) -> u64 {
let (sign, ibm_exponent, ibm_fraction) = split(ibm);
let sign = (sign as u64) << 32;
if ibm_fraction == 0 {
return sign;
}
let (ibm_exponent, ibm_fraction) = {
let shift = ibm_fraction.leading_zeros() as i32 - 8;
((ibm_exponent << 2) as i32 - shift, ibm_fraction << shift)
};
let ieee_exponent = ibm_exponent + 765;
let ieee_fraction = (ibm_fraction as u64) << 29;
sign.wrapping_add((ieee_exponent as u64) << 52)
.wrapping_add(ieee_fraction)
}
pub fn ibm64ieee32(ibm: u64) -> u32 {
let (sign, ibm_exponent, ibm_fraction) = split(ibm);
let sign = (sign >> 32) as u32;
if ibm_fraction == 0 {
return sign;
}
let (ibm_exponent, ibm_fraction) = {
let shift = ibm_fraction.leading_zeros() as i32 - 8;
((ibm_exponent << 2) as i32 - shift, ibm_fraction << shift)
};
let ieee_exponent = ibm_exponent - 131;
if ieee_exponent >= 254 {
sign.wrapping_add(0x7f80_0000)
} else if ieee_exponent >= 0 {
let round_up = if ibm_fraction & 0x0001_7fff_ffff > 0 {
1
} else {
0
};
let ieee_frac = ((ibm_fraction >> 31).wrapping_add(round_up) >> 1) as u32;
sign.wrapping_add((ieee_exponent as u32) << 23)
.wrapping_add(ieee_frac)
} else if ieee_exponent >= -32 {
let mask: u64 = !(0xffff_ffff_ffff_fffdu64 << (31 - ieee_exponent) as u64);
let round_up: u32 = if ibm_fraction & mask > 0 { 1 } else { 0 };
let ieee_frac = ((ibm_fraction >> (31 - ieee_exponent) as u64)
.wrapping_add(round_up as u64)
>> 1) as u32;
sign.wrapping_add(ieee_frac)
} else {
sign
}
}
pub fn ibm64ieee64(ibm: u64) -> u64 {
let (sign, ibm_exponent, ibm_fraction) = split(ibm);
if ibm_fraction == 0 {
return sign;
}
let (ibm_exponent, ibm_fraction) = {
let shift = ibm_fraction.leading_zeros() as i32 - 8;
((ibm_exponent << 2) as i32 - shift, ibm_fraction << shift)
};
let ieee_exponent = ibm_exponent + 765;
let round_up = if (ibm_fraction & 0xb) > 0 { 1 } else { 0 };
let ieee_fraction = (ibm_fraction >> 2).wrapping_add(round_up) >> 1;
sign.wrapping_add((ieee_exponent as u64) << 52)
.wrapping_add(ieee_fraction)
}
#[cfg(test)]
mod tests;