core: added core::num::flt2dec for floating-point formatting.

This is a fork of the flt2dec portion of rust-strconv [1] with
a necessary relicensing (the original code was licensed CC0-1.0).
Each module is accompanied with large unit tests, integrated
in this commit as coretest::num::flt2dec. This module is added
in order to replace the existing core::fmt::float method.

The forked revision of rust-strconv is from 2015-04-20, with a commit ID
9adf6d3571c6764a6f240a740c823024f70dc1c7.

[1] https://github.com/lifthrasiir/rust-strconv/
This commit is contained in:
Kang Seonghoon
2015-04-19 14:19:54 +09:00
parent 7bd71637ca
commit c82da7a54b
14 changed files with 3895 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ extern crate core;
extern crate test;
extern crate libc;
extern crate rustc_unicode;
extern crate rand;
mod any;
mod atomic;

View File

@@ -0,0 +1,160 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::prelude::v1::*;
use core::num::flt2dec::bignum::tests::Big8x3 as Big;
#[test]
#[should_panic]
fn test_from_u64_overflow() {
Big::from_u64(0x1000000);
}
#[test]
fn test_add() {
assert_eq!(*Big::from_small(3).add(&Big::from_small(4)), Big::from_small(7));
assert_eq!(*Big::from_small(3).add(&Big::from_small(0)), Big::from_small(3));
assert_eq!(*Big::from_small(0).add(&Big::from_small(3)), Big::from_small(3));
assert_eq!(*Big::from_small(3).add(&Big::from_u64(0xfffe)), Big::from_u64(0x10001));
assert_eq!(*Big::from_u64(0xfedc).add(&Big::from_u64(0x789)), Big::from_u64(0x10665));
assert_eq!(*Big::from_u64(0x789).add(&Big::from_u64(0xfedc)), Big::from_u64(0x10665));
}
#[test]
#[should_panic]
fn test_add_overflow_1() {
Big::from_small(1).add(&Big::from_u64(0xffffff));
}
#[test]
#[should_panic]
fn test_add_overflow_2() {
Big::from_u64(0xffffff).add(&Big::from_small(1));
}
#[test]
fn test_sub() {
assert_eq!(*Big::from_small(7).sub(&Big::from_small(4)), Big::from_small(3));
assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x789)), Big::from_u64(0xfedc));
assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0xfedc)), Big::from_u64(0x789));
assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10664)), Big::from_small(1));
assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10665)), Big::from_small(0));
}
#[test]
#[should_panic]
fn test_sub_underflow_1() {
Big::from_u64(0x10665).sub(&Big::from_u64(0x10666));
}
#[test]
#[should_panic]
fn test_sub_underflow_2() {
Big::from_small(0).sub(&Big::from_u64(0x123456));
}
#[test]
fn test_mul_small() {
assert_eq!(*Big::from_small(7).mul_small(5), Big::from_small(35));
assert_eq!(*Big::from_small(0xff).mul_small(0xff), Big::from_u64(0xfe01));
assert_eq!(*Big::from_u64(0xffffff/13).mul_small(13), Big::from_u64(0xffffff));
}
#[test]
#[should_panic]
fn test_mul_small_overflow() {
Big::from_u64(0x800000).mul_small(2);
}
#[test]
fn test_mul_pow2() {
assert_eq!(*Big::from_small(0x7).mul_pow2(4), Big::from_small(0x70));
assert_eq!(*Big::from_small(0xff).mul_pow2(1), Big::from_u64(0x1fe));
assert_eq!(*Big::from_small(0xff).mul_pow2(12), Big::from_u64(0xff000));
assert_eq!(*Big::from_small(0x1).mul_pow2(23), Big::from_u64(0x800000));
assert_eq!(*Big::from_u64(0x123).mul_pow2(0), Big::from_u64(0x123));
assert_eq!(*Big::from_u64(0x123).mul_pow2(7), Big::from_u64(0x9180));
assert_eq!(*Big::from_u64(0x123).mul_pow2(15), Big::from_u64(0x918000));
assert_eq!(*Big::from_small(0).mul_pow2(23), Big::from_small(0));
}
#[test]
#[should_panic]
fn test_mul_pow2_overflow_1() {
Big::from_u64(0x1).mul_pow2(24);
}
#[test]
#[should_panic]
fn test_mul_pow2_overflow_2() {
Big::from_u64(0x123).mul_pow2(16);
}
#[test]
fn test_mul_digits() {
assert_eq!(*Big::from_small(3).mul_digits(&[5]), Big::from_small(15));
assert_eq!(*Big::from_small(0xff).mul_digits(&[0xff]), Big::from_u64(0xfe01));
assert_eq!(*Big::from_u64(0x123).mul_digits(&[0x56, 0x4]), Big::from_u64(0x4edc2));
assert_eq!(*Big::from_u64(0x12345).mul_digits(&[0x67]), Big::from_u64(0x7530c3));
assert_eq!(*Big::from_small(0x12).mul_digits(&[0x67, 0x45, 0x3]), Big::from_u64(0x3ae13e));
assert_eq!(*Big::from_u64(0xffffff/13).mul_digits(&[13]), Big::from_u64(0xffffff));
assert_eq!(*Big::from_small(13).mul_digits(&[0x3b, 0xb1, 0x13]), Big::from_u64(0xffffff));
}
#[test]
#[should_panic]
fn test_mul_digits_overflow_1() {
Big::from_u64(0x800000).mul_digits(&[2]);
}
#[test]
#[should_panic]
fn test_mul_digits_overflow_2() {
Big::from_u64(0x1000).mul_digits(&[0, 0x10]);
}
#[test]
fn test_div_rem_small() {
let as_val = |(q, r): (&mut Big, u8)| (q.clone(), r);
assert_eq!(as_val(Big::from_small(0xff).div_rem_small(15)), (Big::from_small(17), 0));
assert_eq!(as_val(Big::from_small(0xff).div_rem_small(16)), (Big::from_small(15), 15));
assert_eq!(as_val(Big::from_small(3).div_rem_small(40)), (Big::from_small(0), 3));
assert_eq!(as_val(Big::from_u64(0xffffff).div_rem_small(123)),
(Big::from_u64(0xffffff / 123), (0xffffffu64 % 123) as u8));
assert_eq!(as_val(Big::from_u64(0x10000).div_rem_small(123)),
(Big::from_u64(0x10000 / 123), (0x10000u64 % 123) as u8));
}
#[test]
fn test_is_zero() {
assert!(Big::from_small(0).is_zero());
assert!(!Big::from_small(3).is_zero());
assert!(!Big::from_u64(0x123).is_zero());
assert!(!Big::from_u64(0xffffff).sub(&Big::from_u64(0xfffffe)).is_zero());
assert!(Big::from_u64(0xffffff).sub(&Big::from_u64(0xffffff)).is_zero());
}
#[test]
fn test_ord() {
assert!(Big::from_u64(0) < Big::from_u64(0xffffff));
assert!(Big::from_u64(0x102) < Big::from_u64(0x201));
}
#[test]
fn test_fmt() {
assert_eq!(format!("{:?}", Big::from_u64(0)), "0x0");
assert_eq!(format!("{:?}", Big::from_u64(0x1)), "0x1");
assert_eq!(format!("{:?}", Big::from_u64(0x12)), "0x12");
assert_eq!(format!("{:?}", Big::from_u64(0x123)), "0x1_23");
assert_eq!(format!("{:?}", Big::from_u64(0x1234)), "0x12_34");
assert_eq!(format!("{:?}", Big::from_u64(0x12345)), "0x1_23_45");
assert_eq!(format!("{:?}", Big::from_u64(0x123456)), "0x12_34_56");
}

View File

@@ -0,0 +1,61 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::num::Float;
use core::num::flt2dec::estimator::*;
#[test]
fn test_estimate_scaling_factor() {
macro_rules! assert_almost_eq {
($actual:expr, $expected:expr) => ({
let actual = $actual;
let expected = $expected;
println!("{} - {} = {} - {} = {}", stringify!($expected), stringify!($actual),
expected, actual, expected - actual);
assert!(expected == actual || expected == actual + 1,
"expected {}, actual {}", expected, actual);
})
}
assert_almost_eq!(estimate_scaling_factor(1, 0), 0);
assert_almost_eq!(estimate_scaling_factor(2, 0), 1);
assert_almost_eq!(estimate_scaling_factor(10, 0), 1);
assert_almost_eq!(estimate_scaling_factor(11, 0), 2);
assert_almost_eq!(estimate_scaling_factor(100, 0), 2);
assert_almost_eq!(estimate_scaling_factor(101, 0), 3);
assert_almost_eq!(estimate_scaling_factor(10000000000000000000, 0), 19);
assert_almost_eq!(estimate_scaling_factor(10000000000000000001, 0), 20);
// 1/2^20 = 0.00000095367...
assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000, -20), -6);
assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000 + 1, -20), -5);
assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000, -20), -5);
assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000 + 1, -20), -4);
assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000, -20), -4);
assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000 + 1, -20), -3);
assert_almost_eq!(estimate_scaling_factor(1048575, -20), 0);
assert_almost_eq!(estimate_scaling_factor(1048576, -20), 0);
assert_almost_eq!(estimate_scaling_factor(1048577, -20), 1);
assert_almost_eq!(estimate_scaling_factor(10485759999999999999, -20), 13);
assert_almost_eq!(estimate_scaling_factor(10485760000000000000, -20), 13);
assert_almost_eq!(estimate_scaling_factor(10485760000000000001, -20), 14);
// extreme values:
// 2^-1074 = 4.94065... * 10^-324
// (2^53-1) * 2^971 = 1.79763... * 10^308
assert_almost_eq!(estimate_scaling_factor(1, -1074), -323);
assert_almost_eq!(estimate_scaling_factor(0x1fffffffffffff, 971), 309);
for i in -1074..972 {
let expected = Float::ldexp(1.0, i).log10().ceil();
assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::prelude::v1::*;
use std::{i16, f64};
use super::super::*;
use core::num::flt2dec::*;
use core::num::flt2dec::bignum::Big32x36 as Big;
use core::num::flt2dec::strategy::dragon::*;
#[test]
fn test_mul_pow10() {
let mut prevpow10 = Big::from_small(1);
for i in 1..340 {
let mut curpow10 = Big::from_small(1);
mul_pow10(&mut curpow10, i);
assert_eq!(curpow10, *prevpow10.clone().mul_small(10));
prevpow10 = curpow10;
}
}
#[test]
fn shortest_sanity_test() {
f64_shortest_sanity_test(format_shortest);
f32_shortest_sanity_test(format_shortest);
more_shortest_sanity_test(format_shortest);
}
#[test]
fn exact_sanity_test() {
f64_exact_sanity_test(format_exact);
f32_exact_sanity_test(format_exact);
}
#[bench]
fn bench_small_shortest(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; MAX_SIG_DIGITS];
b.iter(|| format_shortest(&decoded, &mut buf));
}
#[bench]
fn bench_big_shortest(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; MAX_SIG_DIGITS];
b.iter(|| format_shortest(&decoded, &mut buf));
}
#[bench]
fn bench_small_exact_3(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; 3];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_big_exact_3(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; 3];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_small_exact_12(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; 12];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_big_exact_12(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; 12];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_small_exact_inf(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; 1024];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_big_exact_inf(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; 1024];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[test]
fn test_to_shortest_str() {
to_shortest_str_test(format_shortest);
}
#[test]
fn test_to_shortest_exp_str() {
to_shortest_exp_str_test(format_shortest);
}
#[test]
fn test_to_exact_exp_str() {
to_exact_exp_str_test(format_exact);
}
#[test]
fn test_to_exact_fixed_str() {
to_exact_fixed_str_test(format_exact);
}

View File

@@ -0,0 +1,177 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{i16, f64};
use super::super::*;
use core::num::flt2dec::*;
use core::num::flt2dec::strategy::grisu::*;
#[test]
fn test_cached_power() {
assert_eq!(CACHED_POW10.first().unwrap().1, CACHED_POW10_FIRST_E);
assert_eq!(CACHED_POW10.last().unwrap().1, CACHED_POW10_LAST_E);
for e in -1137..961 { // full range for f64
let low = ALPHA - e - 64;
let high = GAMMA - e - 64;
let (_k, cached) = cached_power(low, high);
assert!(low <= cached.e && cached.e <= high,
"cached_power({}, {}) = {:?} is incorrect", low, high, cached);
}
}
#[test]
fn test_max_pow10_no_more_than() {
let mut prevtenk = 1;
for k in 1..10 {
let tenk = prevtenk * 10;
assert_eq!(max_pow10_no_more_than(tenk - 1), (k - 1, prevtenk));
assert_eq!(max_pow10_no_more_than(tenk), (k, tenk));
prevtenk = tenk;
}
}
#[test]
fn shortest_sanity_test() {
f64_shortest_sanity_test(format_shortest);
f32_shortest_sanity_test(format_shortest);
more_shortest_sanity_test(format_shortest);
}
#[test]
fn shortest_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
}
#[test] #[ignore] // it is too expensive
fn shortest_f32_exhaustive_equivalence_test() {
// it is hard to directly test the optimality of the output, but we can at least test if
// two different algorithms agree to each other.
//
// this reports the progress and the number of f32 values returned `None`.
// with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
// `done, ignored=17643160 passed=2121451879 failed=0`.
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
}
#[test] #[ignore] // is is too expensive
fn shortest_f64_hard_random_equivalence_test() {
// this again probably has to use appropriate rustc flags.
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f64_random_equivalence_test(format_shortest_opt, fallback,
MAX_SIG_DIGITS, 100_000_000);
}
#[test]
fn exact_sanity_test() {
f64_exact_sanity_test(format_exact);
f32_exact_sanity_test(format_exact);
}
#[test]
fn exact_f32_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
for k in 1..21 {
f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
|d, buf| fallback(d, buf, i16::MIN), k, 1_000);
}
}
#[test]
fn exact_f64_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
for k in 1..21 {
f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
|d, buf| fallback(d, buf, i16::MIN), k, 1_000);
}
}
#[bench]
fn bench_small_shortest(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; MAX_SIG_DIGITS];
b.iter(|| format_shortest(&decoded, &mut buf));
}
#[bench]
fn bench_big_shortest(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; MAX_SIG_DIGITS];
b.iter(|| format_shortest(&decoded, &mut buf));
}
#[bench]
fn bench_small_exact_3(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; 3];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_big_exact_3(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; 3];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_small_exact_12(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; 12];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_big_exact_12(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; 12];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_small_exact_inf(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [0; 1024];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[bench]
fn bench_big_exact_inf(b: &mut Bencher) {
let decoded = decode_finite(f64::MAX);
let mut buf = [0; 1024];
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
}
#[test]
fn test_to_shortest_str() {
to_shortest_str_test(format_shortest);
}
#[test]
fn test_to_shortest_exp_str() {
to_shortest_exp_str_test(format_shortest);
}
#[test]
fn test_to_exact_exp_str() {
to_exact_exp_str_test(format_exact);
}
#[test]
fn test_to_exact_fixed_str() {
to_exact_fixed_str_test(format_exact);
}

View File

@@ -29,6 +29,8 @@ mod u16;
mod u32;
mod u64;
mod flt2dec;
/// Helper function for testing numeric operations
pub fn test_num<T>(ten: T, two: T) where
T: PartialEq