Auto merge of #76241 - RalfJung:flt2dec, r=Mark-Simulacrum
flt2dec: properly handle uninitialized memory The float-to-str code currently uses uninitialized memory incorrectly (see https://github.com/rust-lang/rust/issues/76092). This PR fixes that. Specifically, that code used `&mut [T]` as "out references", but it would be incorrect for the caller to actually pass uninitialized memory. So the PR changes this to `&mut [MaybeUninit<T>]`, and then functions return a `&[T]` to the part of the buffer that they initialized (some functions already did that, indirectly via `&Formatted`, others were adjusted to return that buffer instead of just the initialized length). What I particularly like about this is that it moves `unsafe` to the right place: previously, the outermost caller had to use `unsafe` to assert that things are initialized; now it is the functions that do the actual initializing which have the corresponding `unsafe` block when they call `MaybeUninit::slice_get_ref` (renamed in https://github.com/rust-lang/rust/pull/76217 to `slice_assume_init_ref`). Reviewers please be aware that I have no idea how any of this code actually works. My changes were purely mechanical and type-driven. The test suite passes so I guess I didn't screw up badly... Cc @sfackler this is somewhat related to your RFC, and possibly some of this code could benefit from (a generalized version of) the API you describe there. But for now I think what I did is "good enough". Fixes https://github.com/rust-lang/rust/issues/76092.
This commit is contained in:
commit
95815c9b2b
@ -1,59 +1,76 @@
|
||||
use super::super::*;
|
||||
use core::num::flt2dec::strategy::dragon::*;
|
||||
use std::mem::MaybeUninit;
|
||||
use test::Bencher;
|
||||
|
||||
#[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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(0); 1024];
|
||||
b.iter(|| {
|
||||
format_exact(&decoded, &mut buf, i16::MIN);
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::super::*;
|
||||
use core::num::flt2dec::strategy::grisu::*;
|
||||
use std::mem::MaybeUninit;
|
||||
use test::Bencher;
|
||||
|
||||
pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
|
||||
@ -12,55 +13,71 @@ pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
|
||||
#[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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(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));
|
||||
let mut buf = [MaybeUninit::new(0); 1024];
|
||||
b.iter(|| {
|
||||
format_exact(&decoded, &mut buf, i16::MIN);
|
||||
});
|
||||
}
|
||||
|
@ -14,25 +14,17 @@ fn float_to_decimal_common_exact<T>(
|
||||
where
|
||||
T: flt2dec::DecodableFloat,
|
||||
{
|
||||
// SAFETY: Possible undefined behavior, see FIXME(#76092)
|
||||
unsafe {
|
||||
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
|
||||
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit();
|
||||
// FIXME(#76092): This is calling `assume_init_mut` on an uninitialized
|
||||
// `MaybeUninit` (here and elsewhere in this file). Revisit this once
|
||||
// we decided whether that is valid or not.
|
||||
// We can do this only because we are libstd and coupled to the compiler.
|
||||
// (FWIW, using `freeze` would not be enough; `flt2dec::Part` is an enum!)
|
||||
let formatted = flt2dec::to_exact_fixed_str(
|
||||
flt2dec::strategy::grisu::format_exact,
|
||||
*num,
|
||||
sign,
|
||||
precision,
|
||||
buf.assume_init_mut(),
|
||||
parts.assume_init_mut(),
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64
|
||||
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 4] = MaybeUninit::uninit_array();
|
||||
let formatted = flt2dec::to_exact_fixed_str(
|
||||
flt2dec::strategy::grisu::format_exact,
|
||||
*num,
|
||||
sign,
|
||||
precision,
|
||||
&mut buf,
|
||||
&mut parts,
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
|
||||
// Don't inline this so callers that call both this and the above won't wind
|
||||
@ -47,22 +39,18 @@ fn float_to_decimal_common_shortest<T>(
|
||||
where
|
||||
T: flt2dec::DecodableFloat,
|
||||
{
|
||||
// SAFETY: Possible undefined behavior, see FIXME(#76092)
|
||||
unsafe {
|
||||
// enough for f32 and f64
|
||||
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();
|
||||
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit();
|
||||
// FIXME(#76092)
|
||||
let formatted = flt2dec::to_shortest_str(
|
||||
flt2dec::strategy::grisu::format_shortest,
|
||||
*num,
|
||||
sign,
|
||||
precision,
|
||||
buf.assume_init_mut(),
|
||||
parts.assume_init_mut(),
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
// enough for f32 and f64
|
||||
let mut buf: [MaybeUninit<u8>; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array();
|
||||
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 4] = MaybeUninit::uninit_array();
|
||||
let formatted = flt2dec::to_shortest_str(
|
||||
flt2dec::strategy::grisu::format_shortest,
|
||||
*num,
|
||||
sign,
|
||||
precision,
|
||||
&mut buf,
|
||||
&mut parts,
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
|
||||
// Common code of floating point Debug and Display.
|
||||
@ -103,22 +91,18 @@ fn float_to_exponential_common_exact<T>(
|
||||
where
|
||||
T: flt2dec::DecodableFloat,
|
||||
{
|
||||
// SAFETY: Possible undefined behavior, see FIXME(#76092)
|
||||
unsafe {
|
||||
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
|
||||
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit();
|
||||
// FIXME(#76092)
|
||||
let formatted = flt2dec::to_exact_exp_str(
|
||||
flt2dec::strategy::grisu::format_exact,
|
||||
*num,
|
||||
sign,
|
||||
precision,
|
||||
upper,
|
||||
buf.assume_init_mut(),
|
||||
parts.assume_init_mut(),
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64
|
||||
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 6] = MaybeUninit::uninit_array();
|
||||
let formatted = flt2dec::to_exact_exp_str(
|
||||
flt2dec::strategy::grisu::format_exact,
|
||||
*num,
|
||||
sign,
|
||||
precision,
|
||||
upper,
|
||||
&mut buf,
|
||||
&mut parts,
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
|
||||
// Don't inline this so callers that call both this and the above won't wind
|
||||
@ -133,23 +117,19 @@ fn float_to_exponential_common_shortest<T>(
|
||||
where
|
||||
T: flt2dec::DecodableFloat,
|
||||
{
|
||||
// SAFETY: Possible undefined behavior, see FIXME(#76092)
|
||||
unsafe {
|
||||
// enough for f32 and f64
|
||||
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();
|
||||
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit();
|
||||
// FIXME(#76092)
|
||||
let formatted = flt2dec::to_shortest_exp_str(
|
||||
flt2dec::strategy::grisu::format_shortest,
|
||||
*num,
|
||||
sign,
|
||||
(0, 0),
|
||||
upper,
|
||||
buf.assume_init_mut(),
|
||||
parts.assume_init_mut(),
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
// enough for f32 and f64
|
||||
let mut buf: [MaybeUninit<u8>; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array();
|
||||
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 6] = MaybeUninit::uninit_array();
|
||||
let formatted = flt2dec::to_shortest_exp_str(
|
||||
flt2dec::strategy::grisu::format_shortest,
|
||||
*num,
|
||||
sign,
|
||||
(0, 0),
|
||||
upper,
|
||||
&mut buf,
|
||||
&mut parts,
|
||||
);
|
||||
fmt.pad_formatted_parts(&formatted)
|
||||
}
|
||||
|
||||
// Common code of floating point LowerExp and UpperExp.
|
||||
|
@ -124,6 +124,8 @@ functions.
|
||||
|
||||
pub use self::decoder::{decode, DecodableFloat, Decoded, FullDecoded};
|
||||
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
pub mod decoder;
|
||||
pub mod estimator;
|
||||
|
||||
@ -140,23 +142,23 @@ pub mod strategy {
|
||||
/// The exact formula is `ceil(# bits in mantissa * log_10 2 + 1)`.
|
||||
pub const MAX_SIG_DIGITS: usize = 17;
|
||||
|
||||
/// When `d[..n]` contains decimal digits, increase the last digit and propagate carry.
|
||||
/// Returns a next digit when it causes the length change.
|
||||
/// When `d` contains decimal digits, increase the last digit and propagate carry.
|
||||
/// Returns a next digit when it causes the length to change.
|
||||
#[doc(hidden)]
|
||||
pub fn round_up(d: &mut [u8], n: usize) -> Option<u8> {
|
||||
match d[..n].iter().rposition(|&c| c != b'9') {
|
||||
pub fn round_up(d: &mut [u8]) -> Option<u8> {
|
||||
match d.iter().rposition(|&c| c != b'9') {
|
||||
Some(i) => {
|
||||
// d[i+1..n] is all nines
|
||||
d[i] += 1;
|
||||
for j in i + 1..n {
|
||||
for j in i + 1..d.len() {
|
||||
d[j] = b'0';
|
||||
}
|
||||
None
|
||||
}
|
||||
None if n > 0 => {
|
||||
None if d.len() > 0 => {
|
||||
// 999..999 rounds to 1000..000 with an increased exponent
|
||||
d[0] = b'1';
|
||||
for j in 1..n {
|
||||
for j in 1..d.len() {
|
||||
d[j] = b'0';
|
||||
}
|
||||
Some(b'0')
|
||||
@ -281,7 +283,7 @@ fn digits_to_dec_str<'a>(
|
||||
buf: &'a [u8],
|
||||
exp: i16,
|
||||
frac_digits: usize,
|
||||
parts: &'a mut [Part<'a>],
|
||||
parts: &'a mut [MaybeUninit<Part<'a>>],
|
||||
) -> &'a [Part<'a>] {
|
||||
assert!(!buf.is_empty());
|
||||
assert!(buf[0] > b'0');
|
||||
@ -303,38 +305,44 @@ fn digits_to_dec_str<'a>(
|
||||
if exp <= 0 {
|
||||
// the decimal point is before rendered digits: [0.][000...000][1234][____]
|
||||
let minus_exp = -(exp as i32) as usize;
|
||||
parts[0] = Part::Copy(b"0.");
|
||||
parts[1] = Part::Zero(minus_exp);
|
||||
parts[2] = Part::Copy(buf);
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0."));
|
||||
parts[1] = MaybeUninit::new(Part::Zero(minus_exp));
|
||||
parts[2] = MaybeUninit::new(Part::Copy(buf));
|
||||
if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp {
|
||||
parts[3] = Part::Zero((frac_digits - buf.len()) - minus_exp);
|
||||
&parts[..4]
|
||||
parts[3] = MaybeUninit::new(Part::Zero((frac_digits - buf.len()) - minus_exp));
|
||||
// SAFETY: we just initialized the elements `..4`.
|
||||
unsafe { MaybeUninit::slice_get_ref(&parts[..4]) }
|
||||
} else {
|
||||
&parts[..3]
|
||||
// SAFETY: we just initialized the elements `..3`.
|
||||
unsafe { MaybeUninit::slice_get_ref(&parts[..3]) }
|
||||
}
|
||||
} else {
|
||||
let exp = exp as usize;
|
||||
if exp < buf.len() {
|
||||
// the decimal point is inside rendered digits: [12][.][34][____]
|
||||
parts[0] = Part::Copy(&buf[..exp]);
|
||||
parts[1] = Part::Copy(b".");
|
||||
parts[2] = Part::Copy(&buf[exp..]);
|
||||
parts[0] = MaybeUninit::new(Part::Copy(&buf[..exp]));
|
||||
parts[1] = MaybeUninit::new(Part::Copy(b"."));
|
||||
parts[2] = MaybeUninit::new(Part::Copy(&buf[exp..]));
|
||||
if frac_digits > buf.len() - exp {
|
||||
parts[3] = Part::Zero(frac_digits - (buf.len() - exp));
|
||||
&parts[..4]
|
||||
parts[3] = MaybeUninit::new(Part::Zero(frac_digits - (buf.len() - exp)));
|
||||
// SAFETY: we just initialized the elements `..4`.
|
||||
unsafe { MaybeUninit::slice_get_ref(&parts[..4]) }
|
||||
} else {
|
||||
&parts[..3]
|
||||
// SAFETY: we just initialized the elements `..3`.
|
||||
unsafe { MaybeUninit::slice_get_ref(&parts[..3]) }
|
||||
}
|
||||
} else {
|
||||
// the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__].
|
||||
parts[0] = Part::Copy(buf);
|
||||
parts[1] = Part::Zero(exp - buf.len());
|
||||
parts[0] = MaybeUninit::new(Part::Copy(buf));
|
||||
parts[1] = MaybeUninit::new(Part::Zero(exp - buf.len()));
|
||||
if frac_digits > 0 {
|
||||
parts[2] = Part::Copy(b".");
|
||||
parts[3] = Part::Zero(frac_digits);
|
||||
&parts[..4]
|
||||
parts[2] = MaybeUninit::new(Part::Copy(b"."));
|
||||
parts[3] = MaybeUninit::new(Part::Zero(frac_digits));
|
||||
// SAFETY: we just initialized the elements `..4`.
|
||||
unsafe { MaybeUninit::slice_get_ref(&parts[..4]) }
|
||||
} else {
|
||||
&parts[..2]
|
||||
// SAFETY: we just initialized the elements `..2`.
|
||||
unsafe { MaybeUninit::slice_get_ref(&parts[..2]) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,7 +362,7 @@ fn digits_to_exp_str<'a>(
|
||||
exp: i16,
|
||||
min_ndigits: usize,
|
||||
upper: bool,
|
||||
parts: &'a mut [Part<'a>],
|
||||
parts: &'a mut [MaybeUninit<Part<'a>>],
|
||||
) -> &'a [Part<'a>] {
|
||||
assert!(!buf.is_empty());
|
||||
assert!(buf[0] > b'0');
|
||||
@ -362,15 +370,15 @@ fn digits_to_exp_str<'a>(
|
||||
|
||||
let mut n = 0;
|
||||
|
||||
parts[n] = Part::Copy(&buf[..1]);
|
||||
parts[n] = MaybeUninit::new(Part::Copy(&buf[..1]));
|
||||
n += 1;
|
||||
|
||||
if buf.len() > 1 || min_ndigits > 1 {
|
||||
parts[n] = Part::Copy(b".");
|
||||
parts[n + 1] = Part::Copy(&buf[1..]);
|
||||
parts[n] = MaybeUninit::new(Part::Copy(b"."));
|
||||
parts[n + 1] = MaybeUninit::new(Part::Copy(&buf[1..]));
|
||||
n += 2;
|
||||
if min_ndigits > buf.len() {
|
||||
parts[n] = Part::Zero(min_ndigits - buf.len());
|
||||
parts[n] = MaybeUninit::new(Part::Zero(min_ndigits - buf.len()));
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
@ -378,13 +386,14 @@ fn digits_to_exp_str<'a>(
|
||||
// 0.1234 x 10^exp = 1.234 x 10^(exp-1)
|
||||
let exp = exp as i32 - 1; // avoid underflow when exp is i16::MIN
|
||||
if exp < 0 {
|
||||
parts[n] = Part::Copy(if upper { b"E-" } else { b"e-" });
|
||||
parts[n + 1] = Part::Num(-exp as u16);
|
||||
parts[n] = MaybeUninit::new(Part::Copy(if upper { b"E-" } else { b"e-" }));
|
||||
parts[n + 1] = MaybeUninit::new(Part::Num(-exp as u16));
|
||||
} else {
|
||||
parts[n] = Part::Copy(if upper { b"E" } else { b"e" });
|
||||
parts[n + 1] = Part::Num(exp as u16);
|
||||
parts[n] = MaybeUninit::new(Part::Copy(if upper { b"E" } else { b"e" }));
|
||||
parts[n + 1] = MaybeUninit::new(Part::Num(exp as u16));
|
||||
}
|
||||
&parts[..n + 2]
|
||||
// SAFETY: we just initialized the elements `..n + 2`.
|
||||
unsafe { MaybeUninit::slice_get_ref(&parts[..n + 2]) }
|
||||
}
|
||||
|
||||
/// Sign formatting options.
|
||||
@ -446,6 +455,7 @@ fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static
|
||||
/// (which can be an empty string if no sign is rendered).
|
||||
///
|
||||
/// `format_shortest` should be the underlying digit-generation function.
|
||||
/// It should return the part of the buffer that it initialized.
|
||||
/// You probably would want `strategy::grisu::format_shortest` for this.
|
||||
///
|
||||
/// `frac_digits` can be less than the number of actual fractional digits in `v`;
|
||||
@ -461,12 +471,12 @@ pub fn to_shortest_str<'a, T, F>(
|
||||
v: T,
|
||||
sign: Sign,
|
||||
frac_digits: usize,
|
||||
buf: &'a mut [u8],
|
||||
parts: &'a mut [Part<'a>],
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
parts: &'a mut [MaybeUninit<Part<'a>>],
|
||||
) -> Formatted<'a>
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
assert!(parts.len() >= 4);
|
||||
assert!(buf.len() >= MAX_SIG_DIGITS);
|
||||
@ -475,27 +485,31 @@ where
|
||||
let sign = determine_sign(sign, &full_decoded, negative);
|
||||
match full_decoded {
|
||||
FullDecoded::Nan => {
|
||||
parts[0] = Part::Copy(b"NaN");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"NaN"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Infinite => {
|
||||
parts[0] = Part::Copy(b"inf");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"inf"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Zero => {
|
||||
if frac_digits > 0 {
|
||||
// [0.][0000]
|
||||
parts[0] = Part::Copy(b"0.");
|
||||
parts[1] = Part::Zero(frac_digits);
|
||||
Formatted { sign, parts: &parts[..2] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0."));
|
||||
parts[1] = MaybeUninit::new(Part::Zero(frac_digits));
|
||||
// SAFETY: we just initialized the elements `..2`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..2]) } }
|
||||
} else {
|
||||
parts[0] = Part::Copy(b"0");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
}
|
||||
FullDecoded::Finite(ref decoded) => {
|
||||
let (len, exp) = format_shortest(decoded, buf);
|
||||
Formatted { sign, parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) }
|
||||
let (buf, exp) = format_shortest(decoded, buf);
|
||||
Formatted { sign, parts: digits_to_dec_str(buf, exp, frac_digits, parts) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -509,6 +523,7 @@ where
|
||||
/// an empty string if no sign is rendered).
|
||||
///
|
||||
/// `format_shortest` should be the underlying digit-generation function.
|
||||
/// It should return the part of the buffer that it initialized.
|
||||
/// You probably would want `strategy::grisu::format_shortest` for this.
|
||||
///
|
||||
/// The `dec_bounds` is a tuple `(lo, hi)` such that the number is formatted
|
||||
@ -525,12 +540,12 @@ pub fn to_shortest_exp_str<'a, T, F>(
|
||||
sign: Sign,
|
||||
dec_bounds: (i16, i16),
|
||||
upper: bool,
|
||||
buf: &'a mut [u8],
|
||||
parts: &'a mut [Part<'a>],
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
parts: &'a mut [MaybeUninit<Part<'a>>],
|
||||
) -> Formatted<'a>
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
assert!(parts.len() >= 6);
|
||||
assert!(buf.len() >= MAX_SIG_DIGITS);
|
||||
@ -540,28 +555,31 @@ where
|
||||
let sign = determine_sign(sign, &full_decoded, negative);
|
||||
match full_decoded {
|
||||
FullDecoded::Nan => {
|
||||
parts[0] = Part::Copy(b"NaN");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"NaN"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Infinite => {
|
||||
parts[0] = Part::Copy(b"inf");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"inf"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Zero => {
|
||||
parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 {
|
||||
Part::Copy(b"0")
|
||||
MaybeUninit::new(Part::Copy(b"0"))
|
||||
} else {
|
||||
Part::Copy(if upper { b"0E0" } else { b"0e0" })
|
||||
MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" }))
|
||||
};
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Finite(ref decoded) => {
|
||||
let (len, exp) = format_shortest(decoded, buf);
|
||||
let (buf, exp) = format_shortest(decoded, buf);
|
||||
let vis_exp = exp as i32 - 1;
|
||||
let parts = if dec_bounds.0 as i32 <= vis_exp && vis_exp < dec_bounds.1 as i32 {
|
||||
digits_to_dec_str(&buf[..len], exp, 0, parts)
|
||||
digits_to_dec_str(buf, exp, 0, parts)
|
||||
} else {
|
||||
digits_to_exp_str(&buf[..len], exp, 0, upper, parts)
|
||||
digits_to_exp_str(buf, exp, 0, upper, parts)
|
||||
};
|
||||
Formatted { sign, parts }
|
||||
}
|
||||
@ -600,6 +618,7 @@ fn estimate_max_buf_len(exp: i16) -> usize {
|
||||
/// an empty string if no sign is rendered).
|
||||
///
|
||||
/// `format_exact` should be the underlying digit-generation function.
|
||||
/// It should return the part of the buffer that it initialized.
|
||||
/// You probably would want `strategy::grisu::format_exact` for this.
|
||||
///
|
||||
/// The byte buffer should be at least `ndigits` bytes long unless `ndigits` is
|
||||
@ -613,12 +632,12 @@ pub fn to_exact_exp_str<'a, T, F>(
|
||||
sign: Sign,
|
||||
ndigits: usize,
|
||||
upper: bool,
|
||||
buf: &'a mut [u8],
|
||||
parts: &'a mut [Part<'a>],
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
parts: &'a mut [MaybeUninit<Part<'a>>],
|
||||
) -> Formatted<'a>
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
assert!(parts.len() >= 6);
|
||||
assert!(ndigits > 0);
|
||||
@ -627,23 +646,27 @@ where
|
||||
let sign = determine_sign(sign, &full_decoded, negative);
|
||||
match full_decoded {
|
||||
FullDecoded::Nan => {
|
||||
parts[0] = Part::Copy(b"NaN");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"NaN"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Infinite => {
|
||||
parts[0] = Part::Copy(b"inf");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"inf"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Zero => {
|
||||
if ndigits > 1 {
|
||||
// [0.][0000][e0]
|
||||
parts[0] = Part::Copy(b"0.");
|
||||
parts[1] = Part::Zero(ndigits - 1);
|
||||
parts[2] = Part::Copy(if upper { b"E0" } else { b"e0" });
|
||||
Formatted { sign, parts: &parts[..3] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0."));
|
||||
parts[1] = MaybeUninit::new(Part::Zero(ndigits - 1));
|
||||
parts[2] = MaybeUninit::new(Part::Copy(if upper { b"E0" } else { b"e0" }));
|
||||
// SAFETY: we just initialized the elements `..3`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..3]) } }
|
||||
} else {
|
||||
parts[0] = Part::Copy(if upper { b"0E0" } else { b"0e0" });
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" }));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
}
|
||||
FullDecoded::Finite(ref decoded) => {
|
||||
@ -651,8 +674,8 @@ where
|
||||
assert!(buf.len() >= ndigits || buf.len() >= maxlen);
|
||||
|
||||
let trunc = if ndigits < maxlen { ndigits } else { maxlen };
|
||||
let (len, exp) = format_exact(decoded, &mut buf[..trunc], i16::MIN);
|
||||
Formatted { sign, parts: digits_to_exp_str(&buf[..len], exp, ndigits, upper, parts) }
|
||||
let (buf, exp) = format_exact(decoded, &mut buf[..trunc], i16::MIN);
|
||||
Formatted { sign, parts: digits_to_exp_str(buf, exp, ndigits, upper, parts) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -665,6 +688,7 @@ where
|
||||
/// (which can be an empty string if no sign is rendered).
|
||||
///
|
||||
/// `format_exact` should be the underlying digit-generation function.
|
||||
/// It should return the part of the buffer that it initialized.
|
||||
/// You probably would want `strategy::grisu::format_exact` for this.
|
||||
///
|
||||
/// The byte buffer should be enough for the output unless `frac_digits` is
|
||||
@ -677,12 +701,12 @@ pub fn to_exact_fixed_str<'a, T, F>(
|
||||
v: T,
|
||||
sign: Sign,
|
||||
frac_digits: usize,
|
||||
buf: &'a mut [u8],
|
||||
parts: &'a mut [Part<'a>],
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
parts: &'a mut [MaybeUninit<Part<'a>>],
|
||||
) -> Formatted<'a>
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
assert!(parts.len() >= 4);
|
||||
|
||||
@ -690,22 +714,26 @@ where
|
||||
let sign = determine_sign(sign, &full_decoded, negative);
|
||||
match full_decoded {
|
||||
FullDecoded::Nan => {
|
||||
parts[0] = Part::Copy(b"NaN");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"NaN"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Infinite => {
|
||||
parts[0] = Part::Copy(b"inf");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"inf"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
FullDecoded::Zero => {
|
||||
if frac_digits > 0 {
|
||||
// [0.][0000]
|
||||
parts[0] = Part::Copy(b"0.");
|
||||
parts[1] = Part::Zero(frac_digits);
|
||||
Formatted { sign, parts: &parts[..2] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0."));
|
||||
parts[1] = MaybeUninit::new(Part::Zero(frac_digits));
|
||||
// SAFETY: we just initialized the elements `..2`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..2]) } }
|
||||
} else {
|
||||
parts[0] = Part::Copy(b"0");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
}
|
||||
FullDecoded::Finite(ref decoded) => {
|
||||
@ -716,23 +744,25 @@ where
|
||||
// `format_exact` will end rendering digits much earlier in this case,
|
||||
// because we are strictly limited by `maxlen`.
|
||||
let limit = if frac_digits < 0x8000 { -(frac_digits as i16) } else { i16::MIN };
|
||||
let (len, exp) = format_exact(decoded, &mut buf[..maxlen], limit);
|
||||
let (buf, exp) = format_exact(decoded, &mut buf[..maxlen], limit);
|
||||
if exp <= limit {
|
||||
// the restriction couldn't been met, so this should render like zero no matter
|
||||
// `exp` was. this does not include the case that the restriction has been met
|
||||
// only after the final rounding-up; it's a regular case with `exp = limit + 1`.
|
||||
debug_assert_eq!(len, 0);
|
||||
debug_assert_eq!(buf.len(), 0);
|
||||
if frac_digits > 0 {
|
||||
// [0.][0000]
|
||||
parts[0] = Part::Copy(b"0.");
|
||||
parts[1] = Part::Zero(frac_digits);
|
||||
Formatted { sign, parts: &parts[..2] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0."));
|
||||
parts[1] = MaybeUninit::new(Part::Zero(frac_digits));
|
||||
// SAFETY: we just initialized the elements `..2`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..2]) } }
|
||||
} else {
|
||||
parts[0] = Part::Copy(b"0");
|
||||
Formatted { sign, parts: &parts[..1] }
|
||||
parts[0] = MaybeUninit::new(Part::Copy(b"0"));
|
||||
// SAFETY: we just initialized the elements `..1`.
|
||||
Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } }
|
||||
}
|
||||
} else {
|
||||
Formatted { sign, parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) }
|
||||
Formatted { sign, parts: digits_to_dec_str(buf, exp, frac_digits, parts) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
//! quickly and accurately. SIGPLAN Not. 31, 5 (May. 1996), 108-116.
|
||||
|
||||
use crate::cmp::Ordering;
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
use crate::num::bignum::Big32x40 as Big;
|
||||
use crate::num::bignum::Digit32 as Digit;
|
||||
@ -97,7 +98,10 @@ fn div_rem_upto_16<'a>(
|
||||
}
|
||||
|
||||
/// The shortest mode implementation for Dragon.
|
||||
pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) {
|
||||
pub fn format_shortest<'a>(
|
||||
d: &Decoded,
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
) -> (/*digits*/ &'a [u8], /*exp*/ i16) {
|
||||
// the number `v` to format is known to be:
|
||||
// - equal to `mant * 2^exp`;
|
||||
// - preceded by `(mant - 2 * minus) * 2^exp` in the original type; and
|
||||
@ -186,7 +190,7 @@ pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp
|
||||
// generate one digit: `d[n] = floor(mant / scale) < 10`.
|
||||
let (d, _) = div_rem_upto_16(&mut mant, &scale, &scale2, &scale4, &scale8);
|
||||
debug_assert!(d < 10);
|
||||
buf[i] = b'0' + d;
|
||||
buf[i] = MaybeUninit::new(b'0' + d);
|
||||
i += 1;
|
||||
|
||||
// this is a simplified description of the modified Dragon algorithm.
|
||||
@ -241,18 +245,24 @@ pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp
|
||||
// if rounding up changes the length, the exponent should also change.
|
||||
// it seems that this condition is very hard to satisfy (possibly impossible),
|
||||
// but we are just being safe and consistent here.
|
||||
if let Some(c) = round_up(buf, i) {
|
||||
buf[i] = c;
|
||||
// SAFETY: we initialized that memory above.
|
||||
if let Some(c) = round_up(unsafe { MaybeUninit::slice_get_mut(&mut buf[..i]) }) {
|
||||
buf[i] = MaybeUninit::new(c);
|
||||
i += 1;
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
(i, k)
|
||||
// SAFETY: we initialized that memory above.
|
||||
(unsafe { MaybeUninit::slice_get_ref(&buf[..i]) }, k)
|
||||
}
|
||||
|
||||
/// The exact and fixed mode implementation for Dragon.
|
||||
pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) {
|
||||
pub fn format_exact<'a>(
|
||||
d: &Decoded,
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
limit: i16,
|
||||
) -> (/*digits*/ &'a [u8], /*exp*/ i16) {
|
||||
assert!(d.mant > 0);
|
||||
assert!(d.minus > 0);
|
||||
assert!(d.plus > 0);
|
||||
@ -319,9 +329,10 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi
|
||||
// following digits are all zeroes, we stop here
|
||||
// do *not* try to perform rounding! rather, fill remaining digits.
|
||||
for c in &mut buf[i..len] {
|
||||
*c = b'0';
|
||||
*c = MaybeUninit::new(b'0');
|
||||
}
|
||||
return (len, k);
|
||||
// SAFETY: we initialized that memory above.
|
||||
return (unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, k);
|
||||
}
|
||||
|
||||
let mut d = 0;
|
||||
@ -343,7 +354,7 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi
|
||||
}
|
||||
debug_assert!(mant < scale);
|
||||
debug_assert!(d < 10);
|
||||
buf[i] = b'0' + d;
|
||||
buf[i] = MaybeUninit::new(b'0' + d);
|
||||
mant.mul_small(10);
|
||||
}
|
||||
}
|
||||
@ -353,21 +364,25 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi
|
||||
// round to even (i.e., avoid rounding up when the prior digit is even).
|
||||
let order = mant.cmp(scale.mul_small(5));
|
||||
if order == Ordering::Greater
|
||||
|| (order == Ordering::Equal && (len == 0 || buf[len - 1] & 1 == 1))
|
||||
|| (order == Ordering::Equal
|
||||
// SAFETY: `buf[len-1]` is initialized.
|
||||
&& (len == 0 || unsafe { buf[len - 1].assume_init() } & 1 == 1))
|
||||
{
|
||||
// if rounding up changes the length, the exponent should also change.
|
||||
// but we've been requested a fixed number of digits, so do not alter the buffer...
|
||||
if let Some(c) = round_up(buf, len) {
|
||||
// SAFETY: we initialized that memory above.
|
||||
if let Some(c) = round_up(unsafe { MaybeUninit::slice_get_mut(&mut buf[..len]) }) {
|
||||
// ...unless we've been requested the fixed precision instead.
|
||||
// we also need to check that, if the original buffer was empty,
|
||||
// the additional digit can only be added when `k == limit` (edge case).
|
||||
k += 1;
|
||||
if k > limit && len < buf.len() {
|
||||
buf[len] = c;
|
||||
buf[len] = MaybeUninit::new(c);
|
||||
len += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(len, k)
|
||||
// SAFETY: we initialized that memory above.
|
||||
(unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, k)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
//! [^1]: Florian Loitsch. 2010. Printing floating-point numbers quickly and
|
||||
//! accurately with integers. SIGPLAN Not. 45, 6 (June 2010), 233-243.
|
||||
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::num::diy_float::Fp;
|
||||
use crate::num::flt2dec::{round_up, Decoded, MAX_SIG_DIGITS};
|
||||
|
||||
@ -161,10 +162,10 @@ pub fn max_pow10_no_more_than(x: u32) -> (u8, u32) {
|
||||
/// The shortest mode implementation for Grisu.
|
||||
///
|
||||
/// It returns `None` when it would return an inexact representation otherwise.
|
||||
pub fn format_shortest_opt(
|
||||
pub fn format_shortest_opt<'a>(
|
||||
d: &Decoded,
|
||||
buf: &mut [u8],
|
||||
) -> Option<(/*#digits*/ usize, /*exp*/ i16)> {
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
) -> Option<(/*digits*/ &'a [u8], /*exp*/ i16)> {
|
||||
assert!(d.mant > 0);
|
||||
assert!(d.minus > 0);
|
||||
assert!(d.plus > 0);
|
||||
@ -266,14 +267,23 @@ pub fn format_shortest_opt(
|
||||
let q = remainder / ten_kappa;
|
||||
let r = remainder % ten_kappa;
|
||||
debug_assert!(q < 10);
|
||||
buf[i] = b'0' + q as u8;
|
||||
buf[i] = MaybeUninit::new(b'0' + q as u8);
|
||||
i += 1;
|
||||
|
||||
let plus1rem = ((r as u64) << e) + plus1frac; // == (plus1 % 10^kappa) * 2^e
|
||||
if plus1rem < delta1 {
|
||||
// `plus1 % 10^kappa < delta1 = plus1 - minus1`; we've found the correct `kappa`.
|
||||
let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent
|
||||
return round_and_weed(&mut buf[..i], exp, plus1rem, delta1, plus1 - v.f, ten_kappa, 1);
|
||||
return round_and_weed(
|
||||
// SAFETY: we initialized that memory above.
|
||||
unsafe { MaybeUninit::slice_get_mut(&mut buf[..i]) },
|
||||
exp,
|
||||
plus1rem,
|
||||
delta1,
|
||||
plus1 - v.f,
|
||||
ten_kappa,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
// break the loop when we have rendered all integral digits.
|
||||
@ -310,13 +320,14 @@ pub fn format_shortest_opt(
|
||||
let q = remainder >> e;
|
||||
let r = remainder & ((1 << e) - 1);
|
||||
debug_assert!(q < 10);
|
||||
buf[i] = b'0' + q as u8;
|
||||
buf[i] = MaybeUninit::new(b'0' + q as u8);
|
||||
i += 1;
|
||||
|
||||
if r < threshold {
|
||||
let ten_kappa = 1 << e; // implicit divisor
|
||||
return round_and_weed(
|
||||
&mut buf[..i],
|
||||
// SAFETY: we initialized that memory above.
|
||||
unsafe { MaybeUninit::slice_get_mut(&mut buf[..i]) },
|
||||
exp,
|
||||
r,
|
||||
threshold,
|
||||
@ -355,7 +366,7 @@ pub fn format_shortest_opt(
|
||||
plus1v: u64,
|
||||
ten_kappa: u64,
|
||||
ulp: u64,
|
||||
) -> Option<(usize, i16)> {
|
||||
) -> Option<(&[u8], i16)> {
|
||||
assert!(!buf.is_empty());
|
||||
|
||||
// produce two approximations to `v` (actually `plus1 - v`) within 1.5 ulps.
|
||||
@ -437,20 +448,22 @@ pub fn format_shortest_opt(
|
||||
// this is too liberal, though, so we reject any `w(n)` not between `plus0` and `minus0`,
|
||||
// i.e., `plus1 - plus1w(n) <= minus0` or `plus1 - plus1w(n) >= plus0`. we utilize the facts
|
||||
// that `threshold = plus1 - minus1` and `plus1 - plus0 = minus0 - minus1 = 2 ulp`.
|
||||
if 2 * ulp <= plus1w && plus1w <= threshold - 4 * ulp {
|
||||
Some((buf.len(), exp))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if 2 * ulp <= plus1w && plus1w <= threshold - 4 * ulp { Some((buf, exp)) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
/// The shortest mode implementation for Grisu with Dragon fallback.
|
||||
///
|
||||
/// This should be used for most cases.
|
||||
pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) {
|
||||
pub fn format_shortest<'a>(
|
||||
d: &Decoded,
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
) -> (/*digits*/ &'a [u8], /*exp*/ i16) {
|
||||
use crate::num::flt2dec::strategy::dragon::format_shortest as fallback;
|
||||
match format_shortest_opt(d, buf) {
|
||||
// SAFETY: The borrow checker is not smart enough to let us use `buf`
|
||||
// in the second branch, so we launder the lifetime here. But we only re-use
|
||||
// `buf` if `format_shortest_opt` returned `None` so this is okay.
|
||||
match format_shortest_opt(d, unsafe { &mut *(buf as *mut _) }) {
|
||||
Some(ret) => ret,
|
||||
None => fallback(d, buf),
|
||||
}
|
||||
@ -459,11 +472,11 @@ pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp
|
||||
/// The exact and fixed mode implementation for Grisu.
|
||||
///
|
||||
/// It returns `None` when it would return an inexact representation otherwise.
|
||||
pub fn format_exact_opt(
|
||||
pub fn format_exact_opt<'a>(
|
||||
d: &Decoded,
|
||||
buf: &mut [u8],
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
limit: i16,
|
||||
) -> Option<(/*#digits*/ usize, /*exp*/ i16)> {
|
||||
) -> Option<(/*digits*/ &'a [u8], /*exp*/ i16)> {
|
||||
assert!(d.mant > 0);
|
||||
assert!(d.mant < (1 << 61)); // we need at least three bits of additional precision
|
||||
assert!(!buf.is_empty());
|
||||
@ -510,7 +523,11 @@ pub fn format_exact_opt(
|
||||
// thus we are being sloppy here and widen the error range by a factor of 10.
|
||||
// this will increase the false negative rate, but only very, *very* slightly;
|
||||
// it can only matter noticeably when the mantissa is bigger than 60 bits.
|
||||
return possibly_round(buf, 0, exp, limit, v.f / 10, (max_ten_kappa as u64) << e, err << e);
|
||||
//
|
||||
// SAFETY: `len=0`, so the obligation of having initialized this memory is trivial.
|
||||
return unsafe {
|
||||
possibly_round(buf, 0, exp, limit, v.f / 10, (max_ten_kappa as u64) << e, err << e)
|
||||
};
|
||||
} else if ((exp as i32 - limit as i32) as usize) < buf.len() {
|
||||
(exp - limit) as usize
|
||||
} else {
|
||||
@ -534,13 +551,16 @@ pub fn format_exact_opt(
|
||||
let q = remainder / ten_kappa;
|
||||
let r = remainder % ten_kappa;
|
||||
debug_assert!(q < 10);
|
||||
buf[i] = b'0' + q as u8;
|
||||
buf[i] = MaybeUninit::new(b'0' + q as u8);
|
||||
i += 1;
|
||||
|
||||
// is the buffer full? run the rounding pass with the remainder.
|
||||
if i == len {
|
||||
let vrem = ((r as u64) << e) + vfrac; // == (v % 10^kappa) * 2^e
|
||||
return possibly_round(buf, len, exp, limit, vrem, (ten_kappa as u64) << e, err << e);
|
||||
// SAFETY: we have initialized `len` many bytes.
|
||||
return unsafe {
|
||||
possibly_round(buf, len, exp, limit, vrem, (ten_kappa as u64) << e, err << e)
|
||||
};
|
||||
}
|
||||
|
||||
// break the loop when we have rendered all integral digits.
|
||||
@ -585,12 +605,13 @@ pub fn format_exact_opt(
|
||||
let q = remainder >> e;
|
||||
let r = remainder & ((1 << e) - 1);
|
||||
debug_assert!(q < 10);
|
||||
buf[i] = b'0' + q as u8;
|
||||
buf[i] = MaybeUninit::new(b'0' + q as u8);
|
||||
i += 1;
|
||||
|
||||
// is the buffer full? run the rounding pass with the remainder.
|
||||
if i == len {
|
||||
return possibly_round(buf, len, exp, limit, r, 1 << e, err);
|
||||
// SAFETY: we have initialized `len` many bytes.
|
||||
return unsafe { possibly_round(buf, len, exp, limit, r, 1 << e, err) };
|
||||
}
|
||||
|
||||
// restore invariants
|
||||
@ -610,15 +631,17 @@ pub fn format_exact_opt(
|
||||
// - `remainder = (v % 10^kappa) * k`
|
||||
// - `ten_kappa = 10^kappa * k`
|
||||
// - `ulp = 2^-e * k`
|
||||
fn possibly_round(
|
||||
buf: &mut [u8],
|
||||
//
|
||||
// SAFETY: the first `len` bytes of `buf` must be initialized.
|
||||
unsafe fn possibly_round(
|
||||
buf: &mut [MaybeUninit<u8>],
|
||||
mut len: usize,
|
||||
mut exp: i16,
|
||||
limit: i16,
|
||||
remainder: u64,
|
||||
ten_kappa: u64,
|
||||
ulp: u64,
|
||||
) -> Option<(usize, i16)> {
|
||||
) -> Option<(&[u8], i16)> {
|
||||
debug_assert!(remainder < ten_kappa);
|
||||
|
||||
// 10^kappa
|
||||
@ -677,7 +700,8 @@ pub fn format_exact_opt(
|
||||
// we've already verified that `ulp < 10^kappa / 2`, so as long as
|
||||
// `10^kappa` did not overflow after all, the second check is fine.
|
||||
if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp {
|
||||
return Some((len, exp));
|
||||
// SAFETY: our caller initialized that memory.
|
||||
return Some((unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, exp));
|
||||
}
|
||||
|
||||
// :<------- remainder ------>| :
|
||||
@ -698,17 +722,19 @@ pub fn format_exact_opt(
|
||||
// as `10^kappa` is never zero). also note that `remainder - ulp <= 10^kappa`,
|
||||
// so the second check does not overflow.
|
||||
if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp {
|
||||
if let Some(c) = round_up(buf, len) {
|
||||
// SAFETY: our caller must have initialized that memory.
|
||||
if let Some(c) = round_up(unsafe { MaybeUninit::slice_get_mut(&mut buf[..len]) }) {
|
||||
// only add an additional digit when we've been requested the fixed precision.
|
||||
// we also need to check that, if the original buffer was empty,
|
||||
// the additional digit can only be added when `exp == limit` (edge case).
|
||||
exp += 1;
|
||||
if exp > limit && len < buf.len() {
|
||||
buf[len] = c;
|
||||
buf[len] = MaybeUninit::new(c);
|
||||
len += 1;
|
||||
}
|
||||
}
|
||||
return Some((len, exp));
|
||||
// SAFETY: we and our caller initialized that memory.
|
||||
return Some((unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, exp));
|
||||
}
|
||||
|
||||
// otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are
|
||||
@ -720,9 +746,16 @@ pub fn format_exact_opt(
|
||||
/// The exact and fixed mode implementation for Grisu with Dragon fallback.
|
||||
///
|
||||
/// This should be used for most cases.
|
||||
pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) {
|
||||
pub fn format_exact<'a>(
|
||||
d: &Decoded,
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
limit: i16,
|
||||
) -> (/*digits*/ &'a [u8], /*exp*/ i16) {
|
||||
use crate::num::flt2dec::strategy::dragon::format_exact as fallback;
|
||||
match format_exact_opt(d, buf, limit) {
|
||||
// SAFETY: The borrow checker is not smart enough to let us use `buf`
|
||||
// in the second branch, so we launder the lifetime here. But we only re-use
|
||||
// `buf` if `format_exact_opt` returned `None` so this is okay.
|
||||
match format_exact_opt(d, unsafe { &mut *(buf as *mut _) }, limit) {
|
||||
Some(ret) => ret,
|
||||
None => fallback(d, buf, limit),
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::mem::MaybeUninit;
|
||||
use std::{fmt, str};
|
||||
|
||||
use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded};
|
||||
@ -36,20 +37,20 @@ macro_rules! check_shortest {
|
||||
);
|
||||
|
||||
($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({
|
||||
let mut buf = [b'_'; MAX_SIG_DIGITS];
|
||||
let (len, k) = $f(&decode_finite($v), &mut buf);
|
||||
assert!((&buf[..len], k) == ($buf, $exp),
|
||||
$fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k),
|
||||
let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS];
|
||||
let (buf, k) = $f(&decode_finite($v), &mut buf);
|
||||
assert!((buf, k) == ($buf, $exp),
|
||||
$fmt, actual = (str::from_utf8(buf).unwrap(), k),
|
||||
expected = (str::from_utf8($buf).unwrap(), $exp),
|
||||
$($key = $val),*);
|
||||
});
|
||||
|
||||
($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr;
|
||||
$fmt:expr, $($key:ident = $val:expr),*) => ({
|
||||
let mut buf = [b'_'; MAX_SIG_DIGITS];
|
||||
let (len, k) = $f(&Decoded { $($k: $v),+ }, &mut buf);
|
||||
assert!((&buf[..len], k) == ($buf, $exp),
|
||||
$fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k),
|
||||
let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS];
|
||||
let (buf, k) = $f(&Decoded { $($k: $v),+ }, &mut buf);
|
||||
assert!((buf, k) == ($buf, $exp),
|
||||
$fmt, actual = (str::from_utf8(buf).unwrap(), k),
|
||||
expected = (str::from_utf8($buf).unwrap(), $exp),
|
||||
$($key = $val),*);
|
||||
})
|
||||
@ -58,9 +59,9 @@ macro_rules! check_shortest {
|
||||
macro_rules! try_exact {
|
||||
($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr;
|
||||
$fmt:expr, $($key:ident = $val:expr),*) => ({
|
||||
let (len, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN);
|
||||
assert!((&$buf[..len], k) == ($expected, $expectedk),
|
||||
$fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k),
|
||||
let (buf, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN);
|
||||
assert!((buf, k) == ($expected, $expectedk),
|
||||
$fmt, actual = (str::from_utf8(buf).unwrap(), k),
|
||||
expected = (str::from_utf8($expected).unwrap(), $expectedk),
|
||||
$($key = $val),*);
|
||||
})
|
||||
@ -69,9 +70,9 @@ macro_rules! try_exact {
|
||||
macro_rules! try_fixed {
|
||||
($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr;
|
||||
$fmt:expr, $($key:ident = $val:expr),*) => ({
|
||||
let (len, k) = $f($decoded, &mut $buf[..], $request);
|
||||
assert!((&$buf[..len], k) == ($expected, $expectedk),
|
||||
$fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k),
|
||||
let (buf, k) = $f($decoded, &mut $buf[..], $request);
|
||||
assert!((buf, k) == ($expected, $expectedk),
|
||||
$fmt, actual = (str::from_utf8(buf).unwrap(), k),
|
||||
expected = (str::from_utf8($expected).unwrap(), $expectedk),
|
||||
$($key = $val),*);
|
||||
})
|
||||
@ -93,10 +94,10 @@ fn ldexp_f64(a: f64, b: i32) -> f64 {
|
||||
fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16)
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
// use a large enough buffer
|
||||
let mut buf = [b'_'; 1024];
|
||||
let mut buf = [MaybeUninit::new(b'_'); 1024];
|
||||
let mut expected_ = [b'_'; 1024];
|
||||
|
||||
let decoded = decode_finite(v);
|
||||
@ -118,7 +119,7 @@ where
|
||||
// we should always return `100..00` (`i` digits) instead, since that's
|
||||
// what we can came up with `i` digits anyway. `round_up` assumes that
|
||||
// the adjustment to the length is done by caller, which we simply ignore.
|
||||
if let Some(_) = round_up(&mut expected_, i) {
|
||||
if let Some(_) = round_up(&mut expected_[..i]) {
|
||||
expectedk_ += 1;
|
||||
}
|
||||
}
|
||||
@ -193,10 +194,10 @@ impl TestableFloat for f64 {
|
||||
fn check_exact_one<F, T>(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16)
|
||||
where
|
||||
T: TestableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
// use a large enough buffer
|
||||
let mut buf = [b'_'; 1024];
|
||||
let mut buf = [MaybeUninit::new(b'_'); 1024];
|
||||
let v: T = TestableFloat::ldexpi(x, e);
|
||||
let decoded = decode_finite(v);
|
||||
|
||||
@ -230,7 +231,7 @@ macro_rules! check_exact_one {
|
||||
|
||||
pub fn f32_shortest_sanity_test<F>(mut f: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
// 0.0999999940395355224609375
|
||||
// 0.100000001490116119384765625
|
||||
@ -277,7 +278,7 @@ where
|
||||
|
||||
pub fn f32_exact_sanity_test<F>(mut f: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
let minf32 = ldexp_f32(1.0, -149);
|
||||
|
||||
@ -321,7 +322,7 @@ where
|
||||
|
||||
pub fn f64_shortest_sanity_test<F>(mut f: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
// 0.0999999999999999777955395074968691915273...
|
||||
// 0.1000000000000000055511151231257827021181...
|
||||
@ -387,7 +388,7 @@ where
|
||||
|
||||
pub fn f64_exact_sanity_test<F>(mut f: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
let minf64 = ldexp_f64(1.0, -1074);
|
||||
|
||||
@ -474,7 +475,7 @@ where
|
||||
|
||||
pub fn more_shortest_sanity_test<F>(mut f: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1,
|
||||
exp: 0, inclusive: true} => b"1", 18);
|
||||
@ -484,10 +485,10 @@ where
|
||||
|
||||
fn to_string_with_parts<F>(mut f: F) -> String
|
||||
where
|
||||
F: for<'a> FnMut(&'a mut [u8], &'a mut [Part<'a>]) -> Formatted<'a>,
|
||||
F: for<'a> FnMut(&'a mut [MaybeUninit<u8>], &'a mut [MaybeUninit<Part<'a>>]) -> Formatted<'a>,
|
||||
{
|
||||
let mut buf = [0; 1024];
|
||||
let mut parts = [Part::Zero(0); 16];
|
||||
let mut buf = [MaybeUninit::new(0); 1024];
|
||||
let mut parts = [MaybeUninit::new(Part::Zero(0)); 16];
|
||||
let formatted = f(&mut buf, &mut parts);
|
||||
let mut ret = vec![0; formatted.len()];
|
||||
assert_eq!(formatted.write(&mut ret), Some(ret.len()));
|
||||
@ -496,14 +497,14 @@ where
|
||||
|
||||
pub fn to_shortest_str_test<F>(mut f_: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
use core::num::flt2dec::Sign::*;
|
||||
|
||||
fn to_string<T, F>(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
to_string_with_parts(|buf, parts| {
|
||||
to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts)
|
||||
@ -597,14 +598,14 @@ where
|
||||
|
||||
pub fn to_shortest_exp_str_test<F>(mut f_: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
use core::num::flt2dec::Sign::*;
|
||||
|
||||
fn to_string<T, F>(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
to_string_with_parts(|buf, parts| {
|
||||
to_shortest_exp_str(|d, b| f(d, b), v, sign, exp_bounds, upper, buf, parts)
|
||||
@ -716,14 +717,14 @@ where
|
||||
|
||||
pub fn to_exact_exp_str_test<F>(mut f_: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
use core::num::flt2dec::Sign::*;
|
||||
|
||||
fn to_string<T, F>(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
to_string_with_parts(|buf, parts| {
|
||||
to_exact_exp_str(|d, b, l| f(d, b, l), v, sign, ndigits, upper, buf, parts)
|
||||
@ -989,14 +990,14 @@ where
|
||||
|
||||
pub fn to_exact_fixed_str_test<F>(mut f_: F)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
use core::num::flt2dec::Sign::*;
|
||||
|
||||
fn to_string<T, F>(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String
|
||||
where
|
||||
T: DecodableFloat,
|
||||
F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
|
||||
{
|
||||
to_string_with_parts(|buf, parts| {
|
||||
to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts)
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![cfg(not(target_arch = "wasm32"))]
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
use std::str;
|
||||
|
||||
use core::num::flt2dec::strategy::grisu::format_exact_opt;
|
||||
@ -20,8 +21,8 @@ pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
|
||||
|
||||
fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
||||
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
|
||||
G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
V: FnMut(usize) -> Decoded,
|
||||
{
|
||||
assert!(k <= 1024);
|
||||
@ -42,11 +43,11 @@ where
|
||||
}
|
||||
|
||||
let decoded = v(i);
|
||||
let mut buf1 = [0; 1024];
|
||||
if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) {
|
||||
let mut buf2 = [0; 1024];
|
||||
let (len2, e2) = g(&decoded, &mut buf2[..k]);
|
||||
if e1 == e2 && &buf1[..len1] == &buf2[..len2] {
|
||||
let mut buf1 = [MaybeUninit::new(0); 1024];
|
||||
if let Some((buf1, e1)) = f(&decoded, &mut buf1[..k]) {
|
||||
let mut buf2 = [MaybeUninit::new(0); 1024];
|
||||
let (buf2, e2) = g(&decoded, &mut buf2[..k]);
|
||||
if e1 == e2 && buf1 == buf2 {
|
||||
npassed += 1;
|
||||
} else {
|
||||
println!(
|
||||
@ -54,9 +55,9 @@ where
|
||||
i,
|
||||
n,
|
||||
decoded,
|
||||
str::from_utf8(&buf1[..len1]).unwrap(),
|
||||
str::from_utf8(buf1).unwrap(),
|
||||
e1,
|
||||
str::from_utf8(&buf2[..len2]).unwrap(),
|
||||
str::from_utf8(buf2).unwrap(),
|
||||
e2
|
||||
);
|
||||
}
|
||||
@ -85,8 +86,8 @@ where
|
||||
|
||||
pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
||||
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
|
||||
G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
if cfg!(target_os = "emscripten") {
|
||||
return; // using rng pulls in i128 support, which doesn't work
|
||||
@ -101,8 +102,8 @@ where
|
||||
|
||||
pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
||||
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
|
||||
G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
if cfg!(target_os = "emscripten") {
|
||||
return; // using rng pulls in i128 support, which doesn't work
|
||||
@ -117,8 +118,8 @@ where
|
||||
|
||||
pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
|
||||
where
|
||||
F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
||||
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
|
||||
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
|
||||
G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
|
||||
{
|
||||
// we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
|
||||
// so why not simply testing all of them?
|
||||
|
Loading…
Reference in New Issue
Block a user