-
-
Save bjorn3/de955bd055b0dbd10ba97cac700ec484 to your computer and use it in GitHub Desktop.
Repro for rustc_codegen_cranelift#1097
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs | |
index d3832ac4657..577818edc23 100644 | |
--- a/compiler/rustc_apfloat/src/lib.rs | |
+++ b/compiler/rustc_apfloat/src/lib.rs | |
@@ -418,13 +418,7 @@ impl IeeeFloat<SingleS> { | |
let mut carry = val; | |
for x in &mut dec_sig { | |
- let [low, mut high] = sig::widening_mul(*x, multiplier); | |
- | |
- let (low, overflow) = low.overflowing_add(carry); | |
- high += overflow as Limb; | |
- | |
- *x = low; | |
- carry = high; | |
+ unreachable!(); | |
} | |
if carry > 0 { |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] | |
#![no_std] | |
#![forbid(unsafe_code)] | |
#![feature(nll)] | |
#![feature(or_patterns)] | |
extern crate alloc; | |
use core::ops::Neg; | |
bitflags::bitflags! { | |
#[must_use] | |
struct Status: u8 { | |
const OK = 0x00; | |
const INVALID_OP = 0x01; | |
const DIV_BY_ZERO = 0x02; | |
const OVERFLOW = 0x04; | |
const UNDERFLOW = 0x08; | |
const INEXACT = 0x10; | |
} | |
} | |
#[must_use] | |
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] | |
struct StatusAnd<T> { | |
status: Status, | |
value: T, | |
} | |
impl Status { | |
fn and<T>(self, value: T) -> StatusAnd<T> { | |
StatusAnd { status: self, value } | |
} | |
} | |
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
enum Category { | |
Infinity, | |
NaN, | |
Normal, | |
Zero, | |
} | |
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
enum Round { | |
NearestTiesToEven, | |
TowardPositive, | |
TowardNegative, | |
TowardZero, | |
NearestTiesToAway, | |
} | |
impl Neg for Round { | |
type Output = Round; | |
fn neg(self) -> Round { | |
match self { | |
Round::TowardPositive => Round::TowardNegative, | |
Round::TowardNegative => Round::TowardPositive, | |
Round::NearestTiesToEven | Round::TowardZero | Round::NearestTiesToAway => self, | |
} | |
} | |
} | |
type ExpInt = i16; | |
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
pub struct ParseError(pub &'static str); | |
use core::cmp; | |
use core::mem; | |
use core::marker::PhantomData; | |
use smallvec::{smallvec, SmallVec}; | |
#[must_use] | |
pub struct IeeeFloat<S> { | |
sig: [Limb; 1], | |
exp: ExpInt, | |
category: Category, | |
sign: bool, | |
marker: PhantomData<S>, | |
} | |
type Limb = u128; | |
const LIMB_BITS: usize = 128; | |
fn limbs_for_bits(bits: usize) -> usize { | |
(bits + LIMB_BITS - 1) / LIMB_BITS | |
} | |
#[must_use] | |
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
enum Loss { | |
ExactlyZero, // 000000 | |
LessThanHalf, // 0xxxxx x's not all zero | |
ExactlyHalf, // 100000 | |
MoreThanHalf, // 1xxxxx x's not all zero | |
} | |
trait Semantics: Sized { | |
const BITS: usize; | |
const PRECISION: usize; | |
const MAX_EXP: ExpInt; | |
const MIN_EXP: ExpInt = -Self::MAX_EXP + 1; | |
const QNAN_BIT: usize = Self::PRECISION - 2; | |
const QNAN_SIGNIFICAND: Limb = 1 << Self::QNAN_BIT; | |
} | |
impl<S> Copy for IeeeFloat<S> {} | |
impl<S> Clone for IeeeFloat<S> { | |
fn clone(&self) -> Self { | |
*self | |
} | |
} | |
macro_rules! ieee_semantics { | |
($($name:ident = $sem:ident($bits:tt : $exp_bits:tt)),*) => { | |
$(pub struct $sem;)* | |
$(pub type $name = IeeeFloat<$sem>;)* | |
$(impl Semantics for $sem { | |
const BITS: usize = $bits; | |
const PRECISION: usize = ($bits - 1 - $exp_bits) + 1; | |
const MAX_EXP: ExpInt = (1 << ($exp_bits - 1)) - 1; | |
})* | |
} | |
} | |
ieee_semantics! { | |
Single = SingleS(32:8) | |
} | |
impl ::core::str::FromStr for IeeeFloat<SingleS> { | |
type Err = ParseError; | |
fn from_str(s: &str) -> Result<Self, ParseError> { | |
Self::from_str_r(s, Round::NearestTiesToEven).map(|x| x.value) | |
} | |
} | |
impl<S> Neg for IeeeFloat<S> { | |
type Output = Self; | |
fn neg(mut self) -> Self { | |
self.sign = !self.sign; | |
self | |
} | |
} | |
impl IeeeFloat<SingleS> { | |
fn is_finite(self) -> bool { | |
!self.is_nan() && !self.is_infinite() | |
} | |
fn is_zero(self) -> bool { | |
self.category() == Category::Zero | |
} | |
fn is_infinite(self) -> bool { | |
self.category() == Category::Infinity | |
} | |
fn is_nan(self) -> bool { | |
self.category() == Category::NaN | |
} | |
fn is_finite_non_zero(self) -> bool { | |
self.is_finite() && !self.is_zero() | |
} | |
const ZERO: Self = IeeeFloat { | |
sig: [0], | |
exp: SingleS::MIN_EXP - 1, | |
category: Category::Zero, | |
sign: false, | |
marker: PhantomData, | |
}; | |
const INFINITY: Self = IeeeFloat { | |
sig: [0], | |
exp: SingleS::MAX_EXP + 1, | |
category: Category::Infinity, | |
sign: false, | |
marker: PhantomData, | |
}; | |
const NAN: Self = IeeeFloat { | |
sig: [SingleS::QNAN_SIGNIFICAND], | |
exp: SingleS::MAX_EXP + 1, | |
category: Category::NaN, | |
sign: false, | |
marker: PhantomData, | |
}; | |
fn largest() -> Self { | |
IeeeFloat { | |
sig: [(1 << SingleS::PRECISION) - 1], | |
exp: SingleS::MAX_EXP, | |
category: Category::Normal, | |
sign: false, | |
marker: PhantomData, | |
} | |
} | |
const SMALLEST: Self = IeeeFloat { | |
sig: [1], | |
exp: SingleS::MIN_EXP, | |
category: Category::Normal, | |
sign: false, | |
marker: PhantomData, | |
}; | |
fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> { | |
if s.is_empty() { | |
return Err(ParseError("Invalid string length")); | |
} | |
match s { | |
"inf" | "INFINITY" => return Ok(Status::OK.and(Self::INFINITY)), | |
"-inf" | "-INFINITY" => return Ok(Status::OK.and(-Self::INFINITY)), | |
"nan" | "NaN" => return Ok(Status::OK.and(Self::NAN)), | |
"-nan" | "-NaN" => return Ok(Status::OK.and(-Self::NAN)), | |
_ => {} | |
} | |
if s.starts_with('-') || s.starts_with('+') { | |
unreachable!(); | |
} | |
let r = if s.starts_with("0x") || s.starts_with("0X") { | |
unreachable!(); //Self::from_hexadecimal_string(s, round)? | |
} else { | |
Self::from_decimal_string(s, round)? | |
}; | |
Ok(r) | |
} | |
fn category(self) -> Category { | |
self.category | |
} | |
} | |
impl IeeeFloat<SingleS> { | |
fn overflow_result(round: Round) -> StatusAnd<Self> { | |
match round { | |
Round::NearestTiesToEven | Round::NearestTiesToAway | Round::TowardPositive => { | |
(Status::OVERFLOW | Status::INEXACT).and(Self::INFINITY) | |
} | |
Round::TowardNegative | Round::TowardZero => Status::INEXACT.and(Self::largest()), | |
} | |
} | |
fn round_away_from_zero(&self, round: Round, loss: Loss, bit: usize) -> bool { | |
assert!(self.is_finite_non_zero() || self.is_zero()); | |
assert_ne!(loss, Loss::ExactlyZero); | |
match round { | |
Round::NearestTiesToAway => loss == Loss::ExactlyHalf || loss == Loss::MoreThanHalf, | |
Round::NearestTiesToEven => { | |
if loss == Loss::MoreThanHalf { | |
return true; | |
} | |
if loss == Loss::ExactlyHalf && self.category != Category::Zero { | |
return sig::get_bit(&self.sig, bit); | |
} | |
false | |
} | |
Round::TowardZero => false, | |
Round::TowardPositive => !self.sign, | |
Round::TowardNegative => self.sign, | |
} | |
} | |
fn normalize(mut self, round: Round, loss: Loss) -> StatusAnd<Self> { | |
if !self.is_finite_non_zero() { | |
return Status::OK.and(self); | |
} | |
let mut omsb = sig::omsb(&self.sig); | |
if omsb > 0 { | |
let final_exp = | |
self.exp.saturating_add(omsb as ExpInt - SingleS::PRECISION as ExpInt); | |
if final_exp > SingleS::MAX_EXP { | |
unreachable!(); | |
} | |
if final_exp < SingleS::MIN_EXP { | |
unreachable!(); | |
} | |
if final_exp < self.exp { | |
unreachable!(); | |
} | |
if final_exp > self.exp { | |
unreachable!(); | |
} | |
} | |
if loss == Loss::ExactlyZero { | |
unreachable!(); | |
} | |
if self.round_away_from_zero(round, loss, 0) { | |
if omsb == 0 { | |
unreachable!(); | |
} | |
assert_eq!(sig::increment(&mut self.sig), 0); | |
omsb = sig::omsb(&self.sig); | |
if omsb == SingleS::PRECISION + 1 { | |
unreachable!(); | |
} | |
} | |
if omsb == SingleS::PRECISION { | |
return Status::INEXACT.and(self); | |
} | |
unreachable!(); | |
} | |
fn from_decimal_string(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> { | |
let mut any_digits = false; | |
let mut dec_exp = 0i32; | |
let mut first_sig_digit = None; | |
let mut last_sig_digit = 0; | |
let mut dot = s.len(); | |
for (p, c) in s.char_indices() { | |
if c == '.' { | |
if dot != s.len() { | |
return Err(ParseError("String contains multiple dots")); | |
} | |
dot = p; | |
} else if let Some(dec_value) = c.to_digit(10) { | |
any_digits = true; | |
if dec_value != 0 { | |
if first_sig_digit.is_none() { | |
first_sig_digit = Some(p); | |
} | |
last_sig_digit = p; | |
} | |
} else { | |
unreachable!(); | |
} | |
} | |
if !any_digits { | |
return Err(ParseError("Significand has no digits")); | |
} | |
let first_sig_digit = match first_sig_digit { | |
Some(p) => p, | |
None => return Ok(Status::OK.and(Self::ZERO)), | |
}; | |
if dot > last_sig_digit { | |
dec_exp = dec_exp.saturating_add((dot - last_sig_digit - 1) as i32); | |
} else { | |
dec_exp = dec_exp.saturating_sub((last_sig_digit - dot) as i32); | |
} | |
let significand_digits = last_sig_digit - first_sig_digit + 1 | |
- (dot > first_sig_digit && dot < last_sig_digit) as usize; | |
let normalized_exp = dec_exp.saturating_add(significand_digits as i32 - 1); | |
if normalized_exp.saturating_sub(1).saturating_mul(42039) >= 12655 * SingleS::MAX_EXP as i32 | |
{ | |
return Ok(Self::overflow_result(round)); | |
} | |
if normalized_exp.saturating_add(1).saturating_mul(28738) | |
<= 8651 * (SingleS::MIN_EXP as i32 - SingleS::PRECISION as i32) | |
{ | |
let r = | |
if round == Round::TowardPositive { IeeeFloat::SMALLEST } else { IeeeFloat::ZERO }; | |
return Ok((Status::UNDERFLOW | Status::INEXACT).and(r)); | |
} | |
let max_limbs = limbs_for_bits(1 + 196 * significand_digits / 59); | |
let mut dec_sig: SmallVec<[Limb; 1]> = SmallVec::with_capacity(max_limbs); | |
let mut chars = s[first_sig_digit..=last_sig_digit].chars(); | |
loop { | |
let mut val = 0; | |
let mut multiplier = 1; | |
loop { | |
let dec_value = match chars.next() { | |
Some('.') => continue, | |
Some(c) => c.to_digit(10).unwrap(), | |
None => break, | |
}; | |
multiplier *= 10; | |
val = val * 10 + dec_value as Limb; | |
if multiplier > (!0 - 9) / 10 { | |
break; | |
} | |
} | |
if multiplier == 1 { | |
break; | |
} | |
let mut carry = val; | |
for x in &mut dec_sig { | |
let [low, mut high] = sig::widening_mul(*x, multiplier); | |
let (low, overflow) = low.overflowing_add(carry); | |
high += overflow as Limb; | |
*x = low; | |
carry = high; | |
} | |
if carry > 0 { | |
dec_sig.push(carry); | |
} | |
} | |
let (pow5_full, mut pow5_calc, mut sig_calc, mut sig_scratch_calc) = { | |
let mut power = dec_exp.abs() as usize; | |
const FIRST_EIGHT_POWERS: [Limb; 8] = [1, 5, 25, 125, 625, 3125, 15625, 78125]; | |
let p5_scratch = smallvec![]; | |
let p5: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[4]]; | |
let r_scratch = smallvec![]; | |
let r: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[power & 7]]; | |
power >>= 3; | |
while power > 0 { | |
unreachable!(); | |
} | |
(r, r_scratch, p5, p5_scratch) | |
}; | |
let mut attempt = 0; | |
loop { | |
let calc_precision = (LIMB_BITS << attempt) - 1; | |
attempt += 1; | |
let calc_normal_from_limbs = |sig: &mut SmallVec<[Limb; 1]>, | |
limbs: &[Limb]| | |
-> StatusAnd<ExpInt> { | |
sig.resize(limbs_for_bits(calc_precision), 0); | |
let (mut loss, mut exp) = sig::from_limbs(sig, limbs, calc_precision); | |
let mut omsb = sig::omsb(sig); | |
assert_ne!(omsb, 0); | |
let final_exp = exp.saturating_add(omsb as ExpInt - calc_precision as ExpInt); | |
if final_exp < exp { | |
assert_eq!(loss, Loss::ExactlyZero); | |
let exp_change = (exp - final_exp) as usize; | |
sig::shift_left(sig, &mut exp, exp_change); | |
return Status::OK.and(exp); | |
} | |
if final_exp > exp { | |
unreachable!(); | |
} | |
assert_eq!(omsb, calc_precision); | |
if loss == Loss::ExactlyZero { | |
unreachable!(); | |
} | |
if loss == Loss::MoreThanHalf || loss == Loss::ExactlyHalf && sig::get_bit(sig, 0) { | |
unreachable!(); | |
} | |
Status::INEXACT.and(exp) | |
}; | |
let (status, mut exp) = match calc_normal_from_limbs(&mut sig_calc, &dec_sig) { | |
StatusAnd { status, value } => (status, value), | |
}; | |
let (pow5_status, pow5_exp) = match calc_normal_from_limbs(&mut pow5_calc, &pow5_full) { | |
StatusAnd { status, value } => (status, value), | |
}; | |
exp += dec_exp as ExpInt; | |
let mut used_bits = SingleS::PRECISION; | |
let mut truncated_bits = calc_precision - used_bits; | |
let half_ulp_err1 = (status != Status::OK) as Limb; | |
let (calc_loss, half_ulp_err2); | |
if dec_exp >= 0 { | |
unreachable!(); | |
} else { | |
exp -= pow5_exp; | |
sig_scratch_calc.resize(sig_calc.len(), 0); | |
calc_loss = sig::div( | |
&mut sig_scratch_calc, | |
&mut exp, | |
&mut sig_calc, | |
&mut pow5_calc, | |
calc_precision, | |
); | |
mem::swap(&mut sig_calc, &mut sig_scratch_calc); | |
if exp < SingleS::MIN_EXP { | |
truncated_bits += (SingleS::MIN_EXP - exp) as usize; | |
used_bits = calc_precision.saturating_sub(truncated_bits); | |
} | |
half_ulp_err2 = | |
2 * (pow5_status != Status::OK || calc_loss != Loss::ExactlyZero) as Limb; | |
} | |
assert!(sig::get_bit(&sig_calc, calc_precision - 1)); | |
assert!(half_ulp_err1 < 2 || half_ulp_err2 < 2 || (half_ulp_err1 + half_ulp_err2 < 8)); | |
let inexact = (calc_loss != Loss::ExactlyZero) as Limb; | |
let half_ulp_err = if half_ulp_err1 + half_ulp_err2 == 0 { | |
unreachable!(); | |
} else { | |
inexact + 2 * (half_ulp_err1 + half_ulp_err2) | |
}; | |
let ulps_from_boundary = { | |
let bits = calc_precision - used_bits - 1; | |
let i = bits / LIMB_BITS; | |
let limb = sig_calc[i] & (!0 >> (LIMB_BITS - 1 - bits % LIMB_BITS)); | |
let boundary = match round { | |
Round::NearestTiesToEven | Round::NearestTiesToAway => 1 << (bits % LIMB_BITS), | |
_ => 0, | |
}; | |
if i == 0 { | |
let delta = limb.wrapping_sub(boundary); | |
cmp::min(delta, delta.wrapping_neg()) | |
} else { | |
unreachable!(); | |
} | |
}; | |
if ulps_from_boundary.saturating_mul(2) >= half_ulp_err { | |
let mut r = IeeeFloat { | |
sig: [0], | |
exp, | |
category: Category::Normal, | |
sign: false, | |
marker: PhantomData, | |
}; | |
sig::extract(&mut r.sig, &sig_calc, used_bits, calc_precision - used_bits); | |
r.exp += (SingleS::PRECISION - used_bits) as ExpInt; | |
let loss = Loss::through_truncation(&sig_calc, truncated_bits); | |
return Ok(r.normalize(round, loss)); | |
} | |
} | |
} | |
} | |
impl Loss { | |
fn combine(self, less_significant: Loss) -> Loss { | |
let mut more_significant = self; | |
if less_significant != Loss::ExactlyZero { | |
if more_significant == Loss::ExactlyZero { | |
more_significant = Loss::LessThanHalf; | |
} else if more_significant == Loss::ExactlyHalf { | |
more_significant = Loss::MoreThanHalf; | |
} | |
} | |
more_significant | |
} | |
fn through_truncation(limbs: &[Limb], bits: usize) -> Loss { | |
if bits == 0 { | |
return Loss::ExactlyZero; | |
} | |
let half_bit = bits - 1; | |
let half_limb = half_bit / LIMB_BITS; | |
let (half_limb, rest) = if half_limb < limbs.len() { | |
(limbs[half_limb], &limbs[..half_limb]) | |
} else { | |
unreachable!(); | |
}; | |
let half = 1 << (half_bit % LIMB_BITS); | |
let has_half = half_limb & half != 0; | |
let has_rest = half_limb & (half - 1) != 0 || !sig::is_all_zeros(rest); | |
match (has_half, has_rest) { | |
(false, false) => Loss::ExactlyZero, | |
(false, true) => Loss::LessThanHalf, | |
(true, false) => Loss::ExactlyHalf, | |
(true, true) => Loss::MoreThanHalf, | |
} | |
} | |
} | |
mod sig { | |
use super::{limbs_for_bits, ExpInt, Limb, Loss, LIMB_BITS}; | |
use core::cmp::Ordering; | |
pub(super) fn is_all_zeros(limbs: &[Limb]) -> bool { | |
limbs.iter().all(|&l| l == 0) | |
} | |
pub(super) fn olsb(limbs: &[Limb]) -> usize { | |
limbs | |
.iter() | |
.enumerate() | |
.find(|(_, &limb)| limb != 0) | |
.map_or(0, |(i, limb)| i * LIMB_BITS + limb.trailing_zeros() as usize + 1) | |
} | |
pub(super) fn omsb(limbs: &[Limb]) -> usize { | |
limbs | |
.iter() | |
.enumerate() | |
.rfind(|(_, &limb)| limb != 0) | |
.map_or(0, |(i, limb)| (i + 1) * LIMB_BITS - limb.leading_zeros() as usize) | |
} | |
pub(super) fn cmp(a: &[Limb], b: &[Limb]) -> Ordering { | |
assert_eq!(a.len(), b.len()); | |
for (a, b) in a.iter().zip(b).rev() { | |
match a.cmp(b) { | |
Ordering::Equal => {} | |
o => return o, | |
} | |
} | |
Ordering::Equal | |
} | |
pub(super) fn get_bit(limbs: &[Limb], bit: usize) -> bool { | |
limbs[bit / LIMB_BITS] & (1 << (bit % LIMB_BITS)) != 0 | |
} | |
pub(super) fn shift_left(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) { | |
if bits > 0 { | |
*exp = exp.checked_sub(bits as ExpInt).unwrap(); | |
let jump = bits / LIMB_BITS; | |
let shift = bits % LIMB_BITS; | |
for i in (0..dst.len()).rev() { | |
let mut limb; | |
if i < jump { | |
limb = 0; | |
} else { | |
limb = dst[i - jump]; | |
if shift > 0 { | |
limb <<= shift; | |
if i > jump { | |
unreachable!(); | |
} | |
} | |
} | |
dst[i] = limb; | |
} | |
} | |
} | |
pub(super) fn shift_right(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) -> Loss { | |
let loss = Loss::through_truncation(dst, bits); | |
if bits > 0 { | |
*exp = exp.checked_add(bits as ExpInt).unwrap(); | |
let jump = bits / LIMB_BITS; | |
let shift = bits % LIMB_BITS; | |
for i in 0..dst.len() { | |
let mut limb; | |
if i + jump >= dst.len() { | |
unreachable!(); | |
} else { | |
limb = dst[i + jump]; | |
if shift > 0 { | |
limb >>= shift; | |
if i + jump + 1 < dst.len() { | |
unreachable!(); | |
} | |
} | |
} | |
dst[i] = limb; | |
} | |
} | |
loss | |
} | |
pub(super) fn extract(dst: &mut [Limb], src: &[Limb], src_bits: usize, src_lsb: usize) { | |
if src_bits == 0 { | |
unreachable!(); | |
} | |
let dst_limbs = limbs_for_bits(src_bits); | |
assert!(dst_limbs <= dst.len()); | |
let src = &src[src_lsb / LIMB_BITS..]; | |
dst[..dst_limbs].copy_from_slice(&src[..dst_limbs]); | |
let shift = src_lsb % LIMB_BITS; | |
let _: Loss = shift_right(&mut dst[..dst_limbs], &mut 0, shift); | |
let n = dst_limbs * LIMB_BITS - shift; | |
if n < src_bits { | |
unreachable!(); | |
} else if n > src_bits && src_bits % LIMB_BITS > 0 { | |
dst[dst_limbs - 1] &= (1 << (src_bits % LIMB_BITS)) - 1; | |
} | |
for _ in &mut dst[dst_limbs..] { | |
unreachable!(); | |
} | |
} | |
pub(super) fn from_limbs(dst: &mut [Limb], src: &[Limb], precision: usize) -> (Loss, ExpInt) { | |
let omsb = omsb(src); | |
if precision <= omsb { | |
unreachable!(); | |
} else { | |
extract(dst, src, omsb, 0); | |
(Loss::ExactlyZero, precision as ExpInt - 1) | |
} | |
} | |
pub(super) fn each_chunk<F: FnMut(Limb) -> Limb>(limbs: &mut [Limb], bits: usize, mut f: F) { | |
assert_eq!(LIMB_BITS % bits, 0); | |
for limb in limbs.iter_mut().rev() { | |
let mut r = 0; | |
for i in (0..LIMB_BITS / bits).rev() { | |
r |= f((*limb >> (i * bits)) & ((1 << bits) - 1)) << (i * bits); | |
} | |
*limb = r; | |
} | |
} | |
pub(super) fn increment(dst: &mut [Limb]) -> Limb { | |
for x in dst { | |
*x = x.wrapping_add(1); | |
if *x != 0 { | |
return 0; | |
} | |
} | |
1 | |
} | |
pub(super) fn widening_mul(a: Limb, b: Limb) -> [Limb; 2] { | |
let wide = [0, 0]; | |
if a == 0 || b == 0 { | |
return wide; | |
} | |
unreachable!(); | |
} | |
pub(super) fn div( | |
quotient: &mut [Limb], | |
exp: &mut ExpInt, | |
dividend: &mut [Limb], | |
divisor: &mut [Limb], | |
precision: usize, | |
) -> Loss { | |
let bits = precision - omsb(divisor); | |
shift_left(divisor, &mut 0, bits); | |
*exp += bits as ExpInt; | |
let bits = precision - omsb(dividend); | |
shift_left(dividend, exp, bits); | |
let olsb_divisor = olsb(divisor); | |
if olsb_divisor == precision { | |
unreachable!(); | |
} | |
if cmp(dividend, divisor) == Ordering::Less { | |
shift_left(dividend, exp, 1); | |
assert_ne!(cmp(dividend, divisor), Ordering::Less) | |
} | |
let lost_fraction = |dividend: &[Limb], divisor: &[Limb]| match cmp(dividend, divisor) { | |
Ordering::Greater => Loss::MoreThanHalf, | |
Ordering::Equal => Loss::ExactlyHalf, | |
Ordering::Less => { | |
if is_all_zeros(dividend) { | |
Loss::ExactlyZero | |
} else { | |
Loss::LessThanHalf | |
} | |
} | |
}; | |
let divisor_bits = precision - (olsb_divisor - 1); | |
macro_rules! try_short_div { | |
($W:ty, $H:ty, $half:expr) => { | |
if divisor_bits * 2 <= $half { | |
let _: Loss = shift_right(divisor, &mut 0, olsb_divisor - 1); | |
let divisor = divisor[0] as $H as $W; | |
let top_limb = *dividend.last().unwrap(); | |
let mut rem = (top_limb >> (LIMB_BITS - (divisor_bits - 1))) as $H; | |
shift_left(dividend, &mut 0, divisor_bits - 1); | |
each_chunk(dividend, $half, |chunk| { | |
let chunk = chunk as $H; | |
let combined = ((rem as $W) << $half) | (chunk as $W); | |
rem = (combined % divisor) as $H; | |
(combined / divisor) as $H as Limb | |
}); | |
quotient.copy_from_slice(dividend); | |
return lost_fraction(&[(rem as Limb) << 1], &[divisor as Limb]); | |
} | |
}; | |
} | |
try_short_div!(u32, u16, 16); | |
unreachable!(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment