use sealed::mem;
use sealed::ptr;
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub use alloc::string::String;
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub use alloc::vec::Vec;
use float::{cached_grisu_power, FloatType};
use itoa::itoa_forward;
use table::BASEN;
use util::{distance, floor, ln};
pub extern "C" fn exponent_notation_char(base: u64)
-> u8
{
if base >= 15 { b'^' } else { b'e' }
}
const MAX_FLOAT_SIZE: usize = 256;
const BUFFER_SIZE: usize = MAX_FLOAT_SIZE;
const TENS: [u64; 20] = [
10000000000000000000, 1000000000000000000, 100000000000000000,
10000000000000000, 1000000000000000, 100000000000000,
10000000000000, 1000000000000, 100000000000,
10000000000, 1000000000, 100000000,
10000000, 1000000, 100000,
10000, 1000, 100,
10, 1
];
unsafe extern "C"
fn round_digit(digits: *mut u8, ndigits: isize, delta: u64, mut rem: u64, kappa: u64, frac: u64)
{
while rem < frac && delta - rem >= kappa &&
(rem + kappa < frac || frac - rem > rem + kappa - frac) {
*digits.offset(ndigits - 1) -= 1;
rem += kappa;
}
}
unsafe extern "C"
fn generate_digits(fp: &FloatType, upper: &FloatType, lower: &FloatType, digits: *mut u8, k: *mut i32)
-> i32
{
let wfrac = upper.frac - fp.frac;
let mut delta = upper.frac - lower.frac;
let one = FloatType {
frac: 1 << -upper.exp,
exp: upper.exp,
};
let mut part1 = upper.frac >> -one.exp;
let mut part2 = upper.frac & (one.frac - 1);
let mut idx: isize = 0;
let mut kappa: i32 = 10;
let mut divp: *const u64 = TENS.as_ptr().add(10);
while kappa > 0 {
let div = *divp;
let digit = part1 / div;
if digit != 0 || idx != 0 {
*digits.offset(idx) = (digit as u8) + b'0';
idx += 1;
}
part1 -= (digit as u64) * div;
kappa -= 1;
let tmp = (part1 <<-one.exp) + part2;
if tmp <= delta {
*k += kappa;
round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac);
return idx as i32;
}
divp = divp.add(1);
}
let mut unit: *const u64 = TENS.as_ptr().add(18);
loop {
part2 *= 10;
delta *= 10;
kappa -= 1;
let digit = part2 >> -one.exp;
if digit != 0 || idx != 0 {
*digits.offset(idx) = (digit as u8) + b'0';
idx += 1;
}
part2 &= one.frac - 1;
if part2 < delta {
*k += kappa;
round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit);
return idx as i32;
}
unit = unit.sub(1);
}
}
unsafe extern "C" fn grisu2(d: f64, digits: *mut u8, k: *mut i32) -> i32
{
let mut w = FloatType::from_f64(d);
let (mut lower, mut upper) = w.normalized_boundaries();
w.normalize();
let mut ki: i32 = mem::uninitialized();
let cp = cached_grisu_power(upper.exp, &mut ki);
w = w.fast_multiply(&cp);
upper = upper.fast_multiply(&cp);
lower = lower.fast_multiply(&cp);
lower.frac += 1;
upper.frac -= 1;
*k = -ki;
return generate_digits(&w, &upper, &lower, digits, k);
}
unsafe extern "C" fn emit_digits(digits: *mut u8, mut ndigits: i32, dest: *mut u8, k: i32)
-> i32
{
let exp = k + ndigits - 1;
let mut exp = absv!(exp);
if k >= 0 && exp < (ndigits + 7) {
let idx = ndigits as usize;
let count = k as usize;
ptr::copy_nonoverlapping(digits, dest, idx);
ptr::write_bytes(dest.add(idx), b'0', count);
ptr::copy_nonoverlapping(b".0".as_ptr(), dest.add(idx + count), 2);
return ndigits + k + 2;
}
if k < 0 && (k > -7 || exp < 4) {
let mut offset = ndigits - absv!(k);
if offset <= 0 {
offset = -offset;
*dest = b'0';
*dest.add(1) = b'.';
ptr::write_bytes(dest.add(2), b'0', offset as usize);
let dst = dest.add(offset as usize + 2);
ptr::copy_nonoverlapping(digits, dst, ndigits as usize);
return ndigits + 2 + offset;
} else {
ptr::copy_nonoverlapping(digits, dest, offset as usize);
*dest.offset(offset as isize) = b'.';
let dst = dest.offset(offset as isize + 1);
let src = digits.offset(offset as isize);
let count = (ndigits - offset) as usize;
ptr::copy_nonoverlapping(src, dst, count);
return ndigits + 1;
}
}
ndigits = minv!(ndigits, 18);
let mut idx: isize = 0;
*dest.offset(idx) = *digits;
idx += 1;
if ndigits > 1 {
*dest.offset(idx) = b'.';
idx += 1;
let dst = dest.offset(idx);
let src = digits.add(1);
let count = (ndigits - 1) as usize;
ptr::copy_nonoverlapping(src, dst, count);
idx += (ndigits - 1) as isize;
}
*dest.offset(idx) = exponent_notation_char(10);
idx += 1;
let sign: u8 = match k + ndigits - 1 < 0 {
true => b'-',
false => b'+',
};
*dest.offset(idx) = sign;
idx += 1;
let mut cent: i32 = 0;
if exp > 99 {
cent = exp / 100;
*dest.offset(idx) = (cent as u8) + b'0';
idx += 1;
exp -= cent * 100;
}
if exp > 9 {
let dec = exp / 10;
*dest.offset(idx) = (dec as u8) + b'0';
idx += 1;
exp -= dec * 10;
} else if cent != 0 {
*dest.offset(idx) = b'0';
idx += 1;
}
let shift: u8 = (exp % 10) as u8;
*dest.offset(idx) = shift + b'0';
idx += 1;
idx as i32
}
unsafe extern "C" fn filter_special(fp: f64, dest: *mut u8) -> i32
{
const EXPONENT_MASK: u64 = FloatType::F64_EXPONENT_MASK;
const FRACTION_MASK: u64 = FloatType::F64_FRACTION_MASK;
if fp == 0.0 {
ptr::copy_nonoverlapping(b"0.0".as_ptr(), dest, 3);
return 3;
}
let bits = fp.to_bits();
let nan = (bits & EXPONENT_MASK) == EXPONENT_MASK;
if !nan {
return 0;
}
if bits & FRACTION_MASK != 0 {
ptr::copy_nonoverlapping(b"NaN".as_ptr(), dest, 3);
return 3;
} else {
ptr::copy_nonoverlapping(b"Infinity".as_ptr(), dest, 8);
return 8;
}
}
unsafe extern "C" fn fpconv_dtoa(d: f64, dest: *mut u8) -> i32
{
let mut digits: [u8; 18] = mem::uninitialized();
let mut str_len: i32 = 0;
let spec = filter_special(d, dest.offset(str_len as isize));
if spec != 0 {
return str_len + spec;
}
let mut k: i32 = 0;
let ndigits = grisu2(d, digits.as_mut_ptr(), &mut k);
str_len += emit_digits(digits.as_mut_ptr(), ndigits, dest.offset(str_len as isize), k);
str_len
}
unsafe extern "C" fn ftoa_base10(value: f64, first: *mut u8)
-> *mut u8
{
let len = fpconv_dtoa(value, first);
first.offset(len as isize)
}
#[inline]
#[allow(dead_code)]
fn v8_is_denormal(d: f64) -> bool
{
(d.to_bits() & FloatType::F64_EXPONENT_MASK) == 0
}
#[inline]
#[allow(dead_code)]
fn v8_is_special(d: f64) -> bool
{
let bits = d.to_bits();
(bits & FloatType::F64_EXPONENT_MASK) == FloatType::F64_EXPONENT_MASK
}
#[inline]
#[allow(dead_code)]
fn v8_is_nan(d: f64) -> bool
{
const EXPONENT_MASK: u64 = FloatType::F64_EXPONENT_MASK;
const FRACTION_MASK: u64 = FloatType::F64_FRACTION_MASK;
let bits = d.to_bits();
((bits & EXPONENT_MASK) == EXPONENT_MASK) && ((bits & FRACTION_MASK) != 0)
}
#[inline]
#[allow(dead_code)]
fn v8_is_infinite(d: f64) -> bool
{
const EXPONENT_MASK: u64 = FloatType::F64_EXPONENT_MASK;
const FRACTION_MASK: u64 = FloatType::F64_FRACTION_MASK;
let bits = d.to_bits();
((bits & EXPONENT_MASK) == EXPONENT_MASK) && ((bits & FRACTION_MASK) == 0)
}
#[inline]
#[allow(dead_code)]
fn v8_sign(d: f64) -> i32
{
let bits = d.to_bits();
if (bits & FloatType::F64_SIGN_MASK) == 0 { 1 } else { -1 }
}
#[inline]
#[allow(dead_code)]
fn v8_exponent(d: f64) -> i32
{
const EXPONENT_MASK: u64 = FloatType::F64_EXPONENT_MASK;
const SIGNIFICAND_SIZE: i32 = FloatType::F64_SIGNIFICAND_SIZE;
if v8_is_denormal(d) {
return FloatType::F64_DENORMAL_EXPONENT;
}
let bits = d.to_bits();
let biased_e = ((bits & EXPONENT_MASK) >> SIGNIFICAND_SIZE) as i32;
biased_e - FloatType::F64_EXPONENT_BIAS
}
#[inline]
#[allow(dead_code)]
fn v8_significand(d: f64) -> u64
{
let bits = d.to_bits();
let s = bits & FloatType::F64_FRACTION_MASK;
if !v8_is_denormal(d) {
s + FloatType::F64_HIDDEN_BIT_MASK
} else {
s
}
}
#[inline]
#[allow(dead_code)]
fn v8_next_double(d: f64) -> f64
{
let bits = d.to_bits();
if bits == FloatType::U64_INFINITY {
return f64::from_bits(FloatType::U64_INFINITY);
}
if v8_sign(d) < 0 && v8_significand(d) == 0 {
return 0.0;
}
if v8_sign(d) < 0 {
return f64::from_bits(bits - 1);
} else {
return f64::from_bits(bits + 1);
}
}
#[inline]
#[allow(dead_code)]
fn v8_modulo(x: f64, y: f64) -> f64
{
x % y
}
#[inline]
fn naive_exponent(d: f64, base: u64) -> i32
{
(floor(ln(d) / ln(base as f64))) as i32
}
unsafe extern "C" fn ftoa_naive(d: f64, first: *mut u8, base: u64)
-> *mut u8
{
debug_assert!(base >= 2 && base <= 36,"Numerical base must be from 2-36");
let length = filter_special(d, first);
if length != 0 {
return first.offset(length as isize);
}
debug_assert!(!v8_is_special(d));
debug_assert!(d != 0.0);
const MAX_NONDIGIT_LENGTH: usize = 25;
const MAX_DIGIT_LENGTH: usize = BUFFER_SIZE - MAX_NONDIGIT_LENGTH;
const SIZE: usize = 2200;
let mut buffer: [u8; SIZE] = mem::uninitialized();
let buffer = buffer.as_mut_ptr();
let initial_position: usize = SIZE / 2;
let mut integer_cursor = initial_position;
let mut fraction_cursor = initial_position;
let bf = base as f64;
let mut integer = floor(d);
let mut fraction = d - integer;
let mut delta = 0.5 * (v8_next_double(d) - d);
delta = maxv!(v8_next_double(0.0), delta);
debug_assert!(delta > 0.0);
if fraction > delta {
loop {
fraction *= bf;
delta *= bf;
let digit = fraction as i32;
*buffer.add(fraction_cursor) = *BASEN.get_unchecked(digit as usize);
fraction_cursor += 1;
fraction -= digit as f64;
if fraction > 0.5 || (fraction == 0.5 && (digit & 1) != 0) {
if fraction + delta > 1.0 {
loop {
fraction_cursor -= 1;
if fraction_cursor == initial_position-1 {
integer += 1.0;
break;
}
let c = *buffer.add(fraction_cursor);
let digit: i32;
if c <= b'9' {
digit = (c - b'0') as i32;
} else if c >= b'A' && c <= b'Z' {
digit = (c - b'A' + 10) as i32;
} else {
debug_assert!(c >= b'a' && c <= b'z');
digit = (c - b'a' + 10) as i32;
}
if digit + 1 < base as i32 {
let idx = (digit + 1) as usize;
*buffer.add(fraction_cursor) = *BASEN.get_unchecked(idx);
fraction_cursor += 1;
break;
}
}
break;
}
}
if delta >= fraction {
break;
}
}
}
while v8_exponent(integer / bf) > 0 {
integer /= bf;
integer_cursor -= 1;
*buffer.add(integer_cursor) = b'0';
}
loop {
let remainder = v8_modulo(integer, bf);
integer_cursor -= 1;
let idx = remainder as usize;
*buffer.add(integer_cursor) = *BASEN.get_unchecked(idx);
integer = (integer - remainder) / bf;
if integer <= 0.0 {
break;
}
};
if d <= 1e-5 || d >= 1e9 {
let exponent = naive_exponent(d, base);
let start: usize;
let end: usize;
if d <= 1e-5 {
start = ((initial_position as i32) - exponent - 1) as usize;
end = minv!(fraction_cursor, start + MAX_DIGIT_LENGTH + 1);
} else {
start = integer_cursor;
end = minv!(fraction_cursor, start + MAX_DIGIT_LENGTH + 1);
}
let mut buf_first = buffer.add(start);
let mut buf_last = buf_first.add(end - start);
loop {
buf_last = buf_last.sub(1);
if *buf_last != b'0' {
break;
}
}
let mut p = first;
*p = *buf_first;
p = p.add(1);
buf_first = buf_first.add(1);
*p = b'.';
p = p.add(1);
let dist = distance(buf_first, buf_last);
ptr::copy_nonoverlapping(buf_first, p, dist);
p = p.add(dist);
*p = exponent_notation_char(base);
p = p.add(1);
return itoa_forward(exponent as u64, p, base);
} else {
let mut p;
let integer_length = initial_position - integer_cursor;
let fraction_length = minv!(fraction_cursor - initial_position, MAX_DIGIT_LENGTH - integer_length);
ptr::copy_nonoverlapping(buffer.add(integer_cursor), first, integer_length);
p = first.add(integer_length);
if fraction_length > 0 {
*p = b'.';
p = p.add(1);
ptr::copy_nonoverlapping(buffer.add(initial_position), p, fraction_length);
p = p.add(fraction_length);
} else {
ptr::copy_nonoverlapping(b".0".as_ptr(), p, 2);
p = p.add(2);
}
return p;
}
}
#[inline(always)]
unsafe extern "C" fn ftoa_basen(value: f64, first: *mut u8, base: u64)
-> *mut u8
{
ftoa_naive(value, first, base)
}
macro_rules! check_digits {
($value:ident, $first:ident, $last:ident, $base:ident) => ({
debug_assert!(distance($first, $last) >= BUFFER_SIZE, "Need a larger buffer.");
})
}
macro_rules! ftoa_forward {
($value:ident, $first:ident, $base:ident) => (match $base {
10 => ftoa_base10($value, $first),
_ => ftoa_basen($value, $first, $base),
})
}
macro_rules! ftoa_unsafe_impl {
($value:ident, $first:ident, $last:ident, $base:ident) => ({
debug_assert!($first <= $last);
check_digits!($value, $first, $last, $base);
if $value < 0.0 {
*$first= b'-';
$value = -$value;
$first = $first.add(1);
}
ftoa_forward!($value, $first, $base)
})
}
macro_rules! ftoa_unsafe {
($value:ident, $first:ident, $last:ident, $base:ident) => ({
let dist = distance($first, $last);
if dist == 0 {
$first
} else if dist < BUFFER_SIZE {
let mut buffer: [u8; BUFFER_SIZE] = mem::uninitialized();
let mut f = buffer.as_mut_ptr();
let l = f.add(BUFFER_SIZE);
let mut v = $value as f64;
let b = $base as u64;
ftoa_unsafe_impl!(v, f, l, b);
let length = minv!(distance(f, l), dist);
ptr::copy_nonoverlapping(f, $first, length);
$first.add(length)
} else {
let mut v = $value as f64;
let b = $base as u64;
ftoa_unsafe_impl!(v, $first, $last, b)
}
})
}
macro_rules! unsafe_impl {
($func:ident, $t:ty) => (
#[inline]
pub unsafe extern "C" fn $func(
value: $t,
mut first: *mut u8,
last: *mut u8,
base: u8
)
-> *mut u8
{
ftoa_unsafe!(value, first, last, base)
}
)
}
unsafe_impl!(f32toa_unsafe, f32);
unsafe_impl!(f64toa_unsafe, f64);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(f32toa_string, f32, f32toa_unsafe, BUFFER_SIZE);
#[cfg(any(feature = "std", feature = "alloc"))]
string_impl!(f64toa_string, f64, f64toa_unsafe, BUFFER_SIZE);
#[cfg(any(feature = "std", feature = "alloc"))]
#[cfg(test)]
mod tests {
use super::*;
use atof::*;
use util::*;
const F32_DATA : [f32; 31] = [0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e-8, 1.2345e-11, 1.2345e-38];
const F64_DATA: [f64; 33] = [0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e+308, 1.2345e-8, 1.2345e-11, 1.2345e-38, 1.2345e-299];
#[test]
fn f32toa_base2_test() {
assert_eq!("0.0", &f32toa_string(0.0, 2));
assert_eq!("1.0", &f32toa_string(1.0, 2));
assert_eq!("10.0", &f32toa_string(2.0, 2));
assert_eq!("1.1", &f32toa_string(1.5, 2));
assert_eq!("1.01", &f32toa_string(1.25, 2));
assert_eq!("1.001111000000110010", &f32toa_string(1.2345678901234567890e0, 2)[..20]);
assert_eq!("1100.010110000111111", &f32toa_string(1.2345678901234567890e1, 2)[..20]);
assert_eq!("1111011.011101001111", &f32toa_string(1.2345678901234567890e2, 2)[..20]);
assert_eq!("10011010010.10010001", &f32toa_string(1.2345678901234567890e3, 2)[..20]);
assert_eq!("-1.001111000000110010", &f32toa_string(-1.2345678901234567890e0, 2)[..21]);
assert_eq!("-1100.010110000111111", &f32toa_string(-1.2345678901234567890e1, 2)[..21]);
assert_eq!("-1111011.011101001111", &f32toa_string(-1.2345678901234567890e2, 2)[..21]);
assert_eq!("-10011010010.10010001", &f32toa_string(-1.2345678901234567890e3, 2)[..21]);
assert_eq!("NaN", &f32toa_string(F32_NAN, 2));
assert_eq!("Infinity", &f32toa_string(F32_INFINITY, 2));
}
#[test]
fn f32toa_base10_test() {
assert_eq!("0.0", &f32toa_string(0.0, 10));
assert_eq!("1.0", &f32toa_string(1.0, 10));
assert_eq!("10.0", &f32toa_string(10.0, 10));
assert_eq!("1.234567", &f32toa_string(1.2345678901234567890e0, 10)[..8]);
assert_eq!("12.34567", &f32toa_string(1.2345678901234567890e1, 10)[..8]);
assert_eq!("123.4567", &f32toa_string(1.2345678901234567890e2, 10)[..8]);
assert_eq!("1234.567", &f32toa_string(1.2345678901234567890e3, 10)[..8]);
assert_eq!("-1.234567", &f32toa_string(-1.2345678901234567890e0, 10)[..9]);
assert_eq!("-12.34567", &f32toa_string(-1.2345678901234567890e1, 10)[..9]);
assert_eq!("-123.4567", &f32toa_string(-1.2345678901234567890e2, 10)[..9]);
assert_eq!("-1234.567", &f32toa_string(-1.2345678901234567890e3, 10)[..9]);
assert_eq!("NaN", &f32toa_string(F32_NAN, 10));
assert_eq!("Infinity", &f32toa_string(F32_INFINITY, 10));
}
#[test]
fn f32toa_base10_roundtrip_test() {
for f in F32_DATA.iter() {
let s = f32toa_string(*f, 10);
assert_relative_eq!(atof32_bytes(s.as_bytes(), 10), *f, epsilon=1e-6, max_relative=1e-6);
}
}
#[test]
fn f32toa_basen_roundtrip_test() {
for f in F32_DATA.iter() {
for radix in 2..37 {
let s = f32toa_string(*f, radix);
assert_relative_eq!(atof32_bytes(s.as_bytes(), radix), *f, max_relative=2e-5);
}
}
}
#[test]
fn f64toa_base2_test() {
assert_eq!("0.0", &f64toa_string(0.0, 2));
assert_eq!("1.0", &f64toa_string(1.0, 2));
assert_eq!("10.0", &f64toa_string(2.0, 2));
assert_eq!("1.00111100000011001010010000101000110001", &f64toa_string(1.2345678901234567890e0, 2)[..40]);
assert_eq!("1100.01011000011111100110100110010111101", &f64toa_string(1.2345678901234567890e1, 2)[..40]);
assert_eq!("1111011.01110100111100000001111111101101", &f64toa_string(1.2345678901234567890e2, 2)[..40]);
assert_eq!("10011010010.1001000101100001001111110100", &f64toa_string(1.2345678901234567890e3, 2)[..40]);
assert_eq!("-1.00111100000011001010010000101000110001", &f64toa_string(-1.2345678901234567890e0, 2)[..41]);
assert_eq!("-1100.01011000011111100110100110010111101", &f64toa_string(-1.2345678901234567890e1, 2)[..41]);
assert_eq!("-1111011.01110100111100000001111111101101", &f64toa_string(-1.2345678901234567890e2, 2)[..41]);
assert_eq!("-10011010010.1001000101100001001111110100", &f64toa_string(-1.2345678901234567890e3, 2)[..41]);
assert_eq!("NaN", &f64toa_string(F64_NAN, 2));
assert_eq!("Infinity", &f64toa_string(F64_INFINITY, 2));
}
#[test]
fn f64toa_base10_test() {
assert_eq!("0.0", &f64toa_string(0.0, 10));
assert_eq!("1.0", &f64toa_string(1.0, 10));
assert_eq!("10.0", &f64toa_string(10.0, 10));
assert_eq!("1.234567", &f64toa_string(1.2345678901234567890e0, 10)[..8]);
assert_eq!("12.34567", &f64toa_string(1.2345678901234567890e1, 10)[..8]);
assert_eq!("123.4567", &f64toa_string(1.2345678901234567890e2, 10)[..8]);
assert_eq!("1234.567", &f64toa_string(1.2345678901234567890e3, 10)[..8]);
assert_eq!("-1.234567", &f64toa_string(-1.2345678901234567890e0, 10)[..9]);
assert_eq!("-12.34567", &f64toa_string(-1.2345678901234567890e1, 10)[..9]);
assert_eq!("-123.4567", &f64toa_string(-1.2345678901234567890e2, 10)[..9]);
assert_eq!("-1234.567", &f64toa_string(-1.2345678901234567890e3, 10)[..9]);
assert_eq!("NaN", &f64toa_string(F64_NAN, 10));
assert_eq!("Infinity", &f64toa_string(F64_INFINITY, 10));
}
#[test]
fn f64toa_base10_roundtrip_test() {
for f in F64_DATA.iter() {
let s = f64toa_string(*f, 10);
assert_relative_eq!(atof64_bytes(s.as_bytes(), 10), *f, epsilon=1e-12, max_relative=1e-12);
}
}
#[test]
fn f64toa_basen_roundtrip_test() {
for f in F64_DATA.iter() {
for radix in 2..37 {
let s = f64toa_string(*f, radix);
assert_relative_eq!(atof64_bytes(s.as_bytes(), radix), *f, max_relative=3e-5);
}
}
}
}