更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

@@ -0,0 +1,59 @@
#![cfg(feature = "compact")]
#![allow(dead_code)]
use minimal_lexical::bellerophon::bellerophon;
use minimal_lexical::extended_float::{extended_to_float, ExtendedFloat};
use minimal_lexical::num::Float;
use minimal_lexical::number::Number;
pub fn bellerophon_test<F: Float + core::fmt::Debug>(
xmant: u64,
xexp: i32,
many_digits: bool,
ymant: u64,
yexp: i32,
) {
let num = Number {
exponent: xexp,
mantissa: xmant,
many_digits,
};
let xfp = bellerophon::<F>(&num);
let yfp = ExtendedFloat {
mant: ymant,
exp: yexp,
};
// Given us useful error messages if the floats are valid.
if xfp.exp >= 0 && yfp.exp >= 0 {
assert!(
xfp == yfp,
"x != y, xfp={:?}, yfp={:?}, x={:?}, y={:?}",
xfp,
yfp,
extended_to_float::<F>(xfp),
extended_to_float::<F>(yfp)
);
} else {
assert_eq!(xfp, yfp);
}
}
pub fn compute_float32(q: i32, w: u64) -> (i32, u64) {
let num = Number {
exponent: q,
mantissa: w,
many_digits: false,
};
let fp = bellerophon::<f32>(&num);
(fp.exp, fp.mant)
}
pub fn compute_float64(q: i32, w: u64) -> (i32, u64) {
let num = Number {
exponent: q,
mantissa: w,
many_digits: false,
};
let fp = bellerophon::<f64>(&num);
(fp.exp, fp.mant)
}

View File

@@ -0,0 +1,231 @@
#![cfg(feature = "compact")]
mod bellerophon;
use bellerophon::{bellerophon_test, compute_float32, compute_float64};
use minimal_lexical::num::Float;
#[test]
fn halfway_round_down_test() {
// Halfway, round-down tests
bellerophon_test::<f64>(9007199254740992, 0, false, 0, 1076);
bellerophon_test::<f64>(
9007199254740993,
0,
false,
9223372036854776832,
1065 + f64::INVALID_FP,
);
bellerophon_test::<f64>(9007199254740994, 0, false, 1, 1076);
bellerophon_test::<f64>(18014398509481984, 0, false, 0, 1077);
bellerophon_test::<f64>(
18014398509481986,
0,
false,
9223372036854776832,
1066 + f64::INVALID_FP,
);
bellerophon_test::<f64>(18014398509481988, 0, false, 1, 1077);
bellerophon_test::<f64>(9223372036854775808, 0, false, 0, 1086);
bellerophon_test::<f64>(
9223372036854776832,
0,
false,
9223372036854776832,
1075 + f64::INVALID_FP,
);
bellerophon_test::<f64>(9223372036854777856, 0, false, 1, 1086);
// Add a 0 but say we're truncated.
bellerophon_test::<f64>(9007199254740992000, -3, true, 0, 1076);
bellerophon_test::<f64>(
9007199254740993000,
-3,
true,
9223372036854776832,
1065 + f64::INVALID_FP,
);
bellerophon_test::<f64>(9007199254740994000, -3, true, 1, 1076);
}
#[test]
fn halfway_round_up_test() {
// Halfway, round-up tests
bellerophon_test::<f64>(9007199254740994, 0, false, 1, 1076);
bellerophon_test::<f64>(
9007199254740995,
0,
false,
9223372036854778880,
1065 + f64::INVALID_FP,
);
bellerophon_test::<f64>(9007199254740996, 0, false, 2, 1076);
bellerophon_test::<f64>(18014398509481988, 0, false, 1, 1077);
bellerophon_test::<f64>(
18014398509481990,
0,
false,
9223372036854778880,
1066 + f64::INVALID_FP,
);
bellerophon_test::<f64>(18014398509481992, 0, false, 2, 1077);
bellerophon_test::<f64>(9223372036854777856, 0, false, 1, 1086);
bellerophon_test::<f64>(
9223372036854778880,
0,
false,
9223372036854778880,
1075 + f64::INVALID_FP,
);
bellerophon_test::<f64>(9223372036854779904, 0, false, 2, 1086);
// Add a 0 but say we're truncated.
bellerophon_test::<f64>(9007199254740994000, -3, true, 1, 1076);
bellerophon_test::<f64>(
9007199254740994990,
-3,
true,
9223372036854778869,
1065 + f64::INVALID_FP,
);
bellerophon_test::<f64>(
9007199254740995000,
-3,
true,
9223372036854778879,
1065 + f64::INVALID_FP,
);
bellerophon_test::<f64>(
9007199254740995010,
-3,
true,
9223372036854778890,
1065 + f64::INVALID_FP,
);
bellerophon_test::<f64>(9007199254740995050, -3, true, 2, 1076);
bellerophon_test::<f64>(9007199254740996000, -3, true, 2, 1076);
}
#[test]
fn extremes_test() {
// Need to check we get proper results with rounding for near-infinity
// and near-zero and/or denormal floats.
bellerophon_test::<f64>(5, -324, false, 1, 0);
bellerophon_test::<f64>(10, -324, false, 2, 0);
// This is very close to 2.4703282292062327206e-342.
bellerophon_test::<f64>(
2470328229206232720,
-342,
false,
18446744073709551608,
-64 + f64::INVALID_FP,
);
bellerophon_test::<f64>(2470328229206232721, -342, false, 9223372036854775808, -32831);
bellerophon_test::<f64>(2470328229206232725, -342, false, 9223372036854775824, -32831);
bellerophon_test::<f64>(2470328229206232726, -342, false, 1, 0);
bellerophon_test::<f64>(2470328229206232730, -342, false, 1, 0);
// Check very close to literal infinity.
// 17.976931348623155
// 1.797693134862315508561243283845062402343434371574593359244049e+308
// 1.797693134862315708145274237317043567980705675258449965989175e+308
bellerophon_test::<f64>(17976931348623155, 292, false, 4503599627370494, 2046);
bellerophon_test::<f64>(17976931348623156, 292, false, 4503599627370494, 2046);
bellerophon_test::<f64>(1797693134862315605, 290, false, 4503599627370494, 2046);
bellerophon_test::<f64>(1797693134862315607, 290, false, 4503599627370494, 2046);
bellerophon_test::<f64>(1797693134862315608, 290, false, 18446744073709548540, -30733);
bellerophon_test::<f64>(1797693134862315609, 290, false, 18446744073709548550, -30733);
bellerophon_test::<f64>(179769313486231561, 291, false, 4503599627370495, 2046);
bellerophon_test::<f64>(17976931348623157, 292, false, 4503599627370495, 2046);
// Check existing issues and underflow.
bellerophon_test::<f64>(2470328229206232726, -343, false, 0, 0);
bellerophon_test::<f64>(2470328229206232726, -342, false, 1, 0);
bellerophon_test::<f64>(1, -250, false, 1945308223406668, 192);
bellerophon_test::<f64>(1, -150, false, 2867420733609077, 524);
bellerophon_test::<f64>(1, -45, false, 1924152549665465, 873);
bellerophon_test::<f64>(1, -40, false, 400386103400348, 890);
bellerophon_test::<f64>(1, -20, false, 2142540351554083, 956);
bellerophon_test::<f64>(1, 0, false, 0, 1023);
bellerophon_test::<f64>(1, 20, false, 1599915997629504, 1089);
bellerophon_test::<f64>(1, 40, false, 3768206498159781, 1155);
bellerophon_test::<f64>(1, 150, false, 999684479948463, 1521);
bellerophon_test::<f64>(1, 250, false, 1786584717939204, 1853);
// Minimum positive normal float.
bellerophon_test::<f64>(22250738585072014, -324, false, 0, 1);
// Maximum positive subnormal float.
bellerophon_test::<f64>(2225073858507201, -323, false, 4503599627370495, 0);
// Next highest subnormal float.
bellerophon_test::<f64>(22250738585072004, -324, false, 4503599627370494, 0);
bellerophon_test::<f64>(22250738585072006, -324, false, 4503599627370494, 0);
bellerophon_test::<f64>(22250738585072007, -324, false, 4503599627370495, 0);
bellerophon_test::<f64>(222507385850720062, -325, false, 4503599627370494, 0);
bellerophon_test::<f64>(222507385850720063, -325, false, 4503599627370494, 0);
bellerophon_test::<f64>(222507385850720064, -325, false, 4503599627370494, 0);
bellerophon_test::<f64>(2225073858507200641, -326, false, 18446744073709545462, -32779);
bellerophon_test::<f64>(2225073858507200642, -326, false, 18446744073709545472, -32779);
bellerophon_test::<f64>(222507385850720065, -325, false, 4503599627370495, 0);
}
#[test]
fn compute_float_f32_test() {
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_float32(0, 16777216), (151, 0));
assert_eq!(compute_float32(0, 16777217), (111 + f32::INVALID_FP, 9223372586610589696));
assert_eq!(compute_float32(0, 16777218), (151, 1));
assert_eq!(compute_float32(0, 16777219), (111 + f32::INVALID_FP, 9223373686122217472));
assert_eq!(compute_float32(0, 16777220), (151, 2));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
assert_eq!(
compute_float32(-10, 167772170000000000),
(111 + f32::INVALID_FP, 9223372586610589696)
);
assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
// Let's check the lines to see if anything is different in table...
assert_eq!(
compute_float32(-10, 167772190000000000),
(111 + f32::INVALID_FP, 9223373686122217472)
);
assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
}
#[test]
fn compute_float_f64_test() {
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740993), (1065 + f64::INVALID_FP, 9223372036854776832));
assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
assert_eq!(compute_float64(0, 9007199254740995), (1065 + f64::INVALID_FP, 9223372036854778880));
assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
assert_eq!(
compute_float64(0, 18014398509481986),
(1066 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
assert_eq!(
compute_float64(0, 18014398509481990),
(1066 + f64::INVALID_FP, 9223372036854778880)
);
assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
assert_eq!(
compute_float64(-3, 9007199254740993000),
(1065 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
assert_eq!(
compute_float64(-3, 9007199254740995000),
(1065 + f64::INVALID_FP, 9223372036854778879)
);
assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
}

View File

@@ -0,0 +1,228 @@
/// Find and parse sign and get remaining bytes.
#[inline]
fn parse_sign<'a>(bytes: &'a [u8]) -> (bool, &'a [u8]) {
match bytes.get(0) {
Some(&b'+') => (true, &bytes[1..]),
Some(&b'-') => (false, &bytes[1..]),
_ => (true, bytes),
}
}
// Convert u8 to digit.
#[inline]
fn to_digit(c: u8) -> Option<u32> {
(c as char).to_digit(10)
}
// Add digit from exponent.
#[inline]
fn add_digit_i32(value: i32, digit: u32) -> Option<i32> {
return value.checked_mul(10)?.checked_add(digit as i32);
}
// Subtract digit from exponent.
#[inline]
fn sub_digit_i32(value: i32, digit: u32) -> Option<i32> {
return value.checked_mul(10)?.checked_sub(digit as i32);
}
// Convert character to digit.
#[inline]
fn is_digit(c: u8) -> bool {
to_digit(c).is_some()
}
// Split buffer at index.
#[inline]
fn split_at_index<'a>(digits: &'a [u8], index: usize) -> (&'a [u8], &'a [u8]) {
(&digits[..index], &digits[index..])
}
/// Consume until a an invalid digit is found.
///
/// - `digits` - Slice containing 0 or more digits.
#[inline]
fn consume_digits<'a>(digits: &'a [u8]) -> (&'a [u8], &'a [u8]) {
// Consume all digits.
let mut index = 0;
while index < digits.len() && is_digit(digits[index]) {
index += 1;
}
split_at_index(digits, index)
}
// Trim leading 0s.
#[inline]
fn ltrim_zero<'a>(bytes: &'a [u8]) -> &'a [u8] {
let count = bytes.iter().take_while(|&&si| si == b'0').count();
&bytes[count..]
}
// Trim trailing 0s.
#[inline]
fn rtrim_zero<'a>(bytes: &'a [u8]) -> &'a [u8] {
let count = bytes.iter().rev().take_while(|&&si| si == b'0').count();
let index = bytes.len() - count;
&bytes[..index]
}
// PARSERS
// -------
/// Parse the exponent of the float.
///
/// * `exponent` - Slice containing the exponent digits.
/// * `is_positive` - If the exponent sign is positive.
fn parse_exponent(exponent: &[u8], is_positive: bool) -> i32 {
// Parse the sign bit or current data.
let mut value: i32 = 0;
match is_positive {
true => {
for c in exponent {
value = match add_digit_i32(value, to_digit(*c).unwrap()) {
Some(v) => v,
None => return i32::max_value(),
};
}
},
false => {
for c in exponent {
value = match sub_digit_i32(value, to_digit(*c).unwrap()) {
Some(v) => v,
None => return i32::min_value(),
};
}
},
}
value
}
pub fn case_insensitive_starts_with<'a, 'b, Iter1, Iter2>(mut x: Iter1, mut y: Iter2) -> bool
where
Iter1: Iterator<Item = &'a u8>,
Iter2: Iterator<Item = &'b u8>,
{
// We use a faster optimization here for ASCII letters, which NaN
// and infinite strings **must** be. [A-Z] is 0x41-0x5A, while
// [a-z] is 0x61-0x7A. Therefore, the xor must be 0 or 32 if they
// are case-insensitive equal, but only if at least 1 of the inputs
// is an ASCII letter.
loop {
let yi = y.next();
if yi.is_none() {
return true;
}
let yi = *yi.unwrap();
let is_not_equal = x.next().map_or(true, |&xi| {
let xor = xi ^ yi;
xor != 0 && xor != 0x20
});
if is_not_equal {
return false;
}
}
}
/// Parse float from input bytes, returning the float and the remaining bytes.
///
/// * `bytes` - Array of bytes leading with float-data.
pub fn parse_float<'a, F>(bytes: &'a [u8]) -> (F, &'a [u8])
where
F: minimal_lexical::Float,
{
let start = bytes;
// Parse the sign.
let (is_positive, bytes) = parse_sign(bytes);
// Check NaN, Inf, Infinity
if case_insensitive_starts_with(bytes.iter(), b"NaN".iter()) {
let mut float = F::from_bits(F::EXPONENT_MASK | (F::HIDDEN_BIT_MASK >> 1));
if !is_positive {
float = -float;
}
return (float, &bytes[3..]);
} else if case_insensitive_starts_with(bytes.iter(), b"Infinity".iter()) {
let mut float = F::from_bits(F::EXPONENT_MASK);
if !is_positive {
float = -float;
}
return (float, &bytes[8..]);
} else if case_insensitive_starts_with(bytes.iter(), b"inf".iter()) {
let mut float = F::from_bits(F::EXPONENT_MASK);
if !is_positive {
float = -float;
}
return (float, &bytes[3..]);
}
// Extract and parse the float components:
// 1. Integer
// 2. Fraction
// 3. Exponent
let (integer_slc, bytes) = consume_digits(bytes);
let (fraction_slc, bytes) = match bytes.first() {
Some(&b'.') => consume_digits(&bytes[1..]),
_ => (&bytes[..0], bytes),
};
let (exponent, bytes) = match bytes.first() {
Some(&b'e') | Some(&b'E') => {
// Extract and parse the exponent.
let (is_positive, bytes) = parse_sign(&bytes[1..]);
let (exponent, bytes) = consume_digits(bytes);
(parse_exponent(exponent, is_positive), bytes)
},
_ => (0, bytes),
};
if bytes.len() == start.len() {
return (F::from_u64(0), bytes);
}
// Note: You may want to check and validate the float data here:
// 1). Many floats require integer or fraction digits, if a fraction
// is present.
// 2). All floats require either integer or fraction digits.
// 3). Some floats do not allow a '+' sign before the significant digits.
// 4). Many floats require exponent digits after the exponent symbol.
// 5). Some floats do not allow a '+' sign before the exponent.
// We now need to trim leading and trailing 0s from the integer
// and fraction, respectively. This is required to make the
// fast and moderate paths more efficient, and for the slow
// path.
let integer_slc = ltrim_zero(integer_slc);
let fraction_slc = rtrim_zero(fraction_slc);
// Create the float and return our data.
let mut float: F =
minimal_lexical::parse_float(integer_slc.iter(), fraction_slc.iter(), exponent);
if !is_positive {
float = -float;
}
(float, bytes)
}
macro_rules! b {
($x:literal) => {
$x.as_bytes()
};
}
#[test]
fn f32_test() {
assert_eq!(
(184467440000000000000.0, b!("\x00\x00006")),
parse_float::<f32>(b"000184467440737095516150\x00\x00006")
);
}
#[test]
fn f64_test() {
assert_eq!(
(184467440737095500000.0, b!("\x00\x00006")),
parse_float::<f64>(b"000184467440737095516150\x00\x00006")
);
}

View File

@@ -0,0 +1,378 @@
//! These tests are adapted from the Rust core library's unittests.
#![cfg(not(feature = "compact"))]
use minimal_lexical::lemire;
use minimal_lexical::num::Float;
fn compute_error32(q: i32, w: u64) -> (i32, u64) {
let fp = lemire::compute_error::<f32>(q, w);
(fp.exp, fp.mant)
}
fn compute_error64(q: i32, w: u64) -> (i32, u64) {
let fp = lemire::compute_error::<f64>(q, w);
(fp.exp, fp.mant)
}
fn compute_error_scaled32(q: i32, w: u64, lz: i32) -> (i32, u64) {
let fp = lemire::compute_error_scaled::<f32>(q, w, lz);
(fp.exp, fp.mant)
}
fn compute_error_scaled64(q: i32, w: u64, lz: i32) -> (i32, u64) {
let fp = lemire::compute_error_scaled::<f64>(q, w, lz);
(fp.exp, fp.mant)
}
fn compute_float32(q: i32, w: u64) -> (i32, u64) {
let fp = lemire::compute_float::<f32>(q, w);
(fp.exp, fp.mant)
}
fn compute_float64(q: i32, w: u64) -> (i32, u64) {
let fp = lemire::compute_float::<f64>(q, w);
(fp.exp, fp.mant)
}
#[test]
fn compute_error32_test() {
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_error32(0, 16777216), (111 + f32::INVALID_FP, 9223372036854775808));
assert_eq!(compute_error32(0, 16777217), (111 + f32::INVALID_FP, 9223372586610589696));
assert_eq!(compute_error32(0, 16777218), (111 + f32::INVALID_FP, 9223373136366403584));
assert_eq!(compute_error32(0, 16777219), (111 + f32::INVALID_FP, 9223373686122217472));
assert_eq!(compute_error32(0, 16777220), (111 + f32::INVALID_FP, 9223374235878031360));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(
compute_error32(-10, 167772160000000000),
(111 + f32::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error32(-10, 167772170000000000),
(111 + f32::INVALID_FP, 9223372586610589696)
);
assert_eq!(
compute_error32(-10, 167772180000000000),
(111 + f32::INVALID_FP, 9223373136366403584)
);
// Let's check the lines to see if anything is different in table...
assert_eq!(
compute_error32(-10, 167772190000000000),
(111 + f32::INVALID_FP, 9223373686122217472)
);
assert_eq!(
compute_error32(-10, 167772200000000000),
(111 + f32::INVALID_FP, 9223374235878031360)
);
}
#[test]
fn compute_error64_test() {
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_error64(0, 9007199254740992), (1065 + f64::INVALID_FP, 9223372036854775808));
assert_eq!(compute_error64(0, 9007199254740993), (1065 + f64::INVALID_FP, 9223372036854776832));
assert_eq!(compute_error64(0, 9007199254740994), (1065 + f64::INVALID_FP, 9223372036854777856));
assert_eq!(compute_error64(0, 9007199254740995), (1065 + f64::INVALID_FP, 9223372036854778880));
assert_eq!(compute_error64(0, 9007199254740996), (1065 + f64::INVALID_FP, 9223372036854779904));
assert_eq!(
compute_error64(0, 18014398509481984),
(1066 + f64::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error64(0, 18014398509481986),
(1066 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error64(0, 18014398509481988),
(1066 + f64::INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error64(0, 18014398509481990),
(1066 + f64::INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error64(0, 18014398509481992),
(1066 + f64::INVALID_FP, 9223372036854779904)
);
// Test a much closer set of examples.
assert_eq!(
compute_error64(0, 9007199254740991),
(1064 + f64::INVALID_FP, 18446744073709549568)
);
assert_eq!(
compute_error64(0, 9223372036854776831),
(1075 + f64::INVALID_FP, 9223372036854776830)
);
assert_eq!(
compute_error64(0, 9223372036854776832),
(1075 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error64(0, 9223372036854776833),
(1075 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error64(-42, 9123456727292927),
(925 + f64::INVALID_FP, 13021432563531497894)
);
assert_eq!(
compute_error64(-43, 91234567272929275),
(925 + f64::INVALID_FP, 13021432563531498606)
);
assert_eq!(
compute_error64(-42, 9123456727292928),
(925 + f64::INVALID_FP, 13021432563531499320)
);
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(
compute_error64(-3, 9007199254740992000),
(1065 + f64::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error64(-3, 9007199254740993000),
(1065 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error64(-3, 9007199254740994000),
(1065 + f64::INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error64(-3, 9007199254740995000),
(1065 + f64::INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error64(-3, 9007199254740996000),
(1065 + f64::INVALID_FP, 9223372036854779904)
);
// Test from errors in atof.
assert_eq!(
compute_error64(-18, 1000000178813934326),
(1012 + f64::INVALID_FP, 9223373686122217470)
);
// Check edge-cases from previous errors.
assert_eq!(
compute_error64(-342, 2470328229206232720),
(-64 + f64::INVALID_FP, 18446744073709551608)
);
}
#[test]
fn compute_error_scaled32_test() {
// These are the same examples above, just using pre-computed scaled values.
// These test near-halfway cases for single-precision floats.
assert_eq!(
compute_error_scaled32(0, 4611686018427387904, 39),
(111 + f32::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled32(0, 4611686293305294848, 39),
(111 + f32::INVALID_FP, 9223372586610589696)
);
assert_eq!(
compute_error_scaled32(0, 4611686568183201792, 39),
(111 + f32::INVALID_FP, 9223373136366403584)
);
assert_eq!(
compute_error_scaled32(0, 4611686843061108736, 39),
(111 + f32::INVALID_FP, 9223373686122217472)
);
assert_eq!(
compute_error_scaled32(0, 4611687117939015680, 39),
(111 + f32::INVALID_FP, 9223374235878031360)
);
assert_eq!(
compute_error_scaled32(-10, 9223372036854775808, 6),
(111 + f32::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled32(-10, 9223372586610589696, 6),
(111 + f32::INVALID_FP, 9223372586610589696)
);
assert_eq!(
compute_error_scaled32(-10, 9223373136366403584, 6),
(111 + f32::INVALID_FP, 9223373136366403584)
);
assert_eq!(
compute_error_scaled32(-10, 9223373686122217472, 6),
(111 + f32::INVALID_FP, 9223373686122217472)
);
assert_eq!(
compute_error_scaled32(-10, 9223374235878031360, 6),
(111 + f32::INVALID_FP, 9223374235878031360)
);
}
#[test]
fn compute_error_scaled64_test() {
// These are the same examples above, just using pre-computed scaled values.
// These test near-halfway cases for double-precision floats.
assert_eq!(
compute_error_scaled64(0, 4611686018427387904, 10),
(1065 + f64::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 10),
(1065 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388928, 10),
(1065 + f64::INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389440, 10),
(1065 + f64::INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389952, 10),
(1065 + f64::INVALID_FP, 9223372036854779904)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427387904, 9),
(1066 + f64::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 9),
(1066 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388928, 9),
(1066 + f64::INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389440, 9),
(1066 + f64::INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389952, 9),
(1066 + f64::INVALID_FP, 9223372036854779904)
);
// Test a much closer set of examples.
assert_eq!(
compute_error_scaled64(0, 9223372036854774784, 11),
(1064 + f64::INVALID_FP, 18446744073709549568)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388415, 0),
(1075 + f64::INVALID_FP, 9223372036854776830)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 0),
(1075 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 0),
(1075 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(-42, 6510716281765748947, 10),
(925 + f64::INVALID_FP, 13021432563531497894)
);
assert_eq!(
compute_error_scaled64(-43, 6510716281765749303, 7),
(925 + f64::INVALID_FP, 13021432563531498606)
);
assert_eq!(
compute_error_scaled64(-42, 6510716281765749660, 10),
(925 + f64::INVALID_FP, 13021432563531499320)
);
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(
compute_error_scaled64(-3, 9223372036854775808, 1),
(1065 + f64::INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854776832, 1),
(1065 + f64::INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854777856, 1),
(1065 + f64::INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854778880, 1),
(1065 + f64::INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854779904, 1),
(1065 + f64::INVALID_FP, 9223372036854779904)
);
// Test from errors in atof.
assert_eq!(
compute_error_scaled64(-18, 9223373686122217470, 4),
(1012 + f64::INVALID_FP, 9223373686122217470)
);
// Check edge-cases from previous errors.
assert_eq!(
compute_error_scaled64(-342, 9223372036854775804, 2),
(-64 + f64::INVALID_FP, 18446744073709551608)
);
}
#[test]
fn compute_float_f32_rounding() {
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_float32(0, 16777216), (151, 0));
assert_eq!(compute_float32(0, 16777217), (151, 0));
assert_eq!(compute_float32(0, 16777218), (151, 1));
assert_eq!(compute_float32(0, 16777219), (151, 2));
assert_eq!(compute_float32(0, 16777220), (151, 2));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
assert_eq!(compute_float32(-10, 167772170000000000), (151, 0));
assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
// Let's check the lines to see if anything is different in table...
assert_eq!(compute_float32(-10, 167772190000000000), (151, 2));
assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
}
#[test]
fn compute_float_f64_rounding() {
// Also need to check halfway cases **inside** that exponent range.
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740993), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
assert_eq!(compute_float64(0, 9007199254740995), (1076, 2));
assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
assert_eq!(compute_float64(0, 18014398509481986), (1077, 0));
assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
assert_eq!(compute_float64(0, 18014398509481990), (1077, 2));
assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
// Test a much closer set of examples.
assert_eq!(compute_float64(0, 9007199254740991), (1075, 4503599627370495));
assert_eq!(compute_float64(0, 9223372036854776831), (1086, 0));
assert_eq!(compute_float64(0, 9223372036854776832), (1086, 0));
assert_eq!(compute_float64(0, 9223372036854776833), (1086, 1));
assert_eq!(compute_float64(-42, 9123456727292927), (936, 1854521741541368));
assert_eq!(compute_float64(-43, 91234567272929275), (936, 1854521741541369));
assert_eq!(compute_float64(-42, 9123456727292928), (936, 1854521741541369));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0));
assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2));
assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
}

View File

@@ -0,0 +1,289 @@
#![cfg(all(not(feature = "std"), feature = "compact"))]
// These are adapted from libm, a port of musl libc's libm to Rust.
// libm can be found online [here](https://github.com/rust-lang/libm),
// and is similarly licensed under an Apache2.0/MIT license
use core::f64;
use minimal_lexical::libm;
#[test]
fn fabsf_sanity_test() {
assert_eq!(libm::fabsf(-1.0), 1.0);
assert_eq!(libm::fabsf(2.8), 2.8);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs
#[test]
fn fabsf_spec_test() {
assert!(libm::fabsf(f32::NAN).is_nan());
for f in [0.0, -0.0].iter().copied() {
assert_eq!(libm::fabsf(f), 0.0);
}
for f in [f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
assert_eq!(libm::fabsf(f), f32::INFINITY);
}
}
#[test]
fn sqrtf_sanity_test() {
assert_eq!(libm::sqrtf(100.0), 10.0);
assert_eq!(libm::sqrtf(4.0), 2.0);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt
#[test]
fn sqrtf_spec_test() {
// Not Asserted: FE_INVALID exception is raised if argument is negative.
assert!(libm::sqrtf(-1.0).is_nan());
assert!(libm::sqrtf(f32::NAN).is_nan());
for f in [0.0, -0.0, f32::INFINITY].iter().copied() {
assert_eq!(libm::sqrtf(f), f);
}
}
const POS_ZERO: &[f64] = &[0.0];
const NEG_ZERO: &[f64] = &[-0.0];
const POS_ONE: &[f64] = &[1.0];
const NEG_ONE: &[f64] = &[-1.0];
const POS_FLOATS: &[f64] = &[99.0 / 70.0, f64::consts::E, f64::consts::PI];
const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -f64::consts::E, -f64::consts::PI];
const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), f64::MIN_POSITIVE, f64::EPSILON];
const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -f64::MIN_POSITIVE, -f64::EPSILON];
const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, f64::MAX];
const NEG_EVENS: &[f64] = &[f64::MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0];
const POS_ODDS: &[f64] = &[3.0, 7.0];
const NEG_ODDS: &[f64] = &[-7.0, -3.0];
const NANS: &[f64] = &[f64::NAN];
const POS_INF: &[f64] = &[f64::INFINITY];
const NEG_INF: &[f64] = &[f64::NEG_INFINITY];
const ALL: &[&[f64]] = &[
POS_ZERO,
NEG_ZERO,
NANS,
NEG_SMALL_FLOATS,
POS_SMALL_FLOATS,
NEG_FLOATS,
POS_FLOATS,
NEG_EVENS,
POS_EVENS,
NEG_ODDS,
POS_ODDS,
NEG_INF,
POS_INF,
NEG_ONE,
POS_ONE,
];
const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF];
const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF];
fn powd(base: f64, exponent: f64, expected: f64) {
let res = libm::powd(base, exponent);
assert!(
if expected.is_nan() {
res.is_nan()
} else {
libm::powd(base, exponent) == expected
},
"{} ** {} was {} instead of {}",
base,
exponent,
res,
expected
);
}
fn powd_test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) {
sets.iter().for_each(|s| s.iter().for_each(|val| powd(*val, exponent, expected)));
}
fn powd_test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) {
sets.iter().for_each(|s| s.iter().for_each(|val| powd(base, *val, expected)));
}
fn powd_test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) {
sets.iter().for_each(|s| {
s.iter().for_each(|val| {
let exp = expected(*val);
let res = computed(*val);
assert!(
if exp.is_nan() {
res.is_nan()
} else {
exp == res
},
"test for {} was {} instead of {}",
val,
res,
exp
);
})
});
}
#[test]
fn powd_zero_as_exponent() {
powd_test_sets_as_base(ALL, 0.0, 1.0);
powd_test_sets_as_base(ALL, -0.0, 1.0);
}
#[test]
fn powd_one_as_base() {
powd_test_sets_as_exponent(1.0, ALL, 1.0);
}
#[test]
fn powd_nan_inputs() {
// NAN as the base:
// (NAN ^ anything *but 0* should be NAN)
powd_test_sets_as_exponent(f64::NAN, &ALL[2..], f64::NAN);
// NAN as the exponent:
// (anything *but 1* ^ NAN should be NAN)
powd_test_sets_as_base(&ALL[..(ALL.len() - 2)], f64::NAN, f64::NAN);
}
#[test]
fn powd_infinity_as_base() {
// Positive Infinity as the base:
// (+Infinity ^ positive anything but 0 and NAN should be +Infinity)
powd_test_sets_as_exponent(f64::INFINITY, &POS[1..], f64::INFINITY);
// (+Infinity ^ negative anything except 0 and NAN should be 0.0)
powd_test_sets_as_exponent(f64::INFINITY, &NEG[1..], 0.0);
// Negative Infinity as the base:
// (-Infinity ^ positive odd ints should be -Infinity)
powd_test_sets_as_exponent(f64::NEG_INFINITY, &[POS_ODDS], f64::NEG_INFINITY);
// (-Infinity ^ anything but odd ints should be == -0 ^ (-anything))
// We can lump in pos/neg odd ints here because they don't seem to
// cause panics (div by zero) in release mode (I think).
powd_test_sets(ALL, &|v: f64| libm::powd(f64::NEG_INFINITY, v), &|v: f64| libm::powd(-0.0, -v));
}
#[test]
fn infinity_as_exponent() {
// Positive/Negative base greater than 1:
// (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base)
powd_test_sets_as_base(&ALL[5..(ALL.len() - 2)], f64::INFINITY, f64::INFINITY);
// (pos/neg > 1 ^ -Infinity should be 0.0)
powd_test_sets_as_base(&ALL[5..ALL.len() - 2], f64::NEG_INFINITY, 0.0);
// Positive/Negative base less than 1:
let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS];
// (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base)
powd_test_sets_as_base(base_below_one, f64::INFINITY, 0.0);
// (pos/neg < 1 ^ -Infinity should be Infinity)
powd_test_sets_as_base(base_below_one, f64::NEG_INFINITY, f64::INFINITY);
// Positive/Negative 1 as the base:
// (pos/neg 1 ^ Infinity should be 1)
powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::INFINITY, 1.0);
// (pos/neg 1 ^ -Infinity should be 1)
powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::NEG_INFINITY, 1.0);
}
#[test]
fn powd_zero_as_base() {
// Positive Zero as the base:
// (+0 ^ anything positive but 0 and NAN should be +0)
powd_test_sets_as_exponent(0.0, &POS[1..], 0.0);
// (+0 ^ anything negative but 0 and NAN should be Infinity)
// (this should panic because we're dividing by zero)
powd_test_sets_as_exponent(0.0, &NEG[1..], f64::INFINITY);
// Negative Zero as the base:
// (-0 ^ anything positive but 0, NAN, and odd ints should be +0)
powd_test_sets_as_exponent(-0.0, &POS[3..], 0.0);
// (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity)
// (should panic because of divide by zero)
powd_test_sets_as_exponent(-0.0, &NEG[3..], f64::INFINITY);
// (-0 ^ positive odd ints should be -0)
powd_test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0);
// (-0 ^ negative odd ints should be -Infinity)
// (should panic because of divide by zero)
powd_test_sets_as_exponent(-0.0, &[NEG_ODDS], f64::NEG_INFINITY);
}
#[test]
fn special_cases() {
// One as the exponent:
// (anything ^ 1 should be anything - i.e. the base)
powd_test_sets(ALL, &|v: f64| libm::powd(v, 1.0), &|v: f64| v);
// Negative One as the exponent:
// (anything ^ -1 should be 1/anything)
powd_test_sets(ALL, &|v: f64| libm::powd(v, -1.0), &|v: f64| 1.0 / v);
// Factoring -1 out:
// (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer))
[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS].iter().for_each(|int_set| {
int_set.iter().for_each(|int| {
powd_test_sets(ALL, &|v: f64| libm::powd(-v, *int), &|v: f64| {
libm::powd(-1.0, *int) * libm::powd(v, *int)
});
})
});
// Negative base (imaginary results):
// (-anything except 0 and Infinity ^ non-integer should be NAN)
NEG[1..(NEG.len() - 1)].iter().for_each(|set| {
set.iter().for_each(|val| {
powd_test_sets(&ALL[3..7], &|v: f64| libm::powd(*val, v), &|_| f64::NAN);
})
});
}
#[test]
fn normal_cases() {
assert_eq!(libm::powd(2.0, 20.0), (1 << 20) as f64);
assert_eq!(libm::powd(-1.0, 9.0), -1.0);
assert!(libm::powd(-1.0, 2.2).is_nan());
assert!(libm::powd(-1.0, -1.14).is_nan());
}
#[test]
fn fabsd_sanity_test() {
assert_eq!(libm::fabsd(-1.0), 1.0);
assert_eq!(libm::fabsd(2.8), 2.8);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs
#[test]
fn fabsd_spec_test() {
assert!(libm::fabsd(f64::NAN).is_nan());
for f in [0.0, -0.0].iter().copied() {
assert_eq!(libm::fabsd(f), 0.0);
}
for f in [f64::INFINITY, f64::NEG_INFINITY].iter().copied() {
assert_eq!(libm::fabsd(f), f64::INFINITY);
}
}
#[test]
fn sqrtd_sanity_test() {
assert_eq!(libm::sqrtd(100.0), 10.0);
assert_eq!(libm::sqrtd(4.0), 2.0);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt
#[test]
fn sqrtd_spec_test() {
// Not Asserted: FE_INVALID exception is raised if argument is negative.
assert!(libm::sqrtd(-1.0).is_nan());
assert!(libm::sqrtd(f64::NAN).is_nan());
for f in [0.0, -0.0, f64::INFINITY].iter().copied() {
assert_eq!(libm::sqrtd(f), f);
}
}

View File

@@ -0,0 +1,16 @@
use minimal_lexical::mask;
#[test]
fn lower_n_mask_test() {
assert_eq!(mask::lower_n_mask(2), 0b11);
}
#[test]
fn lower_n_halfway_test() {
assert_eq!(mask::lower_n_halfway(2), 0b10);
}
#[test]
fn nth_bit_test() {
assert_eq!(mask::nth_bit(2), 0b100);
}

View File

@@ -0,0 +1,88 @@
use minimal_lexical::number::Number;
#[test]
fn is_fast_path_test() {
let mut number = Number {
exponent: -4,
mantissa: 12345,
many_digits: false,
};
assert_eq!(number.is_fast_path::<f32>(), true);
assert_eq!(number.is_fast_path::<f64>(), true);
number.exponent = -15;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), true);
number.exponent = -25;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), false);
number.exponent = 25;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), true);
number.exponent = 36;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), true);
number.exponent = 38;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), false);
number.mantissa = 1 << 25;
number.exponent = 0;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), true);
number.mantissa = 1 << 54;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), false);
number.mantissa = 1 << 52;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), true);
number.many_digits = true;
assert_eq!(number.is_fast_path::<f32>(), false);
assert_eq!(number.is_fast_path::<f64>(), false);
}
#[test]
fn try_fast_path_test() {
let mut number = Number {
exponent: -4,
mantissa: 12345,
many_digits: false,
};
assert_eq!(number.try_fast_path::<f32>(), Some(1.2345));
assert_eq!(number.try_fast_path::<f64>(), Some(1.2345));
number.exponent = -10;
assert_eq!(number.try_fast_path::<f32>(), Some(1.2345e-6));
assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e-6));
number.exponent = -20;
assert_eq!(number.try_fast_path::<f32>(), None);
assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e-16));
number.exponent = -25;
assert_eq!(number.try_fast_path::<f32>(), None);
assert_eq!(number.try_fast_path::<f64>(), None);
number.exponent = 12;
assert_eq!(number.try_fast_path::<f32>(), Some(1.2345e16));
assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e16));
number.exponent = 25;
assert_eq!(number.try_fast_path::<f32>(), None);
assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e29));
number.exponent = 32;
assert_eq!(number.try_fast_path::<f32>(), None);
assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e36));
number.exponent = 36;
assert_eq!(number.try_fast_path::<f32>(), None);
assert_eq!(number.try_fast_path::<f64>(), None);
}

View File

@@ -0,0 +1,189 @@
use core::f64;
use minimal_lexical::{num, parse};
fn check_parse_float<F: num::Float>(integer: &str, fraction: &str, exponent: i32, expected: F) {
let integer = integer.as_bytes().iter();
let fraction = fraction.as_bytes().iter();
assert!(expected == parse::parse_float::<F, _, _>(integer, fraction, exponent));
}
#[test]
fn parse_f32_test() {
check_parse_float("", "", 0, 0.0_f32);
check_parse_float("1", "2345", 0, 1.2345_f32);
check_parse_float("12", "345", 0, 12.345_f32);
check_parse_float("12345", "6789", 0, 12345.6789_f32);
check_parse_float("1", "2345", 10, 1.2345e10_f32);
check_parse_float("1", "2345", -38, 1.2345e-38_f32);
// Check expected rounding, using borderline cases.
// Round-down, halfway
check_parse_float("16777216", "", 0, 16777216.0_f32);
check_parse_float("16777217", "", 0, 16777216.0_f32);
check_parse_float("16777218", "", 0, 16777218.0_f32);
check_parse_float("33554432", "", 0, 33554432.0_f32);
check_parse_float("33554434", "", 0, 33554432.0_f32);
check_parse_float("33554436", "", 0, 33554436.0_f32);
check_parse_float("17179869184", "", 0, 17179869184.0_f32);
check_parse_float("17179870208", "", 0, 17179869184.0_f32);
check_parse_float("17179871232", "", 0, 17179871232.0_f32);
// Round-up, halfway
check_parse_float("16777218", "", 0, 16777218.0_f32);
check_parse_float("16777219", "", 0, 16777220.0_f32);
check_parse_float("16777220", "", 0, 16777220.0_f32);
check_parse_float("33554436", "", 0, 33554436.0_f32);
check_parse_float("33554438", "", 0, 33554440.0_f32);
check_parse_float("33554440", "", 0, 33554440.0_f32);
check_parse_float("17179871232", "", 0, 17179871232.0_f32);
check_parse_float("17179872256", "", 0, 17179873280.0_f32);
check_parse_float("17179873280", "", 0, 17179873280.0_f32);
// Round-up, above halfway
check_parse_float("33554435", "", 0, 33554436.0_f32);
check_parse_float("17179870209", "", 0, 17179871232.0_f32);
// Check exactly halfway, round-up at halfway
check_parse_float("1", "00000017881393432617187499", 0, 1.0000001_f32);
check_parse_float("1", "000000178813934326171875", 0, 1.0000002_f32);
check_parse_float("1", "00000017881393432617187501", 0, 1.0000002_f32);
check_parse_float("", "000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625", 0, 1.1754943508222875e-38f32);
}
#[test]
fn parse_f64_test() {
check_parse_float("", "", 0, 0.0_f64);
check_parse_float("1", "2345", 0, 1.2345_f64);
check_parse_float("12", "345", 0, 12.345_f64);
check_parse_float("12345", "6789", 0, 12345.6789_f64);
check_parse_float("1", "2345", 10, 1.2345e10_f64);
check_parse_float("1", "2345", -308, 1.2345e-308_f64);
// Check expected rounding, using borderline cases.
// Round-down, halfway
check_parse_float("9007199254740992", "", 0, 9007199254740992.0_f64);
check_parse_float("9007199254740993", "", 0, 9007199254740992.0_f64);
check_parse_float("9007199254740994", "", 0, 9007199254740994.0_f64);
check_parse_float("18014398509481984", "", 0, 18014398509481984.0_f64);
check_parse_float("18014398509481986", "", 0, 18014398509481984.0_f64);
check_parse_float("18014398509481988", "", 0, 18014398509481988.0_f64);
check_parse_float("9223372036854775808", "", 0, 9223372036854775808.0_f64);
check_parse_float("9223372036854776832", "", 0, 9223372036854775808.0_f64);
check_parse_float("9223372036854777856", "", 0, 9223372036854777856.0_f64);
check_parse_float(
"11417981541647679048466287755595961091061972992",
"",
0,
11417981541647679048466287755595961091061972992.0_f64,
);
check_parse_float(
"11417981541647680316116887983825362587765178368",
"",
0,
11417981541647679048466287755595961091061972992.0_f64,
);
check_parse_float(
"11417981541647681583767488212054764084468383744",
"",
0,
11417981541647681583767488212054764084468383744.0_f64,
);
// Round-up, halfway
check_parse_float("9007199254740994", "", 0, 9007199254740994.0_f64);
check_parse_float("9007199254740995", "", 0, 9007199254740996.0_f64);
check_parse_float("9007199254740996", "", 0, 9007199254740996.0_f64);
check_parse_float("18014398509481988", "", 0, 18014398509481988.0_f64);
check_parse_float("18014398509481990", "", 0, 18014398509481992.0_f64);
check_parse_float("18014398509481992", "", 0, 18014398509481992.0_f64);
check_parse_float("9223372036854777856", "", 0, 9223372036854777856.0_f64);
check_parse_float("9223372036854778880", "", 0, 9223372036854779904.0_f64);
check_parse_float("9223372036854779904", "", 0, 9223372036854779904.0_f64);
check_parse_float(
"11417981541647681583767488212054764084468383744",
"",
0,
11417981541647681583767488212054764084468383744.0_f64,
);
check_parse_float(
"11417981541647682851418088440284165581171589120",
"",
0,
11417981541647684119068688668513567077874794496.0_f64,
);
check_parse_float(
"11417981541647684119068688668513567077874794496",
"",
0,
11417981541647684119068688668513567077874794496.0_f64,
);
// Round-up, above halfway
check_parse_float("9223372036854776833", "", 0, 9223372036854777856.0_f64);
check_parse_float(
"11417981541647680316116887983825362587765178369",
"",
0,
11417981541647681583767488212054764084468383744.0_f64,
);
// Rounding error
// Adapted from failures in strtod.
check_parse_float("2", "2250738585072014", -308, 2.2250738585072014e-308_f64);
check_parse_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187499", -308, 2.225073858507201e-308_f64);
check_parse_float("2", "22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875", -308, 2.2250738585072014e-308_f64);
check_parse_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187501", -308, 2.2250738585072014e-308_f64);
check_parse_float("179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791", "9999999999999999999999999999999999999999999999999999999999999999999999", 0, 1.7976931348623157e+308_f64);
check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999", -324, 5.0e-324_f64);
check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375", -324, 1.0e-323_f64);
check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001", -324, 1.0e-323_f64);
check_parse_float("", "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125", 0, 0.0_f64);
// Rounding error
// Adapted from:
// https://www.exploringbinary.com/how-glibc-strtod-works/
check_parse_float("", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0, 2.2250738585072011e-308_f64);
// Rounding error
// Adapted from test-parse-random failures.
check_parse_float("1009", "", -31, 1.009e-28_f64);
check_parse_float("18294", "", 304, f64::INFINITY);
// Rounding error
// Adapted from a @dangrabcad's issue #20.
check_parse_float("7", "689539722041643", 164, 7.689539722041643e164_f64);
check_parse_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", 0, 7.689539722041643e164_f64);
check_parse_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 7.689539722041643e164_f64);
// Check other cases similar to @dangrabcad's issue #20.
check_parse_float("9223372036854776833", "0", 0, 9223372036854777856.0_f64);
check_parse_float(
"11417981541647680316116887983825362587765178369",
"0",
0,
11417981541647681583767488212054764084468383744.0_f64,
);
check_parse_float("9007199254740995", "0", 0, 9007199254740996.0_f64);
check_parse_float("18014398509481990", "0", 0, 18014398509481992.0_f64);
check_parse_float("9223372036854778880", "0", 0, 9223372036854779904.0_f64);
check_parse_float(
"11417981541647682851418088440284165581171589120",
"0",
0,
11417981541647684119068688668513567077874794496.0_f64,
);
// Check other cases ostensibly identified via proptest.
check_parse_float("71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
check_parse_float("126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
check_parse_float("38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
}

View File

@@ -0,0 +1,64 @@
use minimal_lexical::extended_float::ExtendedFloat;
use minimal_lexical::rounding;
#[test]
fn round_test() {
let mut fp = ExtendedFloat {
mant: 9223372036854776832,
exp: -10,
};
rounding::round::<f64, _>(&mut fp, |f, s| {
f.mant >>= s;
f.exp += s;
});
assert_eq!(fp.mant, 0);
assert_eq!(fp.exp, 1);
let mut fp = ExtendedFloat {
mant: 9223372036854776832,
exp: -10,
};
rounding::round::<f64, _>(&mut fp, |f, s| {
f.mant >>= s;
f.exp += s;
// Round-up.
f.mant += 1;
});
assert_eq!(fp.mant, 1);
assert_eq!(fp.exp, 1);
// Round-down
let mut fp = ExtendedFloat {
mant: 9223372036854776832,
exp: -10,
};
rounding::round::<f64, _>(&mut fp, |f, s| {
rounding::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| {
is_above || (is_odd && is_halfway)
});
});
assert_eq!(fp.mant, 0);
assert_eq!(fp.exp, 1);
// Round up
let mut fp = ExtendedFloat {
mant: 9223372036854778880,
exp: -10,
};
rounding::round::<f64, _>(&mut fp, |f, s| {
rounding::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| {
is_above || (is_odd && is_halfway)
});
});
assert_eq!(fp.mant, 2);
assert_eq!(fp.exp, 1);
// Round down
let mut fp = ExtendedFloat {
mant: 9223372036854778880,
exp: -10,
};
rounding::round::<f64, _>(&mut fp, rounding::round_down);
assert_eq!(fp.mant, 1);
assert_eq!(fp.exp, 1);
}

View File

@@ -0,0 +1,337 @@
mod stackvec;
use minimal_lexical::bigint::Bigint;
use minimal_lexical::extended_float::ExtendedFloat;
use minimal_lexical::num::Float;
use minimal_lexical::number::Number;
use minimal_lexical::slow;
use stackvec::vec_from_u32;
fn b<F: Float>(float: F) -> (u64, i32) {
let fp = slow::b(float);
(fp.mant, fp.exp)
}
fn bh<F: Float>(float: F) -> (u64, i32) {
let fp = slow::bh(float);
(fp.mant, fp.exp)
}
#[test]
fn b_test() {
assert_eq!(b(1e-45_f32), (1, -149));
assert_eq!(b(5e-324_f64), (1, -1074));
assert_eq!(b(1e-323_f64), (2, -1074));
assert_eq!(b(2e-323_f64), (4, -1074));
assert_eq!(b(3e-323_f64), (6, -1074));
assert_eq!(b(4e-323_f64), (8, -1074));
assert_eq!(b(5e-323_f64), (10, -1074));
assert_eq!(b(6e-323_f64), (12, -1074));
assert_eq!(b(7e-323_f64), (14, -1074));
assert_eq!(b(8e-323_f64), (16, -1074));
assert_eq!(b(9e-323_f64), (18, -1074));
assert_eq!(b(1_f32), (8388608, -23));
assert_eq!(b(1_f64), (4503599627370496, -52));
assert_eq!(b(1e38_f32), (9860761, 103));
assert_eq!(b(1e308_f64), (5010420900022432, 971));
}
#[test]
fn bh_test() {
assert_eq!(bh(1e-45_f32), (3, -150));
assert_eq!(bh(5e-324_f64), (3, -1075));
assert_eq!(bh(1_f32), (16777217, -24));
assert_eq!(bh(1_f64), (9007199254740993, -53));
assert_eq!(bh(1e38_f32), (19721523, 102));
assert_eq!(bh(1e308_f64), (10020841800044865, 970));
}
#[test]
fn slow_test() {
// 5e-324, round-down.
let integer = b"2";
let fraction = b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999";
let num = Number {
mantissa: 2470328229206232720,
exponent: -342,
many_digits: true,
};
let fp = ExtendedFloat {
mant: 1 << 63,
exp: -63,
};
let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter());
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, round-up.
let fraction = b"47032822920623272088284396434110686182529901307162382212792841250337753635104375932649918180817996189898282347722858865463328355177969898199387398005390939063150356595155702263922908583924491051844359318028499365361525003193704576782492193656236698636584807570015857692699037063119282795585513329278343384093519780155312465972635795746227664652728272200563740064854999770965994704540208281662262378573934507363390079677619305775067401763246736009689513405355374585166611342237666786041621596804619144672918403005300575308490487653917113865916462395249126236538818796362393732804238910186723484976682350898633885879256283027559956575244555072551893136908362547791869486679949683240497058210285131854513962138377228261454376934125320985913276672363281251";
let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter());
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 0);
// 8.98846567431158e+307
let integer = b"8";
let fraction = b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032";
let num = Number {
mantissa: 8988465674311580536,
exponent: 289,
many_digits: true,
};
let fp = ExtendedFloat {
mant: 9223372036854776832,
exp: 2035,
};
let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter());
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 2046);
// 8.988465674311582e+307
let fraction = b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296474151486978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080321";
let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter());
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 2046);
}
#[test]
fn positive_digit_comp_test() {
// 8.98846567431158e+307
let bigmant = Bigint {
data: vec_from_u32(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1024, 2147483648,
]),
};
let exponent = 307 + 1 - 308;
let result = slow::positive_digit_comp::<f64>(bigmant, exponent);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 2046);
// 8.988465674311582e+307
let bigmant = Bigint {
data: vec_from_u32(&[
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1024, 2147483648,
]),
};
let exponent = 307 + 1 - 308;
let result = slow::positive_digit_comp::<f64>(bigmant, exponent);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 2046);
}
#[test]
fn negative_digit_comp_test() {
// 5e-324, below halfway, round-down to 0.0.
let bigmant = Bigint {
data: vec_from_u32(&[
1727738439, 330069557, 3509095598, 686205316, 156923684, 750687444, 2688855918,
28211928, 1887482096, 3222998811, 913348873, 1652282845, 1600735541, 1664240266,
84454144, 1487769792, 1855966778, 2832488299, 507030148, 1410055467, 2513359584,
3453963205, 779237894, 3456088326, 3671009895, 3094451696, 1250165638, 2682979794,
357925323, 1713890438, 3271046672, 3485897285, 3934710962, 1813530592, 199705026,
976390839, 2805488572, 2194288220, 2094065006, 2592523639, 3798974617, 586957244,
1409218821, 3442050171, 3789534764, 1380190380, 2055222457, 3535299831, 429482276,
389342206, 133558576, 721875297, 3013586570, 540178306, 2389746866, 2313334501,
422440635, 1288499129, 864978311, 842263325, 3016323856, 2282442263, 1440906063,
3931458696, 3511314276, 1884879882, 946366824, 4260548261, 1073379659, 1732329252,
3828972211, 1915607049, 3665440937, 1844358779, 3735281178, 2646335050, 1457460927,
2940016422, 1051,
]),
};
let fp = ExtendedFloat {
mant: 1 << 63,
exp: -63,
};
let exponent = -324 + 1 - 755;
let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, halfway, round-down to 0.0.
let bigmant = Bigint {
data: vec_from_u32(&[
2084786877, 507136210, 2666388819, 3110242527, 3178432722, 541916566, 208847286,
3092404665, 83491860, 2893735989, 3973758097, 2600107496, 147629623, 1754010897,
4226332273, 2587058081, 942453804, 88731834, 1319061990, 173208747, 1982493283,
3808794987, 3874839738, 1854586992, 3508364323, 2021729080, 1899625710, 2420749567,
816401711, 3059730605, 1570934109, 3138812023, 1756281367, 3205859133, 2985201975,
1014588672, 3799556578, 577719905, 4052248225, 3649019757, 398935965, 56421532,
976366795, 1876047791, 3147705595, 4025764546, 1097271882, 1910500779, 2397021233,
1340419138, 2753207595, 3067328524, 2210626776, 1280440432, 3940874757, 4172726578,
1035509558, 1062145421, 1465448826, 2990139501, 1785427751, 2093931515, 4055890033,
3388365687, 2245484242, 3609657408, 3527114516, 1013577862, 2389075196, 426934091,
3237939346, 1071362463, 4070999470, 250952461, 2280067948, 1097862995, 2226250520,
221983348, 1,
]),
};
let exponent = -324 + 1 - 752;
let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, above halfway, round-up to 5e-324.
let bigmant = Bigint {
data: vec_from_u32(&[
3667999587, 776394808, 894084415, 1037654204, 1719556155, 1124198371, 2088472861,
859275578, 834918607, 3167556114, 1082875312, 231271193, 1476296236, 360239786,
3608617070, 100777043, 834603454, 887318342, 305718012, 1732087473, 2645063646,
3728211506, 93691724, 1366000745, 723904866, 3037421624, 1816387920, 2732659194,
3869049819, 532534979, 2824439209, 1323349161, 382944493, 1993820262, 4082215981,
1555952134, 3635827414, 1482231762, 1867776587, 2130459211, 3989359658, 564215320,
1173733358, 1580608728, 1412284882, 1602939803, 2382784237, 1925138608, 2495375854,
519289497, 1762272177, 608514174, 631431287, 4214469733, 754041908, 3072560125,
1765160997, 2031519620, 1769586374, 4131591237, 674408332, 3759445970, 1904194670,
3818885807, 980005947, 1736835717, 911406800, 1545844036, 2415915482, 4269340915,
2314622388, 2123690045, 2055289038, 2509524619, 1325843000, 2388695363, 787668722,
2219833485, 10,
]),
};
let exponent = -324 + 1 - 753;
let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 0);
// 1e-323, below halfway, round-down to 5e-324.
let bigmant = Bigint {
data: vec_from_u32(&[
888248023, 990208672, 1937352202, 2058615950, 470771052, 2252062332, 3771600458,
84635785, 1367478992, 1079061842, 2740046621, 661881239, 507239328, 697753503,
253362433, 168342080, 1272933039, 4202497602, 1521090445, 4230166401, 3245111456,
1771955024, 2337713684, 1778330386, 2423095095, 693420498, 3750496916, 3753972086,
1073775970, 846704018, 1223205425, 1867757265, 3214198296, 1145624482, 599115079,
2929172517, 4121498420, 2287897365, 1987227723, 3482603622, 2806989260, 1760871734,
4227656463, 1736215921, 2778669702, 4140571142, 1870700075, 2015964902, 1288446830,
1168026618, 400675728, 2165625891, 450825118, 1620534920, 2874273302, 2645036208,
1267321906, 3865497387, 2594934933, 2526789975, 459036976, 2552359495, 27750894,
3204441497, 1944008238, 1359672352, 2839100473, 4191710191, 3220138979, 902020460,
2896982042, 1451853853, 2406388220, 1238109043, 2615908943, 3644037856, 77415486,
230114675, 3155,
]),
};
let fp = ExtendedFloat {
mant: 1 << 63,
exp: -62,
};
let exponent = -324 + 1 - 755;
let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 0);
// 1e-323, halfway, round-up to 1e-323.
let bigmant = Bigint {
data: vec_from_u32(&[
1959393335, 1521408631, 3704199161, 740792990, 945363576, 1625749700, 626541858,
687279403, 250475582, 91273375, 3331339701, 3505355194, 442888870, 967065395,
4089062228, 3466206949, 2827361413, 266195502, 3957185970, 519626241, 1652512553,
2836450370, 3034584624, 1268793682, 1935158378, 1770219946, 1403909835, 2967281406,
2449205134, 589257223, 417835033, 826501478, 973876807, 1027642808, 365671335,
3043766018, 2808735142, 1733159717, 3566810083, 2357124681, 1196807897, 169264596,
2929100385, 1333176077, 853182194, 3487359048, 3291815648, 1436535041, 2896096404,
4021257415, 3964655489, 612050981, 2336913034, 3841321297, 3232689679, 3928245144,
3106528676, 3186436263, 101379182, 380483912, 1061315959, 1986827250, 3577735508,
1575162471, 2441485432, 2239037633, 1991408958, 3040733588, 2872258292, 1280802274,
1123883446, 3214087391, 3623063818, 752857385, 2545236548, 3293588986, 2383784264,
665950045, 3,
]),
};
let exponent = -324 + 1 - 752;
let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent);
assert_eq!(result.mant, 2);
assert_eq!(result.exp, 0);
// 1e-323, above halfway, round-up to 1e-323.
let bigmant = Bigint {
data: vec_from_u32(&[
2414064167, 2329184426, 2682253245, 3112962612, 863701169, 3372595114, 1970451287,
2577826735, 2504755821, 912733750, 3248625938, 693813579, 133921412, 1080719359,
2235916618, 302331131, 2503810362, 2661955026, 917154036, 901295123, 3640223643,
2594699927, 281075174, 4098002235, 2171714598, 522330280, 1154196466, 3903010287,
3017214866, 1597604939, 4178350331, 3970047484, 1148833479, 1686493490, 3656713352,
372889108, 2317547651, 151727992, 1308362466, 2096410338, 3378144383, 1692645962,
3521200074, 446858888, 4236854647, 513852113, 2853385416, 1480448529, 3191160267,
1557868492, 991849235, 1825542523, 1894293861, 4053474607, 2262125726, 627745783,
1000515697, 1799591565, 1013791827, 3804839120, 2023224998, 2688403318, 1417616716,
2866722830, 2940017843, 915539855, 2734220401, 342564812, 2952779151, 4218088154,
2648899870, 2076102840, 1870899819, 3233606562, 3977529001, 2871118793, 2363006167,
2364533159, 31,
]),
};
let exponent = -324 + 1 - 753;
let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent);
assert_eq!(result.mant, 2);
assert_eq!(result.exp, 0);
}
#[test]
fn parse_mantissa_test() {
let max_digits = f64::MAX_DIGITS;
// Large number of digits.
let integer = b"2";
let fraction = b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999";
let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits);
let expected = vec_from_u32(&[
1727738439, 330069557, 3509095598, 686205316, 156923684, 750687444, 2688855918, 28211928,
1887482096, 3222998811, 913348873, 1652282845, 1600735541, 1664240266, 84454144,
1487769792, 1855966778, 2832488299, 507030148, 1410055467, 2513359584, 3453963205,
779237894, 3456088326, 3671009895, 3094451696, 1250165638, 2682979794, 357925323,
1713890438, 3271046672, 3485897285, 3934710962, 1813530592, 199705026, 976390839,
2805488572, 2194288220, 2094065006, 2592523639, 3798974617, 586957244, 1409218821,
3442050171, 3789534764, 1380190380, 2055222457, 3535299831, 429482276, 389342206,
133558576, 721875297, 3013586570, 540178306, 2389746866, 2313334501, 422440635, 1288499129,
864978311, 842263325, 3016323856, 2282442263, 1440906063, 3931458696, 3511314276,
1884879882, 946366824, 4260548261, 1073379659, 1732329252, 3828972211, 1915607049,
3665440937, 1844358779, 3735281178, 2646335050, 1457460927, 2940016422, 1051,
]);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, 755);
// Truncation.
let integer = b"7";
let fraction = b"4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375332669816033062329967789262837";
let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits);
let expected = vec_from_u32(&[
983641521, 2202462645, 4170685875, 1591772364, 529830014, 803977727, 126733331, 1695971390,
4089590927, 1532849076, 2705586665, 4046282448, 4076195232, 3230469892, 3059053929,
79035789, 744229654, 2026438108, 3570486781, 2818088662, 3485839733, 3653138023,
2857937689, 602717004, 3689362390, 283607819, 1783392475, 2053068939, 1888214698,
550023429, 296880187, 1046779059, 1285361259, 84614934, 1627922685, 2023868765, 1987523901,
743493573, 3897769089, 2210613570, 2261081349, 3015057659, 3949711644, 3346092916,
2433639051, 36411806, 1050442, 269209477, 2649742673, 1494221829, 2763524503, 2514491481,
2325312415, 1741242814, 2479923579, 1098250122, 2416211509, 3612906464, 403420662,
3663250314, 1993722098, 365907183, 4270226312, 3962131185, 432952495, 2963635838,
2996289227, 3200289391, 2753231690, 2780286109, 884373163, 1418533204, 3382415762,
499541562, 3369625401, 3421327641, 3526770155, 3109983188, 1157439767, 734593155,
]);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, max_digits + 1);
// No fraction digits.
let integer = b"74109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375332669816033062329967789262837";
let fraction = b"";
let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, max_digits + 1);
// Multiple of step (check we add our temporary correctly).
let integer = b"7410984687618698162648531893023320585475897039214871466383785237510132609053131277979497545424539885696948470431685765963899850655339096945981621940161728171894510697854671067917687257517734731555330779540854980960845750095811137303474765809687100959097544227100475730780971111893578483867565399878350301522805593404659373979179073872386829939581848166016912201945649993128979841136206248449867871357218035220901702390328579173252022052897402080290685402160661237554998340267130003581248647904138574340187552090159017259254714629617513415977493871857473787096164563890871811984127167305601704549300470526959016576377688490826798697257336652176556794107250876433756084600398490497214911746308553955635418864151316847843631308023759629577398300170898437533266981";
let fraction = b"";
let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits);
let expected = vec_from_u32(&[
617018405, 396211401, 2130402383, 3812547827, 4263683770, 3918012496, 1787721490,
2493014694, 435464626, 3720854431, 2928509507, 2677932436, 369049650, 3606588290,
231237141, 2231172875, 3358152367, 95217925, 2777810007, 1016185079, 596681915, 2331711780,
593487272, 4212730845, 339602972, 4097829793, 262427536, 4182115035, 3414687403,
3711518952, 4168896929, 483727327, 1657080031, 2785588628, 1009114769, 482126749,
485376744, 1123705337, 3225501941, 2939050108, 1338451005, 2104263947, 3425461126,
1834224928, 4061025704, 792093815, 2707019125, 3610271203, 4254101529, 1026215278,
4117890107, 1748110416, 2535111606, 80965120, 3823822115, 2354910057, 590658512,
2682089507, 159300272, 1776569442, 3382166479, 3222978591, 540586210, 934713382,
2014123057, 1455555790, 4119131465, 3685912982, 3019947291, 3437891678, 2660105801,
2605860762, 394373515, 4177081532, 1616198650, 1580399082, 2017617452, 3327697130,
315505357,
]);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, 760);
}

View File

@@ -0,0 +1,32 @@
use minimal_lexical::bigint;
#[cfg(feature = "alloc")]
pub use minimal_lexical::heapvec::HeapVec as VecType;
#[cfg(not(feature = "alloc"))]
pub use minimal_lexical::stackvec::StackVec as VecType;
pub fn vec_from_u32(x: &[u32]) -> VecType {
let mut vec = VecType::new();
#[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))]
{
for &xi in x {
vec.try_push(xi as bigint::Limb).unwrap();
}
}
#[cfg(all(target_pointer_width = "64", not(target_arch = "sparc")))]
{
for xi in x.chunks(2) {
match xi.len() {
1 => vec.try_push(xi[0] as bigint::Limb).unwrap(),
2 => {
let xi0 = xi[0] as bigint::Limb;
let xi1 = xi[1] as bigint::Limb;
vec.try_push((xi1 << 32) | xi0).unwrap()
},
_ => unreachable!(),
}
}
}
vec
}

View File

@@ -0,0 +1,395 @@
mod stackvec;
use core::cmp;
use minimal_lexical::bigint;
use stackvec::{vec_from_u32, VecType};
// u64::MAX and Limb::MAX for older Rustc versions.
const U64_MAX: u64 = 0xffff_ffff_ffff_ffff;
// LIMB_MAX
#[cfg(all(target_pointer_width = "64", not(target_arch = "sparc")))]
const LIMB_MAX: u64 = U64_MAX;
#[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))]
const LIMB_MAX: u32 = 0xffff_ffff;
#[test]
fn simple_test() {
// Test the simple properties of the stack vector.
let mut x = VecType::from_u64(1);
assert_eq!(x.len(), 1);
assert_eq!(x.is_empty(), false);
assert_eq!(x.capacity(), bigint::BIGINT_LIMBS);
x.try_push(5).unwrap();
assert_eq!(x.len(), 2);
assert_eq!(x.pop(), Some(5));
assert_eq!(x.len(), 1);
assert_eq!(&*x, &[1]);
x.try_extend(&[2, 3, 4]).unwrap();
assert_eq!(x.len(), 4);
assert_eq!(&*x, &[1, 2, 3, 4]);
x.try_resize(6, 0).unwrap();
assert_eq!(x.len(), 6);
assert_eq!(&*x, &[1, 2, 3, 4, 0, 0]);
x.try_resize(0, 0).unwrap();
assert_eq!(x.len(), 0);
assert_eq!(x.is_empty(), true);
let x = VecType::try_from(&[5, 1]).unwrap();
assert_eq!(x.len(), 2);
assert_eq!(x.is_empty(), false);
if bigint::LIMB_BITS == 32 {
assert_eq!(x.hi64(), (0x8000000280000000, false));
} else {
assert_eq!(x.hi64(), (0x8000000000000002, true));
}
let rview = bigint::rview(&x);
assert_eq!(x[0], 5);
assert_eq!(x[1], 1);
assert_eq!(rview[0], 1);
assert_eq!(rview[1], 5);
assert_eq!(x.len(), 2);
assert_eq!(VecType::from_u64(U64_MAX).hi64(), (U64_MAX, false));
}
#[test]
fn hi64_test() {
assert_eq!(VecType::from_u64(0xA).hi64(), (0xA000000000000000, false));
assert_eq!(VecType::from_u64(0xAB).hi64(), (0xAB00000000000000, false));
assert_eq!(VecType::from_u64(0xAB00000000).hi64(), (0xAB00000000000000, false));
assert_eq!(VecType::from_u64(0xA23456789A).hi64(), (0xA23456789A000000, false));
}
#[test]
fn cmp_test() {
// Simple
let x = VecType::from_u64(1);
let y = VecType::from_u64(2);
assert_eq!(x.partial_cmp(&x), Some(cmp::Ordering::Equal));
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Less);
// Check asymmetric
let x = VecType::try_from(&[5, 1]).unwrap();
let y = VecType::from_u64(2);
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Greater);
// Check when we use reverse ordering properly.
let x = VecType::try_from(&[5, 1, 9]).unwrap();
let y = VecType::try_from(&[6, 2, 8]).unwrap();
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Greater);
// Complex scenario, check it properly uses reverse ordering.
let x = VecType::try_from(&[0, 1, 9]).unwrap();
let y = VecType::try_from(&[4294967295, 0, 9]).unwrap();
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Greater);
}
#[test]
fn math_test() {
let mut x = VecType::try_from(&[0, 1, 9]).unwrap();
assert_eq!(x.is_normalized(), true);
x.try_push(0).unwrap();
assert_eq!(&*x, &[0, 1, 9, 0]);
assert_eq!(x.is_normalized(), false);
x.normalize();
assert_eq!(&*x, &[0, 1, 9]);
assert_eq!(x.is_normalized(), true);
x.add_small(1);
assert_eq!(&*x, &[1, 1, 9]);
x.add_small(LIMB_MAX);
assert_eq!(&*x, &[0, 2, 9]);
x.mul_small(3);
assert_eq!(&*x, &[0, 6, 27]);
x.mul_small(LIMB_MAX);
let expected: VecType = if bigint::LIMB_BITS == 32 {
vec_from_u32(&[0, 4294967290, 4294967274, 26])
} else {
vec_from_u32(&[0, 0, 4294967290, 4294967295, 4294967274, 4294967295, 26])
};
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u64(0xFFFFFFFF);
let y = VecType::from_u64(5);
x *= &y;
let expected: VecType = vec_from_u32(&[0xFFFFFFFB, 0x4]);
assert_eq!(&*x, &*expected);
// Test with carry
let mut x = VecType::from_u64(1);
assert_eq!(&*x, &[1]);
x.add_small(LIMB_MAX);
assert_eq!(&*x, &[0, 1]);
}
#[test]
fn scalar_add_test() {
assert_eq!(bigint::scalar_add(5, 5), (10, false));
assert_eq!(bigint::scalar_add(LIMB_MAX, 1), (0, true));
}
#[test]
fn scalar_mul_test() {
assert_eq!(bigint::scalar_mul(5, 5, 0), (25, 0));
assert_eq!(bigint::scalar_mul(5, 5, 1), (26, 0));
assert_eq!(bigint::scalar_mul(LIMB_MAX, 2, 0), (LIMB_MAX - 1, 1));
}
#[test]
fn small_add_test() {
let mut x = VecType::from_u64(4294967295);
bigint::small_add(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 1]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u64(5);
bigint::small_add(&mut x, 7);
let expected = VecType::from_u64(12);
assert_eq!(&*x, &*expected);
// Single carry, internal overflow
let mut x = VecType::from_u64(0x80000000FFFFFFFF);
bigint::small_add(&mut x, 7);
let expected: VecType = vec_from_u32(&[6, 0x80000001]);
assert_eq!(&*x, &*expected);
// Double carry, overflow
let mut x = VecType::from_u64(0xFFFFFFFFFFFFFFFF);
bigint::small_add(&mut x, 7);
let expected: VecType = vec_from_u32(&[6, 0, 1]);
assert_eq!(&*x, &*expected);
}
#[test]
fn small_mul_test() {
// No overflow check, 1-int.
let mut x = VecType::from_u64(5);
bigint::small_mul(&mut x, 7);
let expected = VecType::from_u64(35);
assert_eq!(&*x, &*expected);
// No overflow check, 2-ints.
let mut x = VecType::from_u64(0x4000000040000);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[0x00140000, 0x140000]);
assert_eq!(&*x, &*expected);
// Overflow, 1 carry.
let mut x = VecType::from_u64(0x33333334);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 1]);
assert_eq!(&*x, &*expected);
// Overflow, 1 carry, internal.
let mut x = VecType::from_u64(0x133333334);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 6]);
assert_eq!(&*x, &*expected);
// Overflow, 2 carries.
let mut x = VecType::from_u64(0x3333333333333334);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 0, 1]);
assert_eq!(&*x, &*expected);
}
#[test]
fn pow_test() {
let mut x = VecType::from_u64(1);
bigint::pow(&mut x, 2);
let expected = VecType::from_u64(25);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u64(1);
bigint::pow(&mut x, 15);
let expected: VecType = vec_from_u32(&[452807053, 7]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u64(1);
bigint::pow(&mut x, 16);
let expected: VecType = vec_from_u32(&[2264035265, 35]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u64(1);
bigint::pow(&mut x, 17);
let expected: VecType = vec_from_u32(&[2730241733, 177]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u64(1);
bigint::pow(&mut x, 302);
let expected: VecType = vec_from_u32(&[
2443090281, 2149694430, 2297493928, 1584384001, 1279504719, 1930002239, 3312868939,
3735173465, 3523274756, 2025818732, 1641675015, 2431239749, 4292780461, 3719612855,
4174476133, 3296847770, 2677357556, 638848153, 2198928114, 3285049351, 2159526706,
626302612,
]);
assert_eq!(&*x, &*expected);
}
#[test]
fn large_add_test() {
// Overflow, both single values
let mut x = VecType::from_u64(4294967295);
let y = VecType::from_u64(5);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[4, 1]);
assert_eq!(&*x, &*expected);
// No overflow, single value
let mut x = VecType::from_u64(5);
let y = VecType::from_u64(7);
bigint::large_add(&mut x, &y);
let expected = VecType::from_u64(12);
assert_eq!(&*x, &*expected);
// Single carry, internal overflow
let mut x = VecType::from_u64(0x80000000FFFFFFFF);
let y = VecType::from_u64(7);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[6, 0x80000001]);
assert_eq!(&*x, &*expected);
// 1st overflows, 2nd doesn't.
let mut x = VecType::from_u64(0x7FFFFFFFFFFFFFFF);
let y = VecType::from_u64(0x7FFFFFFFFFFFFFFF);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[0xFFFFFFFE, 0xFFFFFFFF]);
assert_eq!(&*x, &*expected);
// Both overflow.
let mut x = VecType::from_u64(0x8FFFFFFFFFFFFFFF);
let y = VecType::from_u64(0x7FFFFFFFFFFFFFFF);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[0xFFFFFFFE, 0x0FFFFFFF, 1]);
assert_eq!(&*x, &*expected);
}
#[test]
fn large_mul_test() {
// Test by empty
let mut x = VecType::from_u64(0xFFFFFFFF);
let y = VecType::new();
bigint::large_mul(&mut x, &y);
let expected = VecType::new();
assert_eq!(&*x, &*expected);
// Simple case
let mut x = VecType::from_u64(0xFFFFFFFF);
let y = VecType::from_u64(5);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[0xFFFFFFFB, 0x4]);
assert_eq!(&*x, &*expected);
// Large u32, but still just as easy.
let mut x = VecType::from_u64(0xFFFFFFFF);
let y = VecType::from_u64(0xFFFFFFFE);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[0x2, 0xFFFFFFFD]);
assert_eq!(&*x, &*expected);
// Let's multiply two large values together.
let mut x: VecType = vec_from_u32(&[0xFFFFFFFE, 0x0FFFFFFF, 1]);
let y: VecType = vec_from_u32(&[0x99999999, 0x99999999, 0xCCCD9999, 0xCCCC]);
bigint::large_mul(&mut x, &y);
let expected: VecType =
vec_from_u32(&[0xCCCCCCCE, 0x5CCCCCCC, 0x9997FFFF, 0x33319999, 0x999A7333, 0xD999]);
assert_eq!(&*x, &*expected);
}
#[test]
fn very_large_mul_test() {
// Test cases triggered to that would normally use `karatsuba_mul`.
// Karatsuba multiplication was ripped out, however, these are useful
// test cases.
let mut x: VecType = vec_from_u32(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
let y: VecType = vec_from_u32(&[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[
4, 13, 28, 50, 80, 119, 168, 228, 300, 385, 484, 598, 728, 875, 1040, 1224, 1340, 1435,
1508, 1558, 1584, 1585, 1560, 1508, 1428, 1319, 1180, 1010, 808, 573, 304,
]);
assert_eq!(&*x, &*expected);
// Test cases triggered to that would normally use `karatsuba_uneven_mul`.
let mut x: VecType = vec_from_u32(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
let y: VecType = vec_from_u32(&[
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
]);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[
4, 13, 28, 50, 80, 119, 168, 228, 300, 385, 484, 598, 728, 875, 1040, 1224, 1360, 1496,
1632, 1768, 1904, 2040, 2176, 2312, 2448, 2584, 2720, 2856, 2992, 3128, 3264, 3400, 3536,
3672, 3770, 3829, 3848, 3826, 3762, 3655, 3504, 3308, 3066, 2777, 2440, 2054, 1618, 1131,
592,
]);
assert_eq!(&*x, &*expected);
}
#[test]
fn bit_length_test() {
let x: VecType = vec_from_u32(&[0, 0, 0, 1]);
assert_eq!(bigint::bit_length(&x), 97);
let x: VecType = vec_from_u32(&[0, 0, 0, 3]);
assert_eq!(bigint::bit_length(&x), 98);
let x = VecType::from_u64(1 << 31);
assert_eq!(bigint::bit_length(&x), 32);
}
#[test]
fn shl_bits_test() {
let mut x = VecType::from_u64(0xD2210408);
bigint::shl_bits(&mut x, 5);
let expected: VecType = vec_from_u32(&[0x44208100, 0x1A]);
assert_eq!(&*x, &*expected);
}
#[test]
fn shl_limbs_test() {
let mut x = VecType::from_u64(0xD2210408);
bigint::shl_limbs(&mut x, 2);
let expected: VecType = if bigint::LIMB_BITS == 32 {
vec_from_u32(&[0, 0, 0xD2210408])
} else {
vec_from_u32(&[0, 0, 0, 0, 0xD2210408])
};
assert_eq!(&*x, &*expected);
}
#[test]
fn shl_test() {
// Pattern generated via `''.join(["1" +"0"*i for i in range(20)])`
let mut x = VecType::from_u64(0xD2210408);
bigint::shl(&mut x, 5);
let expected: VecType = vec_from_u32(&[0x44208100, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 32);
let expected: VecType = vec_from_u32(&[0, 0x44208100, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 27);
let expected: VecType = vec_from_u32(&[0, 0, 0xD2210408]);
assert_eq!(&*x, &*expected);
// 96-bits of previous pattern
let mut x: VecType = vec_from_u32(&[0x20020010, 0x8040100, 0xD2210408]);
bigint::shl(&mut x, 5);
let expected: VecType = vec_from_u32(&[0x400200, 0x802004, 0x44208101, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 32);
let expected: VecType = vec_from_u32(&[0, 0x400200, 0x802004, 0x44208101, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 27);
let expected: VecType = vec_from_u32(&[0, 0, 0x20020010, 0x8040100, 0xD2210408]);
assert_eq!(&*x, &*expected);
}