use core::cmp::Ordering;
use dashu_int::{DoubleWord, IBig, Word};
use crate::repr::Repr;
use crate::utils::log2_base;
const MAG_ONE_HALF: Word = 1 << (Word::BITS - 1);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Mag {
man: Word,
exp: i128,
}
impl Mag {
pub const ZERO: Mag = Mag { man: 0, exp: 0 };
pub const INFINITY: Mag = Mag {
man: 0,
exp: i128::MAX,
};
pub const ONE: Mag = Mag {
man: MAG_ONE_HALF,
exp: 1,
};
#[inline]
pub const fn is_zero(&self) -> bool {
self.man == 0 && self.exp == 0
}
#[inline]
pub const fn is_infinite(&self) -> bool {
self.man == 0 && self.exp != 0
}
#[inline]
const fn is_special(&self) -> bool {
self.man == 0
}
pub(crate) const fn from_pow2(exp: isize) -> Mag {
Mag::from_pow2_stored(exp.saturating_add(1) as i128)
}
const fn from_pow2_stored(e: i128) -> Mag {
if e == i128::MAX {
Mag::INFINITY
} else {
Mag {
man: MAG_ONE_HALF,
exp: e,
}
}
}
pub(crate) fn from_base_pow<const B: Word>(exp: isize) -> Mag {
if B == 2 {
Mag::from_pow2(exp) } else {
Mag::ONE.scale_by_base_pow::<B>(exp, true)
}
}
pub(crate) fn from_word(x: Word) -> Mag {
if x == 0 {
Mag::ZERO
} else {
let n = Word::BITS - x.leading_zeros(); build(x << (Word::BITS - n), n as i128)
}
}
pub(crate) fn from_int(n: &IBig) -> Mag {
significand_bound(n, true)
}
pub fn from_repr<const B: Word>(repr: &Repr<B>) -> Mag {
if repr.significand().is_zero() {
return if repr.is_infinite() {
Mag::INFINITY
} else {
Mag::ZERO
};
}
significand_bound(repr.significand(), true).scale_by_base_pow::<B>(repr.exponent(), true)
}
pub fn from_repr_lower<const B: Word>(repr: &Repr<B>) -> Mag {
if repr.significand().is_zero() {
return if repr.is_infinite() {
Mag::INFINITY
} else {
Mag::ZERO
};
}
significand_bound(repr.significand(), false).scale_by_base_pow::<B>(repr.exponent(), false)
}
pub fn add(&self, other: &Mag) -> Mag {
if self.is_zero() {
return *other;
}
if other.is_zero() {
return *self;
}
if self.is_infinite() || other.is_infinite() {
return Mag::INFINITY;
}
add_up(self.man, self.exp, other.man, other.exp)
}
pub fn mul(&self, other: &Mag) -> Mag {
if self.is_zero() || other.is_zero() {
Mag::ZERO
} else if self.is_infinite() || other.is_infinite() {
Mag::INFINITY
} else {
let prod = (self.man as DoubleWord) * (other.man as DoubleWord);
let man = ((prod >> Word::BITS) + 1) as Word;
finish_fixmul(man, self.exp.saturating_add(other.exp))
}
}
pub fn div(&self, other: &Mag) -> Mag {
if self.is_special() || other.is_special() {
if other.is_zero() || self.is_infinite() {
Mag::INFINITY
} else {
Mag::ZERO
}
} else {
let q = (((self.man as DoubleWord) << Word::BITS) / (other.man as DoubleWord)) + 1;
norm_large_up(q, self.exp.saturating_sub(other.exp))
}
}
pub fn mul_pow2(&self, e: isize) -> Mag {
self.mul_pow2_i128(e as i128)
}
pub(crate) fn mul_pow2_i128(&self, e: i128) -> Mag {
if self.is_special() {
*self
} else {
build(self.man, self.exp.saturating_add(e))
}
}
fn pow(&self, n: usize) -> Mag {
if self.is_infinite() {
return Mag::INFINITY;
}
match n {
0 => Mag::ONE,
1 => *self,
2 => self.mul(self),
_ => {
let mut y = *self;
for i in (0..usize_bits(n) as usize - 1).rev() {
y = y.mul(&y);
if (n >> i) & 1 == 1 {
y = y.mul(self);
}
}
y
}
}
}
pub fn sub(&self, other: &Mag) -> Mag {
if self.is_infinite() {
return Mag::INFINITY;
}
if other.is_infinite() {
return Mag::ZERO;
}
if self.is_zero() || other.is_zero() {
return *self; }
sub_impl(self.man, self.exp, other.man, other.exp, true)
}
pub fn sub_down(&self, other: &Mag) -> Mag {
if other.is_zero() {
return *self;
}
if self.is_zero() || other.is_infinite() {
return Mag::ZERO;
}
if self.is_infinite() {
return Mag::INFINITY;
}
sub_impl(self.man, self.exp, other.man, other.exp, false)
}
pub fn mul_down(&self, other: &Mag) -> Mag {
if self.is_zero() || other.is_zero() {
Mag::ZERO
} else if self.is_infinite() || other.is_infinite() {
Mag::INFINITY
} else {
let prod = (self.man as DoubleWord) * (other.man as DoubleWord);
let man = (prod >> Word::BITS) as Word; finish_fixmul(man, self.exp.saturating_add(other.exp))
}
}
pub fn exp_upper(&self) -> Mag {
if self.is_zero() {
return Mag::ONE; }
if self.is_infinite() {
return Mag::INFINITY;
}
if self.exp >= usize::BITS as i128 {
return Mag::INFINITY;
}
let j = self.exp.max(0) as usize;
let v = self.mul_pow2(-(j as isize));
(Mag::ONE.add(&v.mul_pow2(1))).pow(1usize << j)
}
fn scale_by_base_pow<const B: Word>(&self, e: isize, round_up: bool) -> Mag {
if e == 0 || self.is_special() {
return *self;
}
if B == 2 {
return self.mul_pow2(e);
}
let (l, fb) = log2_base::<B>();
let ratio: i128 = if round_up == (e >= 0) {
(l + 1) as i128
} else {
l as i128
};
let k = div_scaled(e, ratio, fb, round_up);
self.mul_pow2_i128(k)
}
pub(crate) fn to_repr<const B: Word>(self) -> Repr<B> {
if self.is_zero() {
return Repr::zero();
}
if self.is_infinite() {
return Repr::infinity();
}
const EXP_LIMIT: i128 = isize::MAX as i128 - 1;
if B == 2 {
let e = self.exp.saturating_sub(Word::BITS as i128);
if e.abs() > EXP_LIMIT {
return Repr::infinity();
}
Repr::new(IBig::from(self.man), e as isize)
} else {
let (l, fb) = log2_base::<B>();
let a = self.exp.saturating_sub(Word::BITS as i128);
if a.abs() > i128::MAX >> (fb + 1) {
return Repr::infinity();
}
let den: i128 = if a >= 0 { l as i128 } else { l as i128 + 1 };
let scaled = a * (1i128 << fb);
let q = scaled.div_euclid(den);
let k = if scaled.rem_euclid(den) != 0 {
q + 1
} else {
q
};
if k.abs() > EXP_LIMIT {
return Repr::infinity();
}
Repr::new(IBig::from(self.man), k as isize)
}
}
}
#[inline]
const fn build(man: Word, exp: i128) -> Mag {
if exp == i128::MAX {
Mag::INFINITY
} else {
Mag { man, exp }
}
}
#[inline]
fn norm_large_up(mut raw: DoubleWord, mut exp: i128) -> Mag {
let top: DoubleWord = (1 as DoubleWord) << Word::BITS;
while raw >= top {
raw = (raw >> 1) + (raw & 1); exp = exp.saturating_add(1);
}
build(raw as Word, exp)
}
#[inline]
fn finish_fixmul(mut man: Word, mut exp: i128) -> Mag {
if man < MAG_ONE_HALF {
man <<= 1;
exp = exp.saturating_sub(1);
}
build(man, exp)
}
fn add_up(mx: Word, ex: i128, my: Word, ey: i128) -> Mag {
let bits = Word::BITS as i128;
let shift = ex.saturating_sub(ey);
if shift >= bits {
norm_large_up(mx as DoubleWord + 1, ex)
} else if shift > 0 {
let raw = mx as DoubleWord + (my >> shift as u32) as DoubleWord + 1;
norm_large_up(raw, ex)
} else if shift == 0 {
norm_large_up(mx as DoubleWord + my as DoubleWord, ex)
} else if shift <= -bits {
norm_large_up(my as DoubleWord + 1, ey)
} else {
let raw = my as DoubleWord + (mx >> (-shift) as u32) as DoubleWord + 1;
norm_large_up(raw, ey)
}
}
fn sub_impl(mx: Word, ex: i128, my: Word, ey: i128, round_up: bool) -> Mag {
let bits = Word::BITS as i128;
if ex < ey {
return Mag::ZERO;
}
let shift = ex.saturating_sub(ey);
if shift == 0 {
if mx <= my {
return Mag::ZERO;
}
from_double_rounded((mx - my) as DoubleWord, round_up)
.mul_pow2_i128(ex.saturating_sub(bits))
} else if shift >= bits {
if round_up {
Mag { man: mx, exp: ex }
} else {
from_double_rounded((mx - 1) as DoubleWord, false)
.mul_pow2_i128(ex.saturating_sub(bits))
}
} else {
let d = ((mx as DoubleWord) << shift as u32) - (my as DoubleWord);
from_double_rounded(d, round_up).mul_pow2_i128(ey.saturating_sub(bits))
}
}
fn from_double_rounded(x: DoubleWord, round_up: bool) -> Mag {
if x == 0 {
return Mag::ZERO;
}
if (x >> Word::BITS) == 0 {
return Mag::from_word(x as Word);
}
let n = 2 * Word::BITS - x.leading_zeros();
let shift = n - Word::BITS;
let hi = (x >> shift) as Word;
let has_low = x & (((1 as DoubleWord) << shift) - 1) != 0;
let mm = (hi as DoubleWord) + ((has_low && round_up) as DoubleWord);
if (mm >> Word::BITS) != 0 {
build(MAG_ONE_HALF, n as i128 + 1)
} else {
build(mm as Word, n as i128)
}
}
fn significand_bound(sig: &IBig, round_up: bool) -> Mag {
let (_, words) = sig.as_sign_words();
let len = words.len();
if len == 0 {
return Mag::ZERO;
}
if len == 1 {
return Mag::from_word(words[0]);
}
let bits = Word::BITS as usize;
let hi = words[len - 1];
let next = words[len - 2];
let bit_len = (len - 1) * bits + (bits - hi.leading_zeros() as usize);
let shift = bits - hi.leading_zeros() as usize; let combined = ((hi as DoubleWord) << bits) | (next as DoubleWord);
let top = (combined >> shift) as Word; let has_low = (combined & (((1 as DoubleWord) << shift) - 1)) != 0
|| words[..len - 2].iter().any(|&w| w != 0);
let mm = (top as DoubleWord) + ((has_low && round_up) as DoubleWord);
if (mm >> bits) != 0 {
build(MAG_ONE_HALF, bit_len as i128 + 1)
} else {
build(mm as Word, bit_len as i128)
}
}
#[inline]
fn usize_bits(n: usize) -> u32 {
usize::BITS - n.leading_zeros()
}
fn div_scaled(a: isize, ratio: i128, frac_bits: u32, up: bool) -> i128 {
let den = 1i128 << frac_bits;
let scaled = (a as i128) * ratio;
let (q, r) = (scaled.div_euclid(den), scaled.rem_euclid(den));
if up && r != 0 {
q + 1
} else {
q
}
}
impl PartialOrd for Mag {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Mag {
fn cmp(&self, other: &Self) -> Ordering {
if self.man == other.man && self.exp == other.exp {
return Ordering::Equal;
}
if self.is_zero() {
return Ordering::Less;
}
if other.is_zero() {
return Ordering::Greater;
}
if self.is_infinite() {
return Ordering::Greater;
}
if other.is_infinite() {
return Ordering::Less;
}
match self.exp.cmp(&other.exp) {
Ordering::Equal => self.man.cmp(&other.man),
ord => ord,
}
}
}
impl core::fmt::Debug for Mag {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.is_zero() {
write!(f, "Mag(0)")
} else if self.is_infinite() {
write!(f, "Mag(inf)")
} else {
write!(f, "Mag({} * 2^{})", self.man, self.exp.saturating_sub(Word::BITS as i128))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::cmp::Ordering::*;
fn dy(m: &Mag) -> (IBig, i128) {
(IBig::from(m.man), m.exp - Word::BITS as i128)
}
fn dyi(n: IBig) -> (IBig, i128) {
(n, 0)
}
fn dycmp(a: (IBig, i128), b: (IBig, i128)) -> Ordering {
if a.1 == b.1 {
a.0.cmp(&b.0)
} else {
let lo = a.1.min(b.1);
(a.0 << (a.1 - lo) as usize).cmp(&(b.0 << (b.1 - lo) as usize))
}
}
fn assert_normalized(ms: &[&Mag]) {
for m in ms {
if !m.is_special() {
assert!(m.man >= MAG_ONE_HALF, "{m:?} not normalized");
}
}
}
#[test]
fn sentinels_and_ordering() {
assert!(Mag::ZERO.is_zero() && !Mag::ZERO.is_infinite());
assert!(Mag::INFINITY.is_infinite() && !Mag::INFINITY.is_zero());
assert!(Mag::ONE == Mag::from_word(1));
assert!(Mag::ZERO < Mag::ONE);
assert!(Mag::ONE < Mag::INFINITY);
assert_eq!(Mag::from_pow2(0), Mag::ONE);
}
#[test]
fn from_word_and_pow2_are_exact() {
for x in [1u64, 2, 3, 7, 1 << 32, (1 << 63) + 1, u64::MAX] {
let x = x as Word;
let m = Mag::from_word(x);
assert_eq!(dycmp(dy(&m), dyi(IBig::from(x))), Equal, "{x}");
}
for e in [-100isize, -1, 0, 1, 63, 100] {
let m = Mag::from_pow2(e);
let exact = (IBig::ONE, e as i128);
assert_eq!(dycmp(dy(&m), exact), Equal, "2^{e}");
}
assert!(!Mag::from_pow2(isize::MAX).is_infinite());
assert!(!Mag::from_pow2(isize::MIN + 1).is_infinite());
assert!(Mag::from_pow2_stored(i128::MAX).is_infinite());
assert_normalized(&[&Mag::from_word(3), &Mag::from_pow2(-5)]);
}
#[test]
fn add_sub_mul_round_up() {
let a = Mag::from_word(3);
let b = Mag::from_word(5);
let tiny = Mag::from_word(1).mul_pow2(-80);
assert!(dycmp(dy(&a.add(&b)), dyi(8.into())) != Less);
assert!(dycmp(dy(&a.mul(&b)), dyi(15.into())) != Less);
assert!(dycmp(dy(&a.mul_down(&b)), dyi(15.into())) != Greater);
let d = b.sub(&a);
assert!(dycmp(dy(&d), dyi(2.into())) != Less);
assert!(a.sub(&b).is_zero());
assert_eq!(a.sub(&tiny), a);
assert!(Mag::ZERO.mul(&Mag::INFINITY).is_zero());
assert!(a.div(&Mag::ZERO).is_infinite());
assert!(Mag::ZERO.div(&a).is_zero());
let big = Mag::from_pow2(100);
assert!(dycmp(dy(&big.add(&a)), dy(&big)) == Greater);
assert!(dycmp(dy(&big.add(&a)), dy(&big.mul_pow2(1))) == Less);
assert_normalized(&[&a.add(&b), &a.mul(&b), &d, &big.add(&a)]);
}
#[test]
fn div_rounds_up() {
let a = Mag::from_word(7);
let b = Mag::from_word(3);
let q = a.div(&b);
let (qm, qe) = dy(&q);
let (bm, be) = dy(&b);
let prod = (qm * bm, qe + be);
assert!(dycmp(prod, dyi(7.into())) != Less);
assert!(dycmp(dy(&q), dyi(4.into())) == Less);
}
#[test]
fn from_int_rounds_up_at_width() {
let small = IBig::from(12345u32);
assert_eq!(dycmp(dy(&Mag::from_int(&small)), dyi(small.clone())), Equal);
let two_words = (IBig::ONE << (Word::BITS as usize + 37)) + IBig::from(0xdeadbeefu32);
let m = Mag::from_int(&two_words);
assert!(dycmp(dy(&m), dyi(two_words.clone())) != Less);
assert_eq!(m.exp, (Word::BITS as i128) + 38);
let three_words = (IBig::ONE << (2 * Word::BITS as usize + 1)) - IBig::ONE;
let m3 = Mag::from_int(&three_words);
assert!(dycmp(dy(&m3), dyi(three_words.clone())) != Less);
assert_normalized(&[&m, &m3]);
}
#[test]
fn from_repr_brackets_base2() {
let sig = (IBig::ONE << (Word::BITS as usize + 37)) + IBig::from(0xdeadbeefu32);
for e in [-200isize, -3, 0, 5, 137] {
let r = Repr::<2>::new(sig.clone(), e);
let exact = (sig.clone(), e as i128);
assert!(
dycmp(dy(&Mag::from_repr(&r)), exact.clone()) != Less,
"from_repr({e}) under-bounds"
);
assert!(
dycmp(dy(&Mag::from_repr_lower(&r)), exact) != Greater,
"from_repr_lower({e}) over-bounds"
);
}
let s = IBig::from(0x1234u32);
let r = Repr::<2>::new(s.clone(), -20);
assert_eq!(dycmp(dy(&Mag::from_repr(&r)), (s, -20i128)), Equal);
assert!(Mag::from_repr(&Repr::<2>::zero()).is_zero());
assert!(Mag::from_repr(&Repr::<2>::infinity()).is_infinite());
assert!(Mag::from_repr_lower(&Repr::<2>::infinity()).is_infinite());
}
#[test]
fn from_repr_brackets_base10() {
let sig = IBig::from(123456789u32);
for e in [-40isize, -7, 0, 9] {
let r = Repr::<10>::new(sig.clone(), e);
let pow = IBig::from(dashu_int::UBig::from(10u8).pow(e.unsigned_abs()));
let (um, ue) = dy(&Mag::from_repr(&r));
let (lm, le) = dy(&Mag::from_repr_lower(&r));
if e >= 0 {
assert!(dycmp((um, ue), (sig.clone() * &pow, 0)) != Less, "up {e}");
assert!(dycmp((lm, le), (sig.clone() * &pow, 0)) != Greater, "lo {e}");
} else {
assert!(dycmp((um * &pow, ue), dyi(sig.clone())) != Less, "up {e}");
assert!(dycmp((lm * &pow, le), dyi(sig.clone())) != Greater, "lo {e}");
}
}
}
#[test]
fn exp_upper_bounds() {
assert_eq!(Mag::ZERO.exp_upper(), Mag::ONE);
let one = Mag::from_word(1);
assert!(dycmp(dy(&one.exp_upper()), dyi(4.into())) != Less);
assert!(dycmp(dy(&one.exp_upper()), dyi(5.into())) == Less);
let half = Mag::from_pow2(-1);
assert!(dycmp(dy(&half.exp_upper()), dyi(2.into())) != Less);
let t = Mag::from_pow2(-30);
let (bm, be) = dy(&t.exp_upper());
assert!(dycmp((bm.clone(), be + 30), dyi((IBig::ONE << 30) + 1)) != Less);
let bits = Word::BITS as isize;
let s = (bits + 29) as usize;
let rhs =
(IBig::ONE << s) + (IBig::ONE << (s - 29)) + (IBig::ONE << (s - bits as usize + 1));
assert!(dycmp((bm, be + s as i128), (rhs, 0i128)) != Greater);
assert!(Mag::from_pow2(3).exp_upper() >= Mag::from_pow2(1).exp_upper());
assert!(Mag::INFINITY.exp_upper().is_infinite());
assert!(Mag::from_pow2(1000).exp_upper().is_infinite());
}
#[test]
fn to_repr_base2_is_exact() {
assert_eq!(Mag::ZERO.to_repr::<2>(), Repr::<2>::zero());
assert_eq!(Mag::INFINITY.to_repr::<2>(), Repr::<2>::infinity());
let m = Mag::from_word(7);
assert_eq!(m.to_repr::<2>(), Repr::<2>::new(IBig::from(7u8), 0));
let m = Mag::from_pow2(-100);
assert_eq!(m.to_repr::<2>(), Repr::<2>::new(IBig::ONE, -100));
let m = Mag::from_word(3);
let r = m.to_repr::<2>();
assert_eq!(dycmp((r.significand().clone(), r.exponent() as i128), dy(&m)), Equal);
}
fn covers_base10(sig: IBig, er: i128, man: IBig, et: i128) -> bool {
if er >= 0 {
let pow10 = IBig::from(dashu_int::UBig::from(10u8).pow(er as usize));
dycmp((sig * pow10, 0), (man, et)) != Less
} else {
let m = (-er) as usize;
let five_m = IBig::from(dashu_int::UBig::from(5u8).pow(m));
dycmp((sig, 0), (man * five_m, et + er)) != Less
}
}
#[test]
fn log2_base_values_are_pinned() {
const D: i128 = 1_000_000_000_000_000_000; let assert_bracket = |floor: i128, (l, fb): (u64, u32)| {
let (l, den) = (l as i128, 1i128 << fb);
assert!(
(l + 1) * D > floor * den && l * D < (floor + 1) * den,
"log2 fixed point {l}/2^{fb} misses [{floor}, {floor}+1)·10^-18"
);
};
assert_bracket(D, log2_base::<2>()); assert_bracket(1_584_962_500_721_156_181, log2_base::<3>()); assert_bracket(3_321_928_094_887_362_347, log2_base::<10>()); }
#[test]
fn generic_base_powers_are_one_bit_tight() {
fn assert_one_bit_tight<const BASE: Word>() {
for e in [-5000isize, -257, -3, -1, 1, 2, 255, 4096] {
let mag = Mag::from_base_pow::<BASE>(e).mul_pow2(-e);
let bits = {
use dashu_base::BitTest as _;
dashu_int::UBig::from_word(BASE)
.pow(e.unsigned_abs())
.bit_len()
} as i128;
let sh = if e > 0 {
bits - 1 - e as i128
} else {
-(e as i128) - bits
};
let v = dy(&mag);
assert!(
dycmp(v.clone(), (IBig::ONE, sh)) != Less
&& dycmp(v, (IBig::ONE, sh + 1)) != Greater,
"base {BASE} e {e}: not one-bit tight"
);
}
}
macro_rules! check_base {
($base:expr) => {{
const BASE: Word = $base as Word;
if u64::from(BASE) == $base {
assert_one_bit_tight::<BASE>();
}
}};
}
check_base!(3);
check_base!(5);
check_base!(7);
check_base!(10);
check_base!(12);
check_base!(100);
check_base!(1u64 << 20);
check_base!((1u64 << 40) + 1);
}
fn assert_sound_huge_negative<const BASE: Word>() {
let (_, fb) = log2_base::<BASE>();
let int_bits: u32 = Word::BITS - 1 - BASE.leading_zeros();
let mut m: u128 = (BASE as u128) << (63 - int_bits);
let mut frac: u128 = 0;
let mut i = fb + 10;
loop {
if i == 0 {
break;
}
i -= 1;
m = (m * m) >> 63;
if m >> 64 != 0 {
frac |= 1 << i;
m >>= 1;
}
}
let wide = (((int_bits as u128) << (fb + 10)) | frac) as i128;
let lo_ceil = |e: i128| {
let num = e * wide;
let den = 1i128 << (fb + 10);
num.div_euclid(den) + (num.rem_euclid(den) != 0) as i128
};
for j in 1..=4096isize {
let e = (-(1i128 << fb) - j as i128).max(isize::MIN as i128 + 1) as isize;
let got = Mag::from_base_pow::<BASE>(e).exp - 1;
assert!(
got >= lo_ceil(e as i128),
"base {BASE} e {e}: binary exponent {got} undershoots ⌈e·log₂BASE⌉ = {}",
lo_ceil(e as i128)
);
}
}
#[test]
fn generic_base_pow_sound_for_huge_negative_exponents() {
macro_rules! check_base {
($base:expr) => {{
const BASE: Word = $base as Word;
if u64::from(BASE) == $base {
assert_sound_huge_negative::<BASE>();
}
}};
}
check_base!(100);
check_base!(1000);
}
#[test]
fn to_repr_base10_is_upper_bound() {
let m = Mag::from_word(7);
let r = m.to_repr::<10>();
let (man, et) = dy(&m);
assert!(covers_base10(r.significand().clone(), r.exponent() as i128, man, et));
let m = Mag::from_pow2(-100);
let r = m.to_repr::<10>();
assert!(r.exponent() >= -50);
assert!(!r.significand().is_zero());
let (man, et) = dy(&m);
assert!(covers_base10(r.significand().clone(), r.exponent() as i128, man, et));
}
}