use crate::support::apint::APInt;
use std::cmp::Ordering;
use std::fmt;
pub const APFLOAT_OK: u32 = 0;
pub const APFLOAT_INVALID_OP: u32 = 1 << 0;
pub const APFLOAT_DIV_BY_ZERO: u32 = 1 << 1;
pub const APFLOAT_OVERFLOW: u32 = 1 << 2;
pub const APFLOAT_UNDERFLOW: u32 = 1 << 3;
pub const APFLOAT_INEXACT: u32 = 1 << 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RoundingMode {
NearestTiesToEven,
TowardZero,
TowardNegative,
TowardPositive,
NearestTiesToAway,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FloatCategory {
Zero,
Normal,
Infinity,
NaN,
Subnormal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FloatSemantics {
pub name: &'static str,
pub bits: u32,
pub exponent_bits: u32,
pub mantissa_bits: u32,
pub exponent_bias: i32,
pub largest_exponent: u32,
pub smallest_normalized_exponent: i32,
}
pub const IEEE_SINGLE: FloatSemantics = FloatSemantics {
name: "ieee_single",
bits: 32,
exponent_bits: 8,
mantissa_bits: 23,
exponent_bias: 127,
largest_exponent: 255,
smallest_normalized_exponent: -126,
};
pub const IEEE_DOUBLE: FloatSemantics = FloatSemantics {
name: "ieee_double",
bits: 64,
exponent_bits: 11,
mantissa_bits: 52,
exponent_bias: 1023,
largest_exponent: 2047,
smallest_normalized_exponent: -1022,
};
pub const IEEE_QUAD: FloatSemantics = FloatSemantics {
name: "ieee_quad",
bits: 128,
exponent_bits: 15,
mantissa_bits: 112,
exponent_bias: 16383,
largest_exponent: 32767,
smallest_normalized_exponent: -16382,
};
pub const X87_DOUBLE_EXTENDED: FloatSemantics = FloatSemantics {
name: "x87_double_extended",
bits: 80,
exponent_bits: 15,
mantissa_bits: 64, exponent_bias: 16383,
largest_exponent: 32767,
smallest_normalized_exponent: -16382,
};
pub const PPC_DOUBLE_DOUBLE: FloatSemantics = FloatSemantics {
name: "ppc_double_double",
bits: 128,
exponent_bits: 11,
mantissa_bits: 106, exponent_bias: 1023,
largest_exponent: 2047,
smallest_normalized_exponent: -1022,
};
#[derive(Clone)]
pub struct APFloat {
pub(crate) semantics: FloatSemantics,
pub(crate) sign: bool,
pub(crate) exponent: u32,
pub(crate) significand: Vec<u64>,
pub(crate) flags: u32,
}
impl FloatSemantics {
#[inline]
fn total_significand_bits(&self) -> u32 {
if self.is_x87() {
self.mantissa_bits
} else {
self.mantissa_bits + 1
}
}
#[inline]
fn fraction_bits(&self) -> u32 {
if self.is_x87() {
self.mantissa_bits - 1
} else {
self.mantissa_bits
}
}
#[inline]
fn is_x87(&self) -> bool {
self.bits == 80 && self.exponent_bits == 15
}
#[inline]
fn is_ppc_double_double(&self) -> bool {
self.name == "ppc_double_double"
}
}
impl APFloat {
#[inline]
fn sig_apint(&self, width: u32) -> APInt {
let mut words = self.significand.clone();
if words.is_empty() {
words.push(0);
}
let nw = ((width as usize + 63) / 64).max(1);
words.resize(nw, 0);
APInt::from_words(width, words)
}
#[allow(dead_code)]
fn set_sig_from_apint(&mut self, val: &APInt) {
self.significand = val.get_raw_data().to_vec();
while self.significand.len() > 1 && self.significand.last() == Some(&0) {
self.significand.pop();
}
}
fn sig_leading_zeros(&self) -> u32 {
if self.significand.is_empty() || self.significand.iter().all(|&w| w == 0) {
return 0; }
let top = *self.significand.last().unwrap();
let word_lz = top.leading_zeros();
let mut extra_lz = 0u32;
for &w in self.significand.iter().rev().skip(1) {
if w == 0 {
extra_lz += 64;
} else {
break;
}
}
extra_lz + word_lz
}
fn sig_shl(&mut self, shift: u32) {
if shift == 0 || self.significand.is_empty() {
return;
}
let word_shift = (shift / 64) as usize;
let bit_shift = (shift % 64) as u32;
let old_len = self.significand.len();
self.significand.resize(old_len + word_shift + 1, 0);
if bit_shift == 0 {
for i in (0..old_len).rev() {
self.significand[i + word_shift] = self.significand[i];
}
for i in 0..word_shift {
self.significand[i] = 0;
}
} else {
let inv_shift = 64 - bit_shift;
for i in (0..old_len).rev() {
let lo = self.significand[i] << bit_shift;
let hi = if i > 0 {
self.significand[i - 1] >> inv_shift
} else {
0
};
self.significand[i + word_shift + 1] = hi;
self.significand[i + word_shift] = lo;
}
for i in 0..word_shift {
self.significand[i] = 0;
}
if word_shift > 0 {
self.significand[word_shift - 1] = 0;
}
}
while self.significand.len() > 1 && self.significand.last() == Some(&0) {
self.significand.pop();
}
}
fn sig_shr(&mut self, shift: u32) -> bool {
if shift == 0 {
return false;
}
if self.significand.is_empty() || self.significand.iter().all(|&w| w == 0) {
self.significand.clear();
self.significand.push(0);
return false;
}
let word_shift = (shift / 64) as usize;
let bit_shift = (shift % 64) as u32;
let mut sticky = false;
for &w in self.significand.iter().take(word_shift) {
if w != 0 {
sticky = true;
}
}
if word_shift < self.significand.len() && bit_shift > 0 {
let mask = (1u64 << bit_shift) - 1;
if self.significand[word_shift] & mask != 0 {
sticky = true;
}
}
let old_len = self.significand.len();
let new_len = if old_len > word_shift {
old_len - word_shift
} else {
0
};
if new_len == 0 {
self.significand.clear();
self.significand.push(0);
return sticky;
}
if bit_shift == 0 {
for i in 0..new_len {
self.significand[i] = self.significand[i + word_shift];
}
} else {
let inv_shift = 64 - bit_shift;
for i in 0..new_len {
let lo = self.significand[i + word_shift] >> bit_shift;
let hi = if i + word_shift + 1 < old_len {
self.significand[i + word_shift + 1] << inv_shift
} else {
0
};
self.significand[i] = lo | hi;
}
}
self.significand.truncate(new_len);
while self.significand.len() > 1 && self.significand.last() == Some(&0) {
self.significand.pop();
}
sticky
}
#[allow(dead_code)]
fn sig_msb_position(&self) -> u32 {
if self.significand.is_empty() || self.significand.iter().all(|&w| w == 0) {
return 0;
}
for i in (0..self.significand.len()).rev() {
let w = self.significand[i];
if w != 0 {
return (i as u32 * 64) + 63 - w.leading_zeros();
}
}
0
}
fn sig_get_bit(&self, pos: u32) -> bool {
let word = (pos / 64) as usize;
let bit = pos % 64;
if word < self.significand.len() {
(self.significand[word] >> bit) & 1 != 0
} else {
false
}
}
fn sig_is_zero(&self) -> bool {
self.significand.is_empty() || self.significand.iter().all(|&w| w == 0)
}
}
impl APFloat {
pub fn new(sem: FloatSemantics) -> Self {
APFloat {
semantics: sem,
sign: false,
exponent: 0,
significand: Vec::new(),
flags: APFLOAT_OK,
}
}
pub fn get_zero(sem: FloatSemantics, sign: bool) -> Self {
APFloat {
semantics: sem,
sign,
exponent: 0,
significand: Vec::new(),
flags: APFLOAT_OK,
}
}
pub fn get_inf(sem: FloatSemantics, sign: bool) -> Self {
let mut sig = Vec::new();
if sem.is_x87() {
sig.push(1u64 << 63);
}
APFloat {
semantics: sem,
sign,
exponent: sem.largest_exponent,
significand: sig,
flags: APFLOAT_OK,
}
}
pub fn get_nan(sem: FloatSemantics, sign: bool, payload: Option<u64>) -> Self {
let qbit_pos = sem.fraction_bits() - 1; let mut payload_val = payload.unwrap_or(0);
if sem.is_x87() {
let int_bit = 1u64 << 63;
let qbit = 1u64 << 62;
payload_val &= (1u64 << 62) - 1;
let sig_word = int_bit | qbit | payload_val;
APFloat {
semantics: sem,
sign,
exponent: sem.largest_exponent,
significand: vec![sig_word],
flags: APFLOAT_OK,
}
} else {
let qbit = 1u64 << qbit_pos;
payload_val |= qbit;
let num_words = ((qbit_pos + 64) / 64) as usize;
let mut words = vec![0u64; num_words.max(1)];
let w = (qbit_pos / 64) as usize;
if w < words.len() {
words[w] = payload_val;
}
if qbit_pos >= 64 && w + 1 < words.len() {
}
while words.len() > 1 && words.last() == Some(&0) {
words.pop();
}
APFloat {
semantics: sem,
sign,
exponent: sem.largest_exponent,
significand: words,
flags: APFLOAT_OK,
}
}
}
pub fn get_qnan(sem: FloatSemantics, sign: bool) -> Self {
let mut result = Self::get_nan(sem, sign, Some(0));
if sem.is_x87() {
if result.significand.is_empty() {
result.significand.push(1u64 << 63 | 1u64 << 62);
} else {
result.significand[0] |= 1u64 << 62;
}
}
result
}
pub fn get_snan(sem: FloatSemantics, sign: bool) -> Self {
let frac_bits = sem.fraction_bits();
if sem.is_x87() {
let int_bit = 1u64 << 63;
let sig_word = int_bit | 1u64; APFloat {
semantics: sem,
sign,
exponent: sem.largest_exponent,
significand: vec![sig_word],
flags: APFLOAT_OK,
}
} else {
let qbit_pos = frac_bits - 1;
let mut payload: u64 = 1; if qbit_pos < 64 {
payload &= !(1u64 << qbit_pos);
}
let mut result = APFloat {
semantics: sem,
sign,
exponent: sem.largest_exponent,
significand: Vec::new(),
flags: APFLOAT_OK,
};
let num_words = ((frac_bits + 63) / 64) as usize;
result.significand = vec![0u64; num_words.max(1)];
if qbit_pos < 64 {
result.significand[0] = payload;
} else {
let w = (qbit_pos / 64) as usize;
result.significand[w] = payload;
}
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
result
}
}
pub fn from_f64(val: f64) -> Self {
let bits: u64 = val.to_bits();
Self::from_bits(bits, IEEE_DOUBLE)
}
pub fn from_f32(val: f32) -> Self {
let bits: u32 = val.to_bits();
Self::from_bits(bits as u64, IEEE_SINGLE)
}
fn from_bits(bits: u64, sem: FloatSemantics) -> Self {
let sign = (bits >> (sem.bits - 1)) & 1 != 0;
let exp_mask = (1u64 << sem.exponent_bits) - 1;
let mantissa_mask = (1u64 << sem.mantissa_bits) - 1;
let exp_shift = sem.mantissa_bits;
let raw_exp = ((bits >> exp_shift) & exp_mask) as u32;
let raw_mantissa = bits & mantissa_mask;
let mut result = APFloat {
semantics: sem,
sign,
exponent: raw_exp,
significand: Vec::new(),
flags: APFLOAT_OK,
};
if raw_exp == sem.largest_exponent {
if raw_mantissa == 0 {
if sem.is_x87() {
result.significand = vec![1u64 << 63];
}
} else {
let nw = ((sem.mantissa_bits as usize + 63) / 64).max(1);
let mut words = vec![0u64; nw];
words[0] = raw_mantissa;
if sem.is_x87() {
words[0] |= 1u64 << 63;
}
while words.len() > 1 && words.last() == Some(&0) {
words.pop();
}
result.significand = words;
}
} else if raw_exp == 0 {
if raw_mantissa != 0 {
let nw = ((sem.mantissa_bits as usize + 63) / 64).max(1);
let mut words = vec![0u64; nw];
words[0] = raw_mantissa;
if sem.is_x87() {
}
while words.len() > 1 && words.last() == Some(&0) {
words.pop();
}
result.significand = words;
}
} else {
let nw = ((sem.mantissa_bits as usize + 63) / 64).max(1);
let mut words = vec![0u64; nw];
words[0] = raw_mantissa;
if !sem.is_x87() {
let implicit_pos = sem.mantissa_bits;
let w = (implicit_pos / 64) as usize;
let b = implicit_pos % 64;
if w < words.len() {
words[w] |= 1u64 << b;
}
}
while words.len() > 1 && words.last() == Some(&0) {
words.pop();
}
result.significand = words;
}
result
}
pub fn from_apint(val: &APInt, sem: FloatSemantics) -> Self {
let words = val.get_raw_data();
let mut result = APFloat {
semantics: sem,
sign: false,
exponent: 0,
significand: Vec::new(),
flags: APFLOAT_OK,
};
if words.is_empty() || words.iter().all(|&w| w == 0) {
return Self::get_zero(sem, false);
}
let sign_bit_pos = sem.bits - 1;
let sign_word = (sign_bit_pos / 64) as usize;
let sign_bit = sign_bit_pos % 64;
result.sign = sign_word < words.len() && (words[sign_word] >> sign_bit) & 1 != 0;
let exp_lo = sem.mantissa_bits;
let exp_width = sem.exponent_bits;
let mut raw_exp: u32 = 0;
for i in 0..exp_width {
let pos = exp_lo + i;
let w = (pos / 64) as usize;
let b = pos % 64;
if w < words.len() && (words[w] >> b) & 1 != 0 {
raw_exp |= 1 << i;
}
}
result.exponent = raw_exp;
let mantissa_bits = sem.mantissa_bits;
let nw = ((mantissa_bits as usize + 63) / 64).max(1);
let mut sig_words = vec![0u64; nw];
for i in 0..mantissa_bits {
let w = (i / 64) as usize;
let b = i % 64;
if w < words.len() && (words[w] >> b) & 1 != 0 {
let sw = (i / 64) as usize;
let sb = i % 64;
if sw < sig_words.len() {
sig_words[sw] |= 1u64 << sb;
}
}
}
if raw_exp == sem.largest_exponent {
if sig_words.iter().all(|&w| w == 0) && !sem.is_x87() {
} else {
if sem.is_x87() {
sig_words[0] |= 1u64 << 63;
}
while sig_words.len() > 1 && sig_words.last() == Some(&0) {
sig_words.pop();
}
result.significand = sig_words;
}
} else if raw_exp == 0 {
if !sig_words.iter().all(|&w| w == 0) {
while sig_words.len() > 1 && sig_words.last() == Some(&0) {
sig_words.pop();
}
result.significand = sig_words;
}
} else {
if !sem.is_x87() {
let implicit_pos = mantissa_bits;
let sw = (implicit_pos / 64) as usize;
let sb = implicit_pos % 64;
if sw < sig_words.len() {
sig_words[sw] |= 1u64 << sb;
} else {
sig_words.resize(sw + 1, 0);
sig_words[sw] = 1u64 << sb;
}
}
while sig_words.len() > 1 && sig_words.last() == Some(&0) {
sig_words.pop();
}
result.significand = sig_words;
}
result
}
pub fn largest(sem: FloatSemantics, sign: bool) -> Self {
let mut result = APFloat {
semantics: sem,
sign,
exponent: sem.largest_exponent - 1,
significand: Vec::new(),
flags: APFLOAT_OK,
};
let frac_bits = sem.fraction_bits();
let nw = ((frac_bits as usize + 63) / 64).max(1);
result.significand = vec![u64::MAX; nw];
let rem = frac_bits % 64;
if rem != 0 {
let last = result.significand.last_mut().unwrap();
*last &= (1u64 << rem) - 1;
}
if !sem.is_x87() {
let implicit_pos = frac_bits;
let sw = (implicit_pos / 64) as usize;
let sb = implicit_pos % 64;
if sw < result.significand.len() {
result.significand[sw] |= 1u64 << sb;
} else {
result.significand.resize(sw + 1, 0);
result.significand[sw] = 1u64 << sb;
}
} else {
let sig_len = result.significand.len();
if sig_len == 1 {
let last = result.significand.last_mut().unwrap();
*last = (1u64 << 63) | ((*last) & ((1u64 << 63) - 1));
} else {
result.significand[0] |= 1u64 << 63;
}
}
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
result
}
pub fn smallest(sem: FloatSemantics, sign: bool) -> Self {
Self::smallest_normalized(sem, sign)
}
pub fn smallest_normalized(sem: FloatSemantics, sign: bool) -> Self {
let mut result = APFloat {
semantics: sem,
sign,
exponent: 1, significand: Vec::new(),
flags: APFLOAT_OK,
};
if sem.is_x87() {
result.significand = vec![1u64 << 63];
} else {
let implicit_pos = sem.mantissa_bits;
let sw = (implicit_pos / 64) as usize;
let sb = implicit_pos % 64;
result.significand = vec![0u64; sw + 1];
result.significand[sw] = 1u64 << sb;
}
result
}
}
impl APFloat {
pub fn is_zero(&self) -> bool {
self.exponent == 0 && self.sig_is_zero()
}
pub fn is_negative(&self) -> bool {
self.sign
}
pub fn is_infinity(&self) -> bool {
if self.exponent != self.semantics.largest_exponent {
return false;
}
if self.semantics.is_x87() {
if self.significand.len() == 1 {
let w = self.significand[0];
return w == (1u64 << 63);
}
return false;
}
self.sig_is_zero()
}
pub fn is_positively_infinity(&self) -> bool {
!self.sign && self.is_infinity()
}
pub fn is_negatively_infinity(&self) -> bool {
self.sign && self.is_infinity()
}
pub fn is_nan(&self) -> bool {
self.exponent == self.semantics.largest_exponent && !self.sig_is_zero()
}
pub fn is_signaling_nan(&self) -> bool {
if !self.is_nan() {
return false;
}
let qbit_pos = if self.semantics.is_x87() {
62 } else {
self.semantics.fraction_bits() - 1
};
!self.sig_get_bit(qbit_pos)
}
pub fn is_normal(&self) -> bool {
if self.is_zero() || self.is_infinity() || self.is_nan() {
return false;
}
self.exponent > 0
}
pub fn is_subnormal(&self) -> bool {
self.exponent == 0 && !self.sig_is_zero()
}
pub fn is_non_zero(&self) -> bool {
!self.is_zero()
}
pub fn is_finite(&self) -> bool {
!self.is_infinity() && !self.is_nan()
}
pub fn classify(&self) -> FloatCategory {
if self.is_zero() {
FloatCategory::Zero
} else if self.is_infinity() {
FloatCategory::Infinity
} else if self.is_nan() {
FloatCategory::NaN
} else if self.exponent == 0 {
FloatCategory::Subnormal
} else {
FloatCategory::Normal
}
}
}
impl APFloat {
pub fn copy_sign(&self, other: &APFloat) -> APFloat {
let mut result = self.clone();
result.sign = other.sign;
result.flags = APFLOAT_OK;
result
}
pub fn abs(&self) -> APFloat {
let mut result = self.clone();
result.sign = false;
result.flags = APFLOAT_OK;
result
}
pub fn negate(&self) -> APFloat {
let mut result = self.clone();
if !result.is_nan() {
result.sign = !result.sign;
}
result.flags = APFLOAT_OK;
result
}
}
impl APFloat {
#[allow(dead_code)]
fn normalize(&mut self, unbiased_exp: i64) -> u32 {
if self.sig_is_zero() {
self.exponent = 0;
self.significand.clear();
return 0;
}
let lz = self.sig_leading_zeros();
if lz > 0 {
self.sig_shl(lz);
}
let new_unbiased = unbiased_exp - lz as i64;
let bias = self.semantics.exponent_bias as i64;
let min_normal_unbiased = self.semantics.smallest_normalized_exponent as i64;
if new_unbiased < min_normal_unbiased {
let _shift = (min_normal_unbiased - new_unbiased) as u32;
let biased = new_unbiased + bias;
if biased < 1 {
let subnormal_shift = (1 - biased) as u32;
let _sticky = self.sig_shr(subnormal_shift);
return 0;
}
}
let biased = (new_unbiased + bias) as u32;
if biased >= self.semantics.largest_exponent {
return self.semantics.largest_exponent;
}
biased
}
fn unbiased_exponent(&self) -> i64 {
if self.exponent == 0 {
if self.sig_is_zero() {
return 0;
}
1 - self.semantics.exponent_bias as i64
} else {
self.exponent as i64 - self.semantics.exponent_bias as i64
}
}
}
impl APFloat {
fn round_significand(
sig: &mut Vec<u64>,
target_bits: u32,
rm: RoundingMode,
sign: bool,
) -> (bool, bool) {
let msb = sig_msb_position(sig);
if msb + 1 <= target_bits {
return (false, false);
}
let shift = msb + 1 - target_bits;
let guard_bit = if shift >= 1 && shift <= msb + 1 {
get_bit_vec(sig, shift - 1)
} else {
false
};
let round_bit = if shift >= 2 && shift <= msb + 1 {
get_bit_vec(sig, shift - 2)
} else {
false
};
let sticky = if shift > 2 {
any_bit_below_vec(sig, shift - 2)
} else {
false
};
let _shift_sticky = shr_vec(sig, shift);
let round_up = match rm {
RoundingMode::NearestTiesToEven => {
guard_bit && (round_bit || sticky || get_bit_vec(sig, 0))
}
RoundingMode::NearestTiesToAway => {
guard_bit
}
RoundingMode::TowardZero => false,
RoundingMode::TowardNegative => sign && (guard_bit || round_bit || sticky),
RoundingMode::TowardPositive => !sign && (guard_bit || round_bit || sticky),
};
let inexact = guard_bit || round_bit || sticky;
if round_up {
let carry = add_one_vec(sig);
(carry, inexact)
} else {
(false, inexact)
}
}
fn round_and_renormalize(
sig: &mut Vec<u64>,
biased_exp: u32,
target_bits: u32,
rm: RoundingMode,
sign: bool,
sem: &FloatSemantics,
) -> (u32, bool) {
let (carry, inexact) = Self::round_significand(sig, target_bits, rm, sign);
let mut new_exp = biased_exp;
if carry {
let _ = shr_vec(sig, 1);
let msb_pos = target_bits - 1;
let w = (msb_pos / 64) as usize;
let b = msb_pos % 64;
if w >= sig.len() {
sig.resize(w + 1, 0);
}
sig[w] |= 1u64 << b;
new_exp += 1;
if new_exp >= sem.largest_exponent {
sig.clear();
if sem.is_x87() {
sig.push(1u64 << 63);
}
return (sem.largest_exponent, inexact);
}
}
if sig.iter().all(|&w| w == 0) {
sig.clear();
return (0, inexact);
}
let msb = sig_msb_position(sig);
if msb > target_bits - 1 {
let shift = msb - (target_bits - 1);
let _ = shr_vec(sig, shift);
new_exp += shift;
if new_exp >= sem.largest_exponent {
sig.clear();
if sem.is_x87() {
sig.push(1u64 << 63);
}
return (sem.largest_exponent, true);
}
} else if msb < target_bits - 1 && new_exp > 0 {
let shift = (target_bits - 1) - msb;
shl_vec(sig, shift);
if new_exp > shift as u32 {
new_exp -= shift as u32;
} else {
let remaining = shift as u32 - new_exp;
new_exp = 0;
let _ = shr_vec(sig, remaining);
}
} else if new_exp == 0 && msb < target_bits - 1 {
}
(new_exp, inexact)
}
}
fn sig_msb_position(words: &[u64]) -> u32 {
if words.is_empty() {
return 0;
}
for i in (0..words.len()).rev() {
let w = words[i];
if w != 0 {
return (i as u32 * 64) + 63 - w.leading_zeros();
}
}
0
}
fn get_bit_vec(words: &[u64], pos: u32) -> bool {
let w = (pos / 64) as usize;
let b = pos % 64;
w < words.len() && (words[w] >> b) & 1 != 0
}
fn any_bit_below_vec(words: &[u64], pos: u32) -> bool {
if pos == 0 {
return false;
}
let full_words = (pos / 64) as usize;
for i in 0..full_words.min(words.len()) {
if words[i] != 0 {
return true;
}
}
let rem = pos % 64;
if rem > 0 {
let w = full_words;
if w < words.len() {
let mask = (1u64 << rem) - 1;
if words[w] & mask != 0 {
return true;
}
}
}
false
}
fn shr_vec(words: &mut Vec<u64>, shift: u32) -> bool {
if shift == 0 {
return false;
}
if words.is_empty() || words.iter().all(|&w| w == 0) {
words.clear();
words.push(0);
return false;
}
let word_shift = (shift / 64) as usize;
let bit_shift = (shift % 64) as u32;
let mut sticky = false;
for &w in words.iter().take(word_shift) {
if w != 0 {
sticky = true;
}
}
if word_shift < words.len() && bit_shift > 0 {
let mask = (1u64 << bit_shift) - 1;
if words[word_shift] & mask != 0 {
sticky = true;
}
}
let old_len = words.len();
let new_len = if old_len > word_shift {
old_len - word_shift
} else {
0
};
if new_len == 0 {
words.clear();
words.push(0);
return sticky;
}
if bit_shift == 0 {
for i in 0..new_len {
words[i] = words[i + word_shift];
}
} else {
let inv_shift = 64 - bit_shift;
for i in 0..new_len {
let lo = words[i + word_shift] >> bit_shift;
let hi = if i + word_shift + 1 < old_len {
words[i + word_shift + 1] << inv_shift
} else {
0
};
words[i] = lo | hi;
}
}
words.truncate(new_len);
while words.len() > 1 && words.last() == Some(&0) {
words.pop();
}
sticky
}
fn shl_vec(words: &mut Vec<u64>, shift: u32) {
if shift == 0 || words.is_empty() {
return;
}
let word_shift = (shift / 64) as usize;
let bit_shift = (shift % 64) as u32;
let old_len = words.len();
words.resize(old_len + word_shift + 1, 0);
if bit_shift == 0 {
for i in (0..old_len).rev() {
words[i + word_shift] = words[i];
}
for i in 0..word_shift {
words[i] = 0;
}
} else {
let inv_shift = 64 - bit_shift;
for i in (0..old_len).rev() {
let lo = words[i] << bit_shift;
let hi = if i > 0 { words[i - 1] >> inv_shift } else { 0 };
words[i + word_shift + 1] = hi;
words[i + word_shift] = lo;
}
for i in 0..word_shift {
words[i] = 0;
}
}
while words.len() > 1 && words.last() == Some(&0) {
words.pop();
}
}
fn add_one_vec(words: &mut Vec<u64>) -> bool {
for w in words.iter_mut() {
let (sum, carry) = w.overflowing_add(1);
*w = sum;
if !carry {
return false;
}
}
true
}
impl APFloat {
fn propagate_nan(&self, other: &APFloat) -> Option<APFloat> {
let self_nan = self.is_nan();
let other_nan = other.is_nan();
if !self_nan && !other_nan {
return None;
}
let mut result = if other_nan {
other.clone()
} else {
self.clone()
};
if self.is_signaling_nan() || other.is_signaling_nan() {
result.flags |= APFLOAT_INVALID_OP;
}
let qbit_pos = if result.semantics.is_x87() {
62
} else {
result.semantics.fraction_bits() - 1
};
let w = (qbit_pos / 64) as usize;
let b = qbit_pos % 64;
if w < result.significand.len() {
result.significand[w] |= 1u64 << b;
} else if !result.significand.is_empty() {
result.significand.resize(w + 1, 0);
result.significand[w] = 1u64 << b;
} else {
result = Self::get_qnan(result.semantics, result.sign);
}
result.exponent = result.semantics.largest_exponent;
Some(result)
}
}
impl APFloat {
pub fn add(&self, other: &APFloat, rm: RoundingMode) -> APFloat {
if let Some(nan) = self.propagate_nan(other) {
return nan;
}
let self_inf = self.is_infinity();
let other_inf = other.is_infinity();
let self_zero = self.is_zero();
let other_zero = other.is_zero();
if self_inf && other_inf {
if self.sign == other.sign {
let mut result = self.clone();
result.flags = APFLOAT_OK;
return result;
} else {
let mut result = Self::get_qnan(self.semantics, false);
result.flags = APFLOAT_INVALID_OP;
return result;
}
}
if self_inf {
let mut result = self.clone();
result.flags = APFLOAT_OK;
return result;
}
if other_inf {
let mut result = other.clone();
result.flags = APFLOAT_OK;
return result;
}
if self_zero && other_zero {
let result_sign = if self.sign && other.sign {
true
} else if rm == RoundingMode::TowardNegative {
true
} else {
false
};
let mut result = Self::get_zero(self.semantics, result_sign);
result.flags = APFLOAT_OK;
return result;
}
if self_zero {
let mut result = other.clone();
result.flags = APFLOAT_OK;
return result;
}
if other_zero {
let mut result = self.clone();
result.flags = APFLOAT_OK;
return result;
}
self.add_sub_internal(other, false, rm)
}
pub fn sub(&self, other: &APFloat, rm: RoundingMode) -> APFloat {
let mut neg_other = other.clone();
neg_other.sign = !neg_other.sign;
self.add(&neg_other, rm)
}
pub fn mul(&self, other: &APFloat, rm: RoundingMode) -> APFloat {
if let Some(nan) = self.propagate_nan(other) {
return nan;
}
let self_inf = self.is_infinity();
let other_inf = other.is_infinity();
let self_zero = self.is_zero();
let other_zero = other.is_zero();
if (self_inf && other_zero) || (self_zero && other_inf) {
let mut result = Self::get_qnan(self.semantics, false);
result.flags = APFLOAT_INVALID_OP;
return result;
}
let result_sign = self.sign ^ other.sign;
if self_inf || other_inf {
let mut result = Self::get_inf(self.semantics, result_sign);
result.flags = APFLOAT_OK;
return result;
}
if self_zero || other_zero {
let mut result = Self::get_zero(self.semantics, result_sign);
result.flags = APFLOAT_OK;
return result;
}
let target_bits = self.semantics.total_significand_bits();
let internal_width = target_bits * 4 + 10;
let a = self.sig_apint(internal_width);
let b = other.sig_apint(internal_width);
let product = a.mul(&b);
let exp_a = self.unbiased_exponent();
let exp_b = other.unbiased_exponent();
let mut unbiased_exp = exp_a + exp_b - self.semantics.mantissa_bits as i64;
let mut sig_words = product.get_raw_data().to_vec();
while sig_words.len() > 1 && sig_words.last() == Some(&0) {
sig_words.pop();
}
let (biased_exp, inexact) = Self::normalize_and_round(
&mut sig_words,
&mut unbiased_exp,
target_bits,
rm,
result_sign,
&self.semantics,
);
let mut result = APFloat {
semantics: self.semantics,
sign: result_sign,
exponent: biased_exp,
significand: sig_words,
flags: if inexact { APFLOAT_INEXACT } else { APFLOAT_OK },
};
if biased_exp == self.semantics.largest_exponent && !result.is_nan() {
result.flags |= APFLOAT_OVERFLOW | APFLOAT_INEXACT;
}
if biased_exp == 0 && !result.is_zero() && inexact {
result.flags |= APFLOAT_UNDERFLOW;
}
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
result
}
pub fn div(&self, other: &APFloat, rm: RoundingMode) -> APFloat {
if let Some(nan) = self.propagate_nan(other) {
return nan;
}
let self_inf = self.is_infinity();
let other_inf = other.is_infinity();
let self_zero = self.is_zero();
let other_zero = other.is_zero();
if (self_zero && other_zero) || (self_inf && other_inf) {
let mut result = Self::get_qnan(self.semantics, false);
result.flags = APFLOAT_INVALID_OP;
return result;
}
let result_sign = self.sign ^ other.sign;
if other_zero {
let mut result = Self::get_inf(self.semantics, result_sign);
result.flags = APFLOAT_DIV_BY_ZERO;
return result;
}
if other_inf {
let mut result = Self::get_zero(self.semantics, result_sign);
result.flags = APFLOAT_OK;
return result;
}
if self_zero {
let mut result = Self::get_zero(self.semantics, result_sign);
result.flags = APFLOAT_OK;
return result;
}
if self_inf {
let mut result = Self::get_inf(self.semantics, result_sign);
result.flags = APFLOAT_OK;
return result;
}
let target_bits = self.semantics.total_significand_bits();
let internal_width = target_bits * 4 + 10;
let extra_bits = target_bits + 2;
let div_width = internal_width + extra_bits;
let a = self.sig_apint(div_width);
let b = other.sig_apint(div_width);
let shifted_a = a.shl(extra_bits);
let quotient = shifted_a.udiv(&b);
let exp_a = self.unbiased_exponent();
let exp_b = other.unbiased_exponent();
let mut unbiased_exp = exp_a - exp_b - extra_bits as i64
+ self.semantics.mantissa_bits as i64;
let mut sig_words = quotient.get_raw_data().to_vec();
while sig_words.len() > 1 && sig_words.last() == Some(&0) {
sig_words.pop();
}
let (biased_exp, inexact) = Self::normalize_and_round(
&mut sig_words,
&mut unbiased_exp,
target_bits,
rm,
result_sign,
&self.semantics,
);
let mut result = APFloat {
semantics: self.semantics,
sign: result_sign,
exponent: biased_exp,
significand: sig_words,
flags: if inexact { APFLOAT_INEXACT } else { APFLOAT_OK },
};
if biased_exp == self.semantics.largest_exponent && !result.is_nan() {
result.flags |= APFLOAT_OVERFLOW | APFLOAT_INEXACT;
}
if biased_exp == 0 && !result.is_zero() && inexact {
result.flags |= APFLOAT_UNDERFLOW;
}
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
result
}
pub fn rem(&self, other: &APFloat, rm: RoundingMode) -> APFloat {
if let Some(nan) = self.propagate_nan(other) {
return nan;
}
let self_inf = self.is_infinity();
let other_inf = other.is_infinity();
let self_zero = self.is_zero();
let other_zero = other.is_zero();
if self_inf || other_zero {
let mut result = Self::get_qnan(self.semantics, false);
result.flags = APFLOAT_INVALID_OP;
return result;
}
if other_inf {
let mut result = self.clone();
result.flags = APFLOAT_OK;
return result;
}
if self_zero {
let mut result = self.clone();
result.flags = APFLOAT_OK;
return result;
}
let q = self.div(other, RoundingMode::NearestTiesToEven);
let n = q.round_to_integral(RoundingMode::NearestTiesToEven);
let prod = n.mul(other, rm);
self.sub(&prod, rm)
}
pub fn fma(&self, mul: &APFloat, add: &APFloat, rm: RoundingMode) -> APFloat {
if let Some(nan) = self.propagate_nan(mul) {
let nan2 = nan.propagate_nan(add);
return nan2.unwrap_or(nan);
}
if let Some(nan) = self.propagate_nan(add) {
return nan;
}
let self_inf = self.is_infinity();
let mul_inf = mul.is_infinity();
let add_inf = add.is_infinity();
let self_zero = self.is_zero();
let mul_zero = mul.is_zero();
let add_zero = add.is_zero();
if (self_inf && mul_zero) || (self_zero && mul_inf) {
let mut result = Self::get_qnan(self.semantics, false);
result.flags = APFLOAT_INVALID_OP;
return result;
}
let prod_sign = self.sign ^ mul.sign;
let prod_inf = self_inf || mul_inf;
let prod_zero = self_zero || mul_zero;
if prod_inf && add_inf && prod_sign != add.sign {
let mut result = Self::get_qnan(self.semantics, false);
result.flags = APFLOAT_INVALID_OP;
return result;
}
if prod_inf {
let mut result = Self::get_inf(self.semantics, prod_sign);
result.flags = APFLOAT_OK;
return result;
}
if add_inf {
let mut result = add.clone();
result.flags = APFLOAT_OK;
return result;
}
if prod_zero {
let mut result = add.clone();
result.flags = APFLOAT_OK;
return result;
}
if add_zero {
let mut result = self.mul(mul, rm);
result.flags = APFLOAT_OK;
return result;
}
let target_bits = self.semantics.total_significand_bits();
let wide_width = target_bits * 4 + 20;
let a = self.sig_apint(wide_width);
let b = mul.sig_apint(wide_width);
let product = a.mul(&b);
let prod_ap = product;
let prod_exp_unbiased = self.unbiased_exponent() + mul.unbiased_exponent() - self.semantics.mantissa_bits as i64;
let add_exp_unbiased = add.unbiased_exponent();
let exp_diff = prod_exp_unbiased - add_exp_unbiased;
let add_ap = add.sig_apint(wide_width);
let mut sum_sig: Vec<u64>;
let result_sign: bool;
let (aligned_prod, aligned_add, larger_exp) = if exp_diff >= 0 {
let add_raw = add_ap.get_raw_data().to_vec();
let mut shifted_add = add_raw.to_vec();
let _ = shr_vec(&mut shifted_add, exp_diff as u32);
let ap_shifted = APInt::from_words(wide_width, shifted_add);
(prod_ap, ap_shifted, prod_exp_unbiased)
} else {
let prod_raw = prod_ap.get_raw_data().to_vec();
let mut shifted_prod = prod_raw.to_vec();
let _ = shr_vec(&mut shifted_prod, (-exp_diff) as u32);
let ap_shifted = APInt::from_words(wide_width, shifted_prod);
(ap_shifted, add_ap, add_exp_unbiased)
};
if prod_sign == add.sign {
sum_sig = aligned_prod.add(&aligned_add).get_raw_data().to_vec();
result_sign = prod_sign;
} else {
let prod_ge_add = aligned_prod.uge(&aligned_add);
if prod_ge_add {
sum_sig = aligned_prod.sub(&aligned_add).get_raw_data().to_vec();
result_sign = prod_sign;
} else {
sum_sig = aligned_add.sub(&aligned_prod).get_raw_data().to_vec();
result_sign = add.sign;
}
}
while sum_sig.len() > 1 && sum_sig.last() == Some(&0) {
sum_sig.pop();
}
if sum_sig.iter().all(|&w| w == 0) {
let mut result = Self::get_zero(self.semantics, false);
result.flags = APFLOAT_OK;
return result;
}
let mut unbiased_exp = larger_exp;
let (biased_exp, inexact) = Self::normalize_and_round(
&mut sum_sig,
&mut unbiased_exp,
target_bits,
rm,
result_sign,
&self.semantics,
);
let mut result = APFloat {
semantics: self.semantics,
sign: result_sign,
exponent: biased_exp,
significand: sum_sig,
flags: if inexact { APFLOAT_INEXACT } else { APFLOAT_OK },
};
if biased_exp == self.semantics.largest_exponent && !result.is_nan() {
result.flags |= APFLOAT_OVERFLOW | APFLOAT_INEXACT;
}
if biased_exp == 0 && !result.is_zero() && inexact {
result.flags |= APFLOAT_UNDERFLOW;
}
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
result
}
pub fn sqrt(&self, rm: RoundingMode) -> APFloat {
if self.is_nan() {
return self.clone();
}
if self.is_zero() {
let mut result = self.clone();
result.flags = APFLOAT_OK;
return result;
}
if self.is_infinity() && !self.sign {
let mut result = self.clone();
result.flags = APFLOAT_OK;
return result;
}
if self.sign {
let mut result = Self::get_qnan(self.semantics, false);
result.flags = APFLOAT_INVALID_OP;
return result;
}
let target_bits = self.semantics.total_significand_bits();
let internal_width = target_bits * 4 + 10;
let extra_shift = 2 * target_bits;
let s = self.sig_apint(internal_width);
let s_exp = self.unbiased_exponent();
let (mut adjusted_s, adjusted_exp) = if s_exp % 2 != 0 {
(s.shl(1), s_exp - 1)
} else {
(s, s_exp)
};
adjusted_s = adjusted_s.shl(extra_shift);
let s_bits = adjusted_s.count_leading_zeros();
let bit_len = internal_width - s_bits;
let mut x = APInt::from_u64(internal_width, 1);
x = x.shl((bit_len + 1) / 2);
for _ in 0..15 {
let q = adjusted_s.udiv(&x);
let sum = x.add(&q);
let x_next = sum.lshr(1);
if x_next.eq(&x) {
break;
}
x = x_next;
}
let mut sig_words = x.get_raw_data().to_vec();
while sig_words.len() > 1 && sig_words.last() == Some(&0) {
sig_words.pop();
}
let mut unbiased_exp = (adjusted_exp + self.semantics.mantissa_bits as i64) / 2
- target_bits as i64;
let (biased_exp, inexact) = Self::normalize_and_round(
&mut sig_words,
&mut unbiased_exp,
target_bits,
rm,
false,
&self.semantics,
);
let mut result = APFloat {
semantics: self.semantics,
sign: false,
exponent: biased_exp,
significand: sig_words,
flags: if inexact { APFLOAT_INEXACT } else { APFLOAT_OK },
};
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
result
}
fn add_sub_internal(&self, other: &APFloat, _is_sub: bool, rm: RoundingMode) -> APFloat {
let target_bits = self.semantics.total_significand_bits();
let internal_width = target_bits * 4 + 10;
let exp_a = self.unbiased_exponent();
let exp_b = other.unbiased_exponent();
let (larger, smaller, larger_exp) = if exp_a > exp_b
|| (exp_a == exp_b
&& self
.sig_apint(internal_width)
.uge(&other.sig_apint(internal_width)))
{
(self, other, exp_a)
} else {
(other, self, exp_b)
};
let exp_diff = larger_exp - smaller.unbiased_exponent();
let larger_sig = larger.sig_apint(internal_width);
let smaller_sig = smaller.sig_apint(internal_width);
let mut smaller_words = smaller_sig.get_raw_data().to_vec();
let _sticky = shr_vec(&mut smaller_words, exp_diff as u32);
let aligned_smaller = APInt::from_words(internal_width, smaller_words);
let result_sign: bool;
let sum_words: Vec<u64>;
let _eff_sub = larger.sign != smaller.sign;
if larger.sign == smaller.sign {
sum_words = larger_sig.add(&aligned_smaller).get_raw_data().to_vec();
result_sign = larger.sign;
} else {
let diff = larger_sig.sub(&aligned_smaller);
sum_words = diff.get_raw_data().to_vec();
result_sign = larger.sign;
}
let mut sig = sum_words;
while sig.len() > 1 && sig.last() == Some(&0) {
sig.pop();
}
if sig.iter().all(|&w| w == 0) {
let mut result = Self::get_zero(self.semantics, false);
result.flags = APFLOAT_OK;
return result;
}
let mut larger_unbiased_exp = larger_exp;
let (biased_exp, inexact) = Self::normalize_and_round(
&mut sig,
&mut larger_unbiased_exp,
target_bits,
rm,
result_sign,
&self.semantics,
);
let mut result = APFloat {
semantics: self.semantics,
sign: result_sign,
exponent: biased_exp,
significand: sig,
flags: if inexact { APFLOAT_INEXACT } else { APFLOAT_OK },
};
if biased_exp == self.semantics.largest_exponent && !result.is_nan() {
result.flags |= APFLOAT_OVERFLOW | APFLOAT_INEXACT;
}
if biased_exp == 0 && !result.is_zero() && inexact {
result.flags |= APFLOAT_UNDERFLOW;
}
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
result
}
fn normalize_and_round(
sig_words: &mut Vec<u64>,
unbiased_exp: &mut i64,
target_bits: u32,
rm: RoundingMode,
sign: bool,
sem: &FloatSemantics,
) -> (u32, bool) {
if sig_words.is_empty() || sig_words.iter().all(|&w| w == 0) {
return (0, false);
}
let msb = sig_msb_position(sig_words);
let target_msb = target_bits.saturating_sub(1);
if msb > target_msb {
let shift = msb - target_msb;
let _ = shr_vec(sig_words, shift);
*unbiased_exp += shift as i64;
} else if msb < target_msb && *unbiased_exp > sem.smallest_normalized_exponent as i64 - 1 {
let max_shift = (*unbiased_exp - (sem.smallest_normalized_exponent as i64 - 1)) as u32;
let needed_shift = (target_msb - msb).min(max_shift);
if needed_shift > 0 {
shl_vec(sig_words, needed_shift);
*unbiased_exp -= needed_shift as i64;
}
}
let bias = sem.exponent_bias as i64;
let biased_raw = *unbiased_exp + bias;
let (biased_exp, inexact) = if biased_raw <= 0 {
let shift = (1 - biased_raw) as u32;
let _ = shr_vec(sig_words, shift);
Self::round_and_renormalize(sig_words, 0, target_bits, rm, sign, sem)
} else if biased_raw >= sem.largest_exponent as i64 {
sig_words.clear();
if sem.is_x87() {
sig_words.push(1u64 << 63);
}
(sem.largest_exponent, true)
} else {
Self::round_and_renormalize(sig_words, biased_raw as u32, target_bits, rm, sign, sem)
};
(biased_exp, inexact)
}
fn round_to_integral(&self, rm: RoundingMode) -> APFloat {
if self.is_zero() || self.is_infinity() || self.is_nan() {
return self.clone();
}
if self.exponent as i64 - self.semantics.exponent_bias as i64
>= self.semantics.fraction_bits() as i64
{
return self.clone();
}
if self.unbiased_exponent() < 0 {
match rm {
RoundingMode::TowardNegative => {
if self.sign {
return Self::from_f64(-1.0).convert(self.semantics, rm);
} else {
return Self::get_zero(self.semantics, false);
}
}
RoundingMode::TowardPositive => {
if self.sign {
return Self::get_zero(self.semantics, true);
} else {
return Self::from_f64(1.0).convert(self.semantics, rm);
}
}
RoundingMode::NearestTiesToAway => {
let half = Self::from_f64(0.5).convert(self.semantics, rm);
let abs_self = self.abs();
if abs_self.compare(&half) == Some(Ordering::Greater) {
let one = Self::from_f64(1.0).convert(self.semantics, rm);
return if self.sign { one.negate() } else { one };
} else {
return Self::get_zero(self.semantics, self.sign);
}
}
_ => {
return Self::get_zero(self.semantics, self.sign);
}
}
}
let frac_pos = (self.semantics.fraction_bits() as i64 - self.unbiased_exponent()) as u32;
let mut result = self.clone();
let mask_shift = frac_pos;
let _sticky = shr_vec(&mut result.significand, mask_shift);
shl_vec(&mut result.significand, mask_shift);
let _sticky = false; let target_bits = self.semantics.total_significand_bits();
let (new_exp, _inexact) = Self::round_and_renormalize(
&mut result.significand,
result.exponent,
target_bits,
rm,
result.sign,
&self.semantics,
);
result.exponent = new_exp;
result.flags = APFLOAT_OK;
result
}
}
#[allow(dead_code)]
fn count_leading_zeros_sig(words: &[u64]) -> u32 {
if words.is_empty() || words.iter().all(|&w| w == 0) {
return 0;
}
let mut lz = 0u32;
for i in (0..words.len()).rev() {
let w = words[i];
if w == 0 {
lz += 64;
} else {
lz += w.leading_zeros();
break;
}
}
lz
}
impl APFloat {
pub fn compare(&self, other: &APFloat) -> Option<Ordering> {
if self.is_nan() || other.is_nan() {
return None;
}
let self_zero = self.is_zero();
let other_zero = other.is_zero();
if self_zero && other_zero {
return Some(Ordering::Equal);
}
if self.sign != other.sign {
if self.sign {
return Some(Ordering::Less);
} else {
return Some(Ordering::Greater);
}
}
let negate_result = self.sign;
if self_zero {
return Some(if negate_result {
Ordering::Greater
} else {
Ordering::Less
});
}
if other_zero {
return Some(if negate_result {
Ordering::Less
} else {
Ordering::Greater
});
}
let self_inf = self.is_infinity();
let other_inf = other.is_infinity();
if self_inf && other_inf {
return Some(Ordering::Equal);
}
if self_inf {
return Some(if negate_result {
Ordering::Less
} else {
Ordering::Greater
});
}
if other_inf {
return Some(if negate_result {
Ordering::Greater
} else {
Ordering::Less
});
}
let exp_a = self.unbiased_exponent();
let exp_b = other.unbiased_exponent();
let ord = if exp_a != exp_b {
if exp_a > exp_b {
Ordering::Greater
} else {
Ordering::Less
}
} else {
let internal_width = self.semantics.total_significand_bits() * 2;
let sig_a = self.sig_apint(internal_width);
let sig_b = other.sig_apint(internal_width);
if sig_a.ugt(&sig_b) {
Ordering::Greater
} else if sig_a.ult(&sig_b) {
Ordering::Less
} else {
Ordering::Equal
}
};
if negate_result {
Some(ord.reverse())
} else {
Some(ord)
}
}
pub fn bitwise_identical(&self, other: &APFloat) -> bool {
self.sign == other.sign
&& self.exponent == other.exponent
&& self.significand == other.significand
}
}
impl APFloat {
pub fn to_f64(&self, rm: RoundingMode) -> f64 {
if self.semantics == IEEE_DOUBLE {
self.to_native_f64()
} else {
let converted = self.convert(IEEE_DOUBLE, rm);
converted.to_native_f64()
}
}
pub fn to_f32(&self, rm: RoundingMode) -> f32 {
if self.semantics == IEEE_SINGLE {
self.to_native_f32()
} else {
let converted = self.convert(IEEE_SINGLE, rm);
converted.to_native_f32()
}
}
fn to_native_f64(&self) -> f64 {
let mut bits: u64 = 0;
if self.sign {
bits |= 1u64 << 63;
}
let exp = (self.exponent as u64) & 0x7FF;
bits |= exp << 52;
if self.exponent == IEEE_DOUBLE.largest_exponent {
if !self.sig_is_zero() {
let mut mantissa: u64 = 0;
for (i, &w) in self.significand.iter().enumerate() {
mantissa |= w << (i * 64);
}
mantissa &= (1u64 << 52) - 1;
bits |= mantissa;
}
} else if self.exponent == 0 {
if !self.sig_is_zero() {
let mut mantissa: u64 = 0;
for (i, &w) in self.significand.iter().enumerate() {
mantissa |= w << (i * 64);
}
mantissa &= (1u64 << 52) - 1;
bits |= mantissa;
}
} else {
let mut mantissa: u64 = 0;
for (i, &w) in self.significand.iter().enumerate() {
mantissa |= w << (i * 64);
}
mantissa &= (1u64 << 52) - 1;
bits |= mantissa;
}
f64::from_bits(bits)
}
fn to_native_f32(&self) -> f32 {
let mut bits: u32 = 0;
if self.sign {
bits |= 1u32 << 31;
}
let exp = (self.exponent & 0xFF) as u32;
bits |= exp << 23;
if self.exponent == IEEE_SINGLE.largest_exponent {
if !self.sig_is_zero() {
let mantissa = self.significand.first().copied().unwrap_or(0) as u32;
bits |= mantissa & ((1u32 << 23) - 1);
}
} else if self.exponent == 0 {
if !self.sig_is_zero() {
let mantissa = self.significand.first().copied().unwrap_or(0) as u32;
bits |= mantissa & ((1u32 << 23) - 1);
}
} else {
let mantissa = self.significand.first().copied().unwrap_or(0) as u32;
bits |= mantissa & ((1u32 << 23) - 1);
}
f32::from_bits(bits)
}
pub fn to_apint(&self) -> APInt {
let sem = &self.semantics;
let bit_width = sem.bits;
let mut result = APInt::get_zero(bit_width);
if self.sign {
result.set_bit(bit_width - 1);
}
for i in 0..sem.exponent_bits {
if (self.exponent >> i) & 1 != 0 {
result.set_bit(sem.mantissa_bits + i);
}
}
let frac_bits = sem.fraction_bits();
for i in 0..frac_bits {
if self.sig_get_bit(i) {
result.set_bit(i);
}
}
if sem.is_x87() && !self.is_zero() {
if self.is_infinity() || self.is_nan() || self.exponent > 0 {
result.set_bit(63);
}
}
result
}
pub fn to_string(&self, precision: usize, max_padding: usize) -> String {
if self.is_nan() {
return "nan".to_string();
}
if self.is_infinity() {
return if self.sign { "-inf" } else { "inf" }.to_string();
}
if self.is_zero() {
return if self.sign { "-0.0" } else { "0.0" }.to_string();
}
let sign_str = if self.sign { "-" } else { "" };
let unbiased = self.unbiased_exponent();
let frac_bits = self.semantics.fraction_bits();
let hex_digits = (precision.max(1) + 3) / 4;
let mut hex_str = String::new();
let int_bit = if self.exponent > 0 || self.semantics.is_x87() {
1u8
} else {
0u8
};
let mut frac_accum: u64 = 0;
let mut bits_collected = 0u32;
for i in (0..frac_bits).rev() {
frac_accum = (frac_accum << 1) | (if self.sig_get_bit(i) { 1 } else { 0 });
bits_collected += 1;
if bits_collected == 4 {
hex_str.push(char::from_digit(frac_accum as u32, 16).unwrap_or('0'));
frac_accum = 0;
bits_collected = 0;
}
}
if bits_collected > 0 {
frac_accum <<= 4 - bits_collected;
hex_str.push(char::from_digit(frac_accum as u32, 16).unwrap_or('0'));
}
if hex_str.len() < hex_digits {
let pad = hex_digits - hex_str.len();
for _ in 0..pad.min(max_padding) {
hex_str.push('0');
}
} else if hex_str.len() > hex_digits {
hex_str.truncate(hex_digits);
}
format!("{}0x{}.{}p{}", sign_str, int_bit, hex_str, unbiased)
}
pub fn convert(&self, new_sem: FloatSemantics, rm: RoundingMode) -> APFloat {
if self.semantics == new_sem {
return self.clone();
}
if self.is_nan() {
let mut result = Self::get_qnan(new_sem, self.sign);
result.flags = self.flags;
return result;
}
if self.is_infinity() {
let mut result = Self::get_inf(new_sem, self.sign);
result.flags = APFLOAT_OK;
return result;
}
if self.is_zero() {
let mut result = Self::get_zero(new_sem, self.sign);
result.flags = APFLOAT_OK;
return result;
}
if new_sem.is_ppc_double_double() {
return self.convert_to_ppc_double_double(rm);
}
if self.semantics.is_ppc_double_double() {
return self.convert_from_ppc_double_double(new_sem, rm);
}
let target_bits = new_sem.total_significand_bits();
let _internal_width = target_bits.max(self.semantics.total_significand_bits()) * 4 + 10;
let mut sig_words = self.significand.clone();
while sig_words.len() > 1 && sig_words.last() == Some(&0) {
sig_words.pop();
}
let unbiased = self.unbiased_exponent();
let old_target = self.semantics.total_significand_bits();
let new_target = new_sem.total_significand_bits();
let mut cur_unbiased = unbiased;
if new_target > old_target {
let extend_shift = new_target - old_target;
shl_vec(&mut sig_words, extend_shift);
}
if new_target < old_target {
let (carry, _inexact) =
Self::round_significand(&mut sig_words, new_target, rm, self.sign);
if carry {
let _ = shr_vec(&mut sig_words, 1);
let set_pos = new_target - 1;
let w = (set_pos / 64) as usize;
let b = set_pos % 64;
if w >= sig_words.len() {
sig_words.resize(w + 1, 0);
}
sig_words[w] |= 1u64 << b;
cur_unbiased += 1;
}
}
let trunc_shift = if sig_msb_position(&sig_words) >= new_target {
sig_msb_position(&sig_words) - new_target + 1
} else {
0
};
if trunc_shift > 0 {
let _ = shr_vec(&mut sig_words, trunc_shift);
cur_unbiased += trunc_shift as i64;
}
let bias = new_sem.exponent_bias as i64;
let biased = cur_unbiased + bias;
let (final_exp, final_sig) = if biased <= 0 {
let shift = (1 - biased) as u32;
let _ = shr_vec(&mut sig_words, shift);
(0u32, sig_words)
} else if biased >= new_sem.largest_exponent as i64 {
let mut inf_sig = Vec::new();
if new_sem.is_x87() {
inf_sig.push(1u64 << 63);
}
(new_sem.largest_exponent, inf_sig)
} else {
(biased as u32, sig_words)
};
let mut result_sig = final_sig;
let (final_exp, _inexact) = if final_exp == new_sem.largest_exponent && result_sig.is_empty() {
(final_exp, false)
} else {
Self::round_and_renormalize(
&mut result_sig,
final_exp,
new_target,
rm,
self.sign,
&new_sem,
)
};
while result_sig.len() > 1 && result_sig.last() == Some(&0) {
result_sig.pop();
}
let mut result = APFloat {
semantics: new_sem,
sign: self.sign,
exponent: final_exp,
significand: result_sig,
flags: APFLOAT_OK,
};
if final_exp == new_sem.largest_exponent && !result.is_nan() {
result.flags |= APFLOAT_OVERFLOW | APFLOAT_INEXACT;
}
result
}
fn convert_to_ppc_double_double(&self, rm: RoundingMode) -> APFloat {
let high = self.convert(IEEE_DOUBLE, rm);
let high_as_self_sem = high.convert(self.semantics, rm);
let low_exact = self.sub(&high_as_self_sem, rm);
let low = low_exact.convert(IEEE_DOUBLE, rm);
let mut result = high.clone();
result.semantics = PPC_DOUBLE_DOUBLE;
result.flags = high.flags | low.flags;
result
}
fn convert_from_ppc_double_double(&self, new_sem: FloatSemantics, rm: RoundingMode) -> APFloat {
let high_bits = self.significand.first().copied().unwrap_or(0);
let high_f64 = f64::from_bits(high_bits);
let high = APFloat::from_f64(high_f64);
high.convert(new_sem, rm)
}
}
impl APFloat {
pub fn from_string(s: &str, sem: FloatSemantics) -> Result<Self, String> {
let s = s.trim().to_lowercase();
if s.is_empty() {
return Err("empty string".to_string());
}
let (negative, rest) = if s.starts_with('-') {
(true, &s[1..])
} else if s.starts_with('+') {
(false, &s[1..])
} else {
(false, s.as_str())
};
if rest == "inf" || rest == "infinity" {
return Ok(Self::get_inf(sem, negative));
}
if rest.starts_with("nan") {
let payload = if rest.starts_with("nan(") && rest.ends_with(')') {
let inner = &rest[4..rest.len() - 1];
if inner.is_empty() {
None
} else {
let (radix, digits) = if inner.starts_with("0x") || inner.starts_with("0X") {
(16, &inner[2..])
} else {
(10, inner)
};
u64::from_str_radix(digits, radix).ok()
}
} else {
None
};
let mut result = Self::get_nan(sem, negative, payload);
result.flags = APFLOAT_OK;
return Ok(result);
}
if rest.starts_with("0x") || rest.starts_with("0X") {
return Self::from_hex_string(rest, negative, sem);
}
Self::from_decimal_string(rest, negative, sem)
}
fn from_hex_string(s: &str, negative: bool, sem: FloatSemantics) -> Result<Self, String> {
let s = s.to_lowercase();
let s = if s.starts_with("0x") {
&s[2..]
} else {
s.as_str()
};
let (mantissa_str, exp_str) = if let Some(pos) = s.find('p') {
(&s[..pos], &s[pos + 1..])
} else {
return Err("hex float missing exponent (p)".to_string());
};
let exp: i64 = exp_str
.parse()
.map_err(|_| format!("invalid hex exponent: {}", exp_str))?;
let (int_part, frac_part) = if let Some(pos) = mantissa_str.find('.') {
(&mantissa_str[..pos], &mantissa_str[pos + 1..])
} else {
(mantissa_str, "")
};
let combined = if frac_part.is_empty() {
int_part.to_string()
} else {
format!("{}{}", int_part, frac_part)
};
if combined.is_empty() {
return Ok(Self::get_zero(sem, negative));
}
let int_val = APInt::from_string(&combined, 16)
.map_err(|e| format!("invalid hex mantissa: {}", e))?;
let frac_len = frac_part.len() as i64;
let binary_exp = exp - 4 * frac_len;
Self::from_apint_and_exp(&int_val, binary_exp, negative, sem)
}
fn from_decimal_string(s: &str, negative: bool, sem: FloatSemantics) -> Result<Self, String> {
let (mantissa_str, exp_str) = if let Some(pos) = s.find('e') {
(&s[..pos], Some(&s[pos + 1..]))
} else {
(s, None)
};
let dec_exp: i64 = if let Some(es) = exp_str {
es.parse()
.map_err(|_| format!("invalid exponent: {}", es))?
} else {
0
};
let (int_part, frac_part) = if let Some(pos) = mantissa_str.find('.') {
(&mantissa_str[..pos], &mantissa_str[pos + 1..])
} else {
(mantissa_str, "")
};
let digits = format!("{}{}", int_part, frac_part);
if digits.is_empty() || digits.chars().all(|c| c == '0') {
return Ok(Self::get_zero(sem, negative));
}
let digits = digits.trim_start_matches('0');
if digits.is_empty() {
return Ok(Self::get_zero(sem, negative));
}
let frac_len = frac_part.len() as i64;
let effective_exp = dec_exp - frac_len;
let int_val = APInt::from_string(digits, 10)
.map_err(|e| format!("invalid decimal mantissa: {}", e))?;
Self::from_decimal_apint_and_exp(&int_val, effective_exp, negative, sem)
}
fn from_apint_and_exp(
mantissa: &APInt,
binary_exp: i64,
negative: bool,
sem: FloatSemantics,
) -> Result<Self, String> {
let words = mantissa.get_raw_data();
let mut sig = words.to_vec();
while sig.len() > 1 && sig.last() == Some(&0) {
sig.pop();
}
if sig.iter().all(|&w| w == 0) {
return Ok(Self::get_zero(sem, negative));
}
let target_bits = sem.total_significand_bits();
let msb = sig_msb_position(&sig);
let _sig_bits = msb + 1;
let mut unbiased_exp = binary_exp + sem.mantissa_bits as i64;
let (biased_exp, _inexact) = Self::normalize_and_round(
&mut sig,
&mut unbiased_exp,
target_bits,
RoundingMode::NearestTiesToEven,
negative,
&sem,
);
let mut result = APFloat {
semantics: sem,
sign: negative,
exponent: biased_exp,
significand: sig,
flags: APFLOAT_OK,
};
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
Ok(result)
}
fn from_decimal_apint_and_exp(
mantissa: &APInt,
dec_exp: i64,
negative: bool,
sem: FloatSemantics,
) -> Result<Self, String> {
let target_bits = sem.total_significand_bits();
let internal_width = target_bits * 4 + 20;
let mut sig = mantissa.get_raw_data().to_vec();
while sig.len() > 1 && sig.last() == Some(&0) {
sig.pop();
}
if sig.iter().all(|&w| w == 0) {
return Ok(Self::get_zero(sem, negative));
}
let mut binary_exp: i64 = 0;
let mut big = APInt::from_words(internal_width, sig);
if dec_exp > 0 {
let five = APInt::from_u64(internal_width, 5);
let mut remaining = dec_exp;
let mut pow5 = five.clone();
while remaining > 0 {
if remaining & 1 != 0 {
big = big.mul(&pow5);
}
pow5 = pow5.mul(&pow5);
remaining >>= 1;
}
binary_exp += dec_exp; } else if dec_exp < 0 {
let pos_exp = (-dec_exp) as u64;
let five = APInt::from_u64(internal_width, 5);
let mut remaining = pos_exp;
let mut pow5 = five.clone();
let mut pow5_val = APInt::from_u64(internal_width, 1);
while remaining > 0 {
if remaining & 1 != 0 {
pow5_val = pow5_val.mul(&pow5);
}
pow5 = pow5.mul(&pow5);
remaining >>= 1;
}
let extra_bits = target_bits + 10;
let mut wide_dividend = big.zext(internal_width + extra_bits + 64);
wide_dividend = wide_dividend.shl(extra_bits);
let divisor = pow5_val.zext(internal_width + extra_bits + 64);
wide_dividend = wide_dividend.udiv(&divisor);
big = wide_dividend;
binary_exp += dec_exp - extra_bits as i64;
}
let mut sig = big.get_raw_data().to_vec();
while sig.len() > 1 && sig.last() == Some(&0) {
sig.pop();
}
if sig.iter().all(|&w| w == 0) {
return Ok(Self::get_zero(sem, negative));
}
let _msb = sig_msb_position(&sig);
let mut unbiased_exp = binary_exp + sem.mantissa_bits as i64;
let (biased_exp, _inexact) = Self::normalize_and_round(
&mut sig,
&mut unbiased_exp,
target_bits,
RoundingMode::NearestTiesToEven,
negative,
&sem,
);
let mut result = APFloat {
semantics: sem,
sign: negative,
exponent: biased_exp,
significand: sig,
flags: APFLOAT_OK,
};
while result.significand.len() > 1 && result.significand.last() == Some(&0) {
result.significand.pop();
}
Ok(result)
}
}
impl fmt::Debug for APFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"APFloat({}, {})",
self.semantics.name,
self.to_string(6, 0)
)
}
}
impl fmt::Display for APFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string(6, 0))
}
}
impl PartialEq for APFloat {
fn eq(&self, other: &APFloat) -> bool {
self.compare(other) == Some(Ordering::Equal)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_zero() {
let f = APFloat::new(IEEE_DOUBLE);
assert!(f.is_zero());
assert!(!f.sign);
assert_eq!(f.classify(), FloatCategory::Zero);
}
#[test]
fn test_get_zero() {
let pos = APFloat::get_zero(IEEE_DOUBLE, false);
let neg = APFloat::get_zero(IEEE_DOUBLE, true);
assert!(pos.is_zero());
assert!(neg.is_zero());
assert!(!pos.is_negative());
assert!(neg.is_negative());
assert_eq!(pos.compare(&neg), Some(Ordering::Equal));
}
#[test]
fn test_get_inf() {
let pos_inf = APFloat::get_inf(IEEE_DOUBLE, false);
let neg_inf = APFloat::get_inf(IEEE_DOUBLE, true);
assert!(pos_inf.is_infinity());
assert!(neg_inf.is_infinity());
assert!(!pos_inf.is_negative());
assert!(neg_inf.is_negative());
assert!(pos_inf.is_positively_infinity());
assert!(neg_inf.is_negatively_infinity());
}
#[test]
fn test_get_nan() {
let nan = APFloat::get_nan(IEEE_DOUBLE, false, None);
assert!(nan.is_nan());
assert!(!nan.is_signaling_nan());
let snan = APFloat::get_snan(IEEE_DOUBLE, false);
assert!(snan.is_nan());
assert!(snan.is_signaling_nan());
}
#[test]
fn test_get_qnan() {
let qnan = APFloat::get_qnan(IEEE_DOUBLE, true);
assert!(qnan.is_nan());
assert!(!qnan.is_signaling_nan());
assert!(qnan.is_negative());
}
#[test]
fn test_get_snan() {
let snan = APFloat::get_snan(IEEE_DOUBLE, true);
assert!(snan.is_nan());
assert!(snan.is_signaling_nan());
assert!(snan.is_negative());
}
#[test]
fn test_get_snan_single() {
let snan = APFloat::get_snan(IEEE_SINGLE, false);
assert!(snan.is_nan());
assert!(snan.is_signaling_nan());
}
#[test]
fn test_largest() {
let largest = APFloat::largest(IEEE_DOUBLE, false);
assert!(largest.is_normal());
assert!(!largest.is_negative());
let val = largest.to_f64(RoundingMode::NearestTiesToEven);
assert_eq!(val, f64::MAX);
}
#[test]
fn test_smallest_normalized() {
let smallest = APFloat::smallest_normalized(IEEE_DOUBLE, false);
assert!(smallest.is_normal());
let val = smallest.to_f64(RoundingMode::NearestTiesToEven);
assert_eq!(val, f64::MIN_POSITIVE);
}
#[test]
fn test_f64_roundtrip_zero() {
let f = APFloat::from_f64(0.0);
assert!(f.is_zero());
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), 0.0);
}
#[test]
fn test_f64_roundtrip_neg_zero() {
let f = APFloat::from_f64(-0.0);
assert!(f.is_zero());
assert!(f.is_negative());
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), -0.0);
}
#[test]
fn test_f64_roundtrip_one() {
let f = APFloat::from_f64(1.0);
assert!(!f.is_zero());
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), 1.0);
}
#[test]
fn test_f64_roundtrip_neg_one() {
let f = APFloat::from_f64(-1.0);
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), -1.0);
}
#[test]
fn test_f64_roundtrip_pi() {
let f = APFloat::from_f64(std::f64::consts::PI);
assert_eq!(
f.to_f64(RoundingMode::NearestTiesToEven),
std::f64::consts::PI
);
}
#[test]
fn test_f64_roundtrip_inf() {
let f = APFloat::from_f64(f64::INFINITY);
assert!(f.is_infinity());
assert!(!f.is_negative());
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), f64::INFINITY);
}
#[test]
fn test_f64_roundtrip_neg_inf() {
let f = APFloat::from_f64(f64::NEG_INFINITY);
assert!(f.is_infinity());
assert!(f.is_negative());
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), f64::NEG_INFINITY);
}
#[test]
fn test_f64_roundtrip_nan() {
let f = APFloat::from_f64(f64::NAN);
assert!(f.is_nan());
let result = f.to_f64(RoundingMode::NearestTiesToEven);
assert!(result.is_nan());
}
#[test]
fn test_f32_roundtrip_zero() {
let f = APFloat::from_f32(0.0f32);
assert!(f.is_zero());
assert_eq!(f.to_f32(RoundingMode::NearestTiesToEven), 0.0f32);
}
#[test]
fn test_f32_roundtrip_one() {
let f = APFloat::from_f32(1.0f32);
assert_eq!(f.to_f32(RoundingMode::NearestTiesToEven), 1.0f32);
}
#[test]
fn test_f32_roundtrip_inf() {
let f = APFloat::from_f32(f32::INFINITY);
assert!(f.is_infinity());
assert_eq!(f.to_f32(RoundingMode::NearestTiesToEven), f32::INFINITY);
}
#[test]
fn test_f64_roundtrip_subnormal() {
let val = f64::from_bits(1); let f = APFloat::from_f64(val);
assert!(f.is_subnormal());
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), val);
}
#[test]
fn test_f64_roundtrip_max() {
let f = APFloat::from_f64(f64::MAX);
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), f64::MAX);
}
#[test]
fn test_f64_roundtrip_min_positive() {
let f = APFloat::from_f64(f64::MIN_POSITIVE);
assert!(f.is_normal());
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), f64::MIN_POSITIVE);
}
#[test]
fn test_add_basic() {
let a = APFloat::from_f64(1.5);
let b = APFloat::from_f64(2.5);
let sum = a.add(&b, RoundingMode::NearestTiesToEven);
assert_eq!(sum.to_f64(RoundingMode::NearestTiesToEven), 4.0);
}
#[test]
fn test_add_negatives() {
let a = APFloat::from_f64(-1.5);
let b = APFloat::from_f64(-2.5);
let sum = a.add(&b, RoundingMode::NearestTiesToEven);
assert_eq!(sum.to_f64(RoundingMode::NearestTiesToEven), -4.0);
}
#[test]
fn test_add_mixed_signs() {
let a = APFloat::from_f64(3.0);
let b = APFloat::from_f64(-1.0);
let sum = a.add(&b, RoundingMode::NearestTiesToEven);
assert_eq!(sum.to_f64(RoundingMode::NearestTiesToEven), 2.0);
}
#[test]
fn test_sub_basic() {
let a = APFloat::from_f64(5.0);
let b = APFloat::from_f64(3.0);
let diff = a.sub(&b, RoundingMode::NearestTiesToEven);
assert_eq!(diff.to_f64(RoundingMode::NearestTiesToEven), 2.0);
}
#[test]
fn test_mul_basic() {
let a = APFloat::from_f64(2.0);
let b = APFloat::from_f64(3.0);
let prod = a.mul(&b, RoundingMode::NearestTiesToEven);
assert_eq!(prod.to_f64(RoundingMode::NearestTiesToEven), 6.0);
}
#[test]
fn test_mul_negatives() {
let a = APFloat::from_f64(-2.0);
let b = APFloat::from_f64(3.0);
let prod = a.mul(&b, RoundingMode::NearestTiesToEven);
assert_eq!(prod.to_f64(RoundingMode::NearestTiesToEven), -6.0);
}
#[test]
fn test_div_basic() {
let a = APFloat::from_f64(6.0);
let b = APFloat::from_f64(2.0);
let quot = a.div(&b, RoundingMode::NearestTiesToEven);
assert_eq!(quot.to_f64(RoundingMode::NearestTiesToEven), 3.0);
}
#[test]
fn test_div_negatives() {
let a = APFloat::from_f64(-6.0);
let b = APFloat::from_f64(2.0);
let quot = a.div(&b, RoundingMode::NearestTiesToEven);
assert_eq!(quot.to_f64(RoundingMode::NearestTiesToEven), -3.0);
}
#[test]
fn test_add_inf_inf_same_sign() {
let a = APFloat::get_inf(IEEE_DOUBLE, false);
let b = APFloat::get_inf(IEEE_DOUBLE, false);
let sum = a.add(&b, RoundingMode::NearestTiesToEven);
assert!(sum.is_infinity());
assert!(!sum.is_negative());
}
#[test]
fn test_add_inf_neg_inf() {
let a = APFloat::get_inf(IEEE_DOUBLE, false);
let b = APFloat::get_inf(IEEE_DOUBLE, true);
let sum = a.add(&b, RoundingMode::NearestTiesToEven);
assert!(sum.is_nan());
assert!(sum.flags & APFLOAT_INVALID_OP != 0);
}
#[test]
fn test_mul_inf_zero() {
let a = APFloat::get_inf(IEEE_DOUBLE, false);
let b = APFloat::get_zero(IEEE_DOUBLE, false);
let prod = a.mul(&b, RoundingMode::NearestTiesToEven);
assert!(prod.is_nan());
assert!(prod.flags & APFLOAT_INVALID_OP != 0);
}
#[test]
fn test_div_zero_by_zero() {
let a = APFloat::get_zero(IEEE_DOUBLE, false);
let b = APFloat::get_zero(IEEE_DOUBLE, false);
let quot = a.div(&b, RoundingMode::NearestTiesToEven);
assert!(quot.is_nan());
assert!(quot.flags & APFLOAT_INVALID_OP != 0);
}
#[test]
fn test_div_by_zero() {
let a = APFloat::from_f64(1.0);
let b = APFloat::get_zero(IEEE_DOUBLE, false);
let quot = a.div(&b, RoundingMode::NearestTiesToEven);
assert!(quot.is_infinity());
assert!(quot.flags & APFLOAT_DIV_BY_ZERO != 0);
}
#[test]
fn test_sqrt_negative() {
let a = APFloat::from_f64(-4.0);
let r = a.sqrt(RoundingMode::NearestTiesToEven);
assert!(r.is_nan());
assert!(r.flags & APFLOAT_INVALID_OP != 0);
}
#[test]
fn test_sqrt_zero() {
let a = APFloat::from_f64(0.0);
let r = a.sqrt(RoundingMode::NearestTiesToEven);
assert!(r.is_zero());
}
#[test]
fn test_sqrt_four() {
let a = APFloat::from_f64(4.0);
let r = a.sqrt(RoundingMode::NearestTiesToEven);
assert_eq!(r.to_f64(RoundingMode::NearestTiesToEven), 2.0);
}
#[test]
fn test_nan_add_propagation() {
let nan = APFloat::get_qnan(IEEE_DOUBLE, false);
let val = APFloat::from_f64(1.0);
let sum = nan.add(&val, RoundingMode::NearestTiesToEven);
assert!(sum.is_nan());
}
#[test]
fn test_snan_add_invalid() {
let snan = APFloat::get_snan(IEEE_DOUBLE, false);
let val = APFloat::from_f64(1.0);
let sum = snan.add(&val, RoundingMode::NearestTiesToEven);
assert!(sum.is_nan());
assert!(sum.flags & APFLOAT_INVALID_OP != 0);
}
#[test]
fn test_compare_nan_returns_none() {
let nan = APFloat::get_qnan(IEEE_DOUBLE, false);
let val = APFloat::from_f64(1.0);
assert_eq!(nan.compare(&val), None);
assert_eq!(val.compare(&nan), None);
assert_eq!(nan.compare(&nan), None);
}
#[test]
fn test_compare_zero_equal() {
let pos = APFloat::get_zero(IEEE_DOUBLE, false);
let neg = APFloat::get_zero(IEEE_DOUBLE, true);
assert_eq!(pos.compare(&neg), Some(Ordering::Equal));
}
#[test]
fn test_compare_neg_less_than_pos() {
let neg = APFloat::from_f64(-1.0);
let pos = APFloat::from_f64(1.0);
assert_eq!(neg.compare(&pos), Some(Ordering::Less));
assert_eq!(pos.compare(&neg), Some(Ordering::Greater));
}
#[test]
fn test_compare_infinity() {
let inf = APFloat::get_inf(IEEE_DOUBLE, false);
let max = APFloat::from_f64(f64::MAX);
assert_eq!(max.compare(&inf), Some(Ordering::Less));
assert_eq!(inf.compare(&max), Some(Ordering::Greater));
}
#[test]
fn test_bitwise_identical() {
let a = APFloat::from_f64(1.0);
let b = APFloat::from_f64(1.0);
assert!(a.bitwise_identical(&b));
}
#[test]
fn test_bitwise_not_identical() {
let a = APFloat::from_f64(1.0);
let b = APFloat::from_f64(2.0);
assert!(!a.bitwise_identical(&b));
}
#[test]
fn test_parse_decimal_simple() {
let f = APFloat::from_string("1.5", IEEE_DOUBLE).unwrap();
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), 1.5);
}
#[test]
fn test_parse_decimal_negative() {
let f = APFloat::from_string("-0.5", IEEE_DOUBLE).unwrap();
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), -0.5);
}
#[test]
fn test_parse_decimal_exponent() {
let f = APFloat::from_string("1.5e10", IEEE_DOUBLE).unwrap();
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), 1.5e10);
}
#[test]
fn test_parse_decimal_negative_exponent() {
let f = APFloat::from_string("1.5e-3", IEEE_DOUBLE).unwrap();
assert!((f.to_f64(RoundingMode::NearestTiesToEven) - 0.0015).abs() < 1e-10);
}
#[test]
fn test_parse_hex_float() {
let f = APFloat::from_string("0x1.5p3", IEEE_DOUBLE).unwrap();
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), 10.5);
}
#[test]
fn test_parse_hex_float_negative_exp() {
let f = APFloat::from_string("0x1.0p-2", IEEE_DOUBLE).unwrap();
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), 0.25);
}
#[test]
fn test_parse_inf() {
let f = APFloat::from_string("inf", IEEE_DOUBLE).unwrap();
assert!(f.is_infinity());
assert!(!f.is_negative());
}
#[test]
fn test_parse_neg_inf() {
let f = APFloat::from_string("-inf", IEEE_DOUBLE).unwrap();
assert!(f.is_infinity());
assert!(f.is_negative());
}
#[test]
fn test_parse_nan() {
let f = APFloat::from_string("nan", IEEE_DOUBLE).unwrap();
assert!(f.is_nan());
}
#[test]
fn test_parse_nan_payload() {
let f = APFloat::from_string("nan(42)", IEEE_DOUBLE).unwrap();
assert!(f.is_nan());
}
#[test]
fn test_parse_neg_nan() {
let f = APFloat::from_string("-nan", IEEE_DOUBLE).unwrap();
assert!(f.is_nan());
assert!(f.is_negative());
}
#[test]
fn test_convert_double_to_single() {
let d = APFloat::from_f64(std::f64::consts::PI);
let s = d.convert(IEEE_SINGLE, RoundingMode::NearestTiesToEven);
let f32_val = s.to_f32(RoundingMode::NearestTiesToEven);
assert!((f32_val as f64 - std::f64::consts::PI).abs() < 1e-6);
}
#[test]
fn test_convert_single_to_double() {
let s = APFloat::from_f32(1.5f32);
let d = s.convert(IEEE_DOUBLE, RoundingMode::NearestTiesToEven);
assert_eq!(d.to_f64(RoundingMode::NearestTiesToEven), 1.5);
}
#[test]
fn test_convert_overflow_to_inf() {
let large = APFloat::from_f64(1e300);
let s = large.convert(IEEE_SINGLE, RoundingMode::NearestTiesToEven);
assert!(s.is_infinity());
}
#[test]
fn test_classify_normal() {
let f = APFloat::from_f64(1.0);
assert_eq!(f.classify(), FloatCategory::Normal);
assert!(f.is_normal());
assert!(f.is_finite());
assert!(f.is_non_zero());
}
#[test]
fn test_classify_subnormal() {
let f = APFloat::from_f64(f64::from_bits(1));
assert_eq!(f.classify(), FloatCategory::Subnormal);
assert!(f.is_subnormal());
}
#[test]
fn test_classify_zero() {
let f = APFloat::from_f64(0.0);
assert_eq!(f.classify(), FloatCategory::Zero);
assert!(!f.is_non_zero());
}
#[test]
fn test_classify_inf() {
let f = APFloat::get_inf(IEEE_DOUBLE, false);
assert_eq!(f.classify(), FloatCategory::Infinity);
}
#[test]
fn test_classify_nan() {
let f = APFloat::get_qnan(IEEE_DOUBLE, false);
assert_eq!(f.classify(), FloatCategory::NaN);
}
#[test]
fn test_fma_basic() {
let a = APFloat::from_f64(2.0);
let b = APFloat::from_f64(3.0);
let c = APFloat::from_f64(4.0);
let r = a.fma(&b, &c, RoundingMode::NearestTiesToEven);
assert_eq!(r.to_f64(RoundingMode::NearestTiesToEven), 10.0);
}
#[test]
fn test_fma_inf_zero() {
let a = APFloat::get_inf(IEEE_DOUBLE, false);
let b = APFloat::get_zero(IEEE_DOUBLE, false);
let c = APFloat::from_f64(1.0);
let r = a.fma(&b, &c, RoundingMode::NearestTiesToEven);
assert!(r.is_nan());
assert!(r.flags & APFLOAT_INVALID_OP != 0);
}
#[test]
fn test_sqrt_two() {
let a = APFloat::from_f64(2.0);
let r = a.sqrt(RoundingMode::NearestTiesToEven);
let diff = (r.to_f64(RoundingMode::NearestTiesToEven) - 2.0f64.sqrt()).abs();
assert!(diff < 1e-15);
}
#[test]
fn test_sqrt_inf() {
let a = APFloat::get_inf(IEEE_DOUBLE, false);
let r = a.sqrt(RoundingMode::NearestTiesToEven);
assert!(r.is_infinity());
assert!(!r.is_negative());
}
#[test]
fn test_negate() {
let a = APFloat::from_f64(1.0);
let b = a.negate();
assert_eq!(b.to_f64(RoundingMode::NearestTiesToEven), -1.0);
}
#[test]
fn test_abs() {
let a = APFloat::from_f64(-1.0);
let b = a.abs();
assert_eq!(b.to_f64(RoundingMode::NearestTiesToEven), 1.0);
}
#[test]
fn test_copy_sign() {
let a = APFloat::from_f64(1.0);
let b = APFloat::from_f64(-2.0);
let c = a.copy_sign(&b);
assert_eq!(c.to_f64(RoundingMode::NearestTiesToEven), -1.0);
}
#[test]
fn test_from_apint_zero() {
let bits = APInt::from_u64(64, 0);
let f = APFloat::from_apint(&bits, IEEE_DOUBLE);
assert!(f.is_zero());
}
#[test]
fn test_from_apint_one() {
let bits = APInt::from_u64(64, 0x3FF0000000000000u64);
let f = APFloat::from_apint(&bits, IEEE_DOUBLE);
assert_eq!(f.to_f64(RoundingMode::NearestTiesToEven), 1.0);
}
#[test]
fn test_to_apint_roundtrip() {
let f = APFloat::from_f64(1.5);
let bits = f.to_apint();
let f2 = APFloat::from_apint(&bits, IEEE_DOUBLE);
assert!(f.bitwise_identical(&f2));
}
#[test]
fn test_x87_zero() {
let f = APFloat::get_zero(X87_DOUBLE_EXTENDED, false);
assert!(f.is_zero());
}
#[test]
fn test_x87_inf() {
let f = APFloat::get_inf(X87_DOUBLE_EXTENDED, false);
assert!(f.is_infinity());
assert!(!f.is_negative());
}
#[test]
fn test_x87_nan() {
let f = APFloat::get_qnan(X87_DOUBLE_EXTENDED, false);
assert!(f.is_nan());
}
#[test]
fn test_round_toward_zero() {
let a = APFloat::from_f64(1.7);
let b = APFloat::from_f64(1.0);
let diff = a.sub(&b, RoundingMode::TowardZero);
assert_eq!(diff.to_f64(RoundingMode::NearestTiesToEven), 0.7);
}
#[test]
fn test_flags_clear_on_new_op() {
let a = APFloat::get_inf(IEEE_DOUBLE, false);
let b = APFloat::get_inf(IEEE_DOUBLE, true);
let r = a.add(&b, RoundingMode::NearestTiesToEven);
assert!(r.flags & APFLOAT_INVALID_OP != 0);
let c = APFloat::from_f64(1.0);
assert_eq!(c.flags, APFLOAT_OK);
}
#[test]
fn test_display() {
let f = APFloat::from_f64(1.0);
let s = format!("{}", f);
assert!(!s.is_empty());
}
#[test]
fn test_debug() {
let f = APFloat::from_f64(1.0);
let s = format!("{:?}", f);
assert!(s.contains("APFloat"));
}
#[test]
fn test_ppc_double_double_zero() {
let f = APFloat::get_zero(PPC_DOUBLE_DOUBLE, false);
assert!(f.is_zero());
}
#[test]
fn test_ppc_double_double_inf() {
let f = APFloat::get_inf(PPC_DOUBLE_DOUBLE, false);
assert!(f.is_infinity());
}
}