#![allow(
clippy::many_single_char_names,
clippy::unreadable_literal,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::cast_lossless,
clippy::cast_precision_loss,
clippy::similar_names,
clippy::module_name_repetitions,
clippy::missing_const_for_fn,
clippy::doc_markdown,
clippy::redundant_pub_crate
)]
extern crate alloc;
use alloc::string::String;
use core::fmt;
const MANTISSA_WIDTH: u32 = 53;
const EXPONENT_MIN: i32 = -1074;
const HIDDEN_BIT: u64 = 1u64 << (MANTISSA_WIDTH - 1);
const SIG_MASK: u64 = HIDDEN_BIT - 1;
const EXP_SHIFT: u32 = MANTISSA_WIDTH - 1;
struct BinaryF64 {
exponent: i32,
mantissa: u64,
}
#[inline]
fn decompose(value: f64) -> BinaryF64 {
debug_assert!(value.is_finite() && value > 0.0);
let bits = value.to_bits();
let mut mantissa = bits & SIG_MASK;
let biased_exp = (bits >> EXP_SHIFT) as i32;
let mut exponent;
if biased_exp != 0 {
exponent = biased_exp - 1;
mantissa |= HIDDEN_BIT;
} else {
exponent = 0;
}
exponent += EXPONENT_MIN;
BinaryF64 { exponent, mantissa }
}
#[inline]
fn log10_pow2(e: i32) -> i32 {
((1_292_913_987i64 * i64::from(e)) >> 32) as i32
}
#[inline]
fn log10_pow2_residual(e: i32) -> u32 {
let product = 1_292_913_987i64 * i64::from(e);
(product as u32) / 1_292_913_987u32
}
#[inline]
fn mshift(m: u64, upper: u64, lower: u64) -> u64 {
let hi = u128::from(m) * u128::from(upper);
let lo = u128::from(m) * u128::from(lower);
((hi + (lo >> 64)) >> 64) as u64
}
#[inline]
fn mshift_pow2(k: u32, upper: u64, lower: u64) -> u64 {
let s = k as i32 - 64;
if s <= 0 {
upper >> (-s) as u32
} else {
(upper << s as u32) | (lower >> (64 - s as u32))
}
}
#[inline]
fn is_multiple_of_pow2(e: i32, n: u64) -> bool {
debug_assert!(e >= 0 && (e as u32) < 64);
(n >> e as u32) << e as u32 == n
}
#[inline]
fn can_test_pow5(f: i32) -> bool {
f >= 0 && (f as usize) < MINVERSE.len()
}
#[inline]
fn is_multiple_of_pow5(f: i32, n: u64) -> bool {
debug_assert!(can_test_pow5(f));
let (inv, bound) = MINVERSE[f as usize];
n.wrapping_mul(inv) <= bound
}
#[inline]
fn div10(n: u64) -> u64 {
n / 10
}
struct DecimalF64 {
exponent: i32,
mantissa: u64,
}
#[inline]
fn remove_trailing_zeros(mut m: u64, mut e: i32) -> DecimalF64 {
if m == 0 {
return DecimalF64 {
exponent: e,
mantissa: 0,
};
}
let minv5: u64 = 0u64.wrapping_sub(u64::MAX / 5);
let bound: u64 = u64::MAX / 10 + 1;
loop {
let q = m.wrapping_mul(minv5).rotate_right(1);
if q >= bound {
return DecimalF64 {
exponent: e,
mantissa: m,
};
}
e += 1;
m = q;
}
}
const MANTISSA_UNCENTRED: u64 = 1u64 << (MANTISSA_WIDTH - 1);
#[inline]
fn is_small_integer(e: i32, m: u64) -> bool {
let neg_e = -e;
neg_e >= 0 && (neg_e as u32) < MANTISSA_WIDTH && is_multiple_of_pow2(neg_e, m)
}
#[inline]
fn is_centred(e: i32, m: u64) -> bool {
m != MANTISSA_UNCENTRED || e == EXPONENT_MIN
}
fn to_decimal_small_integer(e: i32, m: u64) -> DecimalF64 {
debug_assert!(is_small_integer(e, m));
remove_trailing_zeros(m >> (-e) as u32, 0)
}
#[inline]
fn is_multiple_of_pow5_safe(f: i32, n: u64) -> bool {
let in_range = f >= 0 && (f as usize) < MINVERSE.len();
let idx = if in_range { f as usize } else { 0 };
let (inv, bound) = MINVERSE[idx];
let multiple = n.wrapping_mul(inv) <= bound;
in_range & multiple
}
#[inline]
fn is_tie_neg_f_safe(f: i32, c_2: u64) -> bool {
is_multiple_of_pow5_safe(-f, c_2)
}
#[inline]
fn to_decimal_centred(e: i32, m: u64) -> DecimalF64 {
debug_assert!(is_centred(e, m));
let f = log10_pow2(e);
let r = log10_pow2_residual(e);
let idx = (f - STORAGE_INDEX_OFFSET) as usize;
let (lower, upper) = MULTIPLIERS[idx];
let m_b = (2 * m + 1) << r;
let m_a = (2 * m - 1) << r;
let b = mshift(m_b, upper, lower);
let a = mshift(m_a, upper, lower);
let q = div10(b);
let s = 10 * q;
let m_even = m & 1 == 0;
let m_b_mult_pow5 = is_multiple_of_pow5_safe(f, m_b);
let m_a_mult_pow5 = is_multiple_of_pow5_safe(f, m_a);
let allows_ties = can_test_pow5(f);
let cond_s_eq_b = !m_b_mult_pow5 || m_even;
let cond_s_eq_a = m_a_mult_pow5 && m_even;
let cond_else = s > a;
let shortest_pow5 = if s == b {
cond_s_eq_b
} else if s == a {
cond_s_eq_a
} else {
cond_else
};
let shortest = if allows_ties { shortest_pow5 } else { s > a };
let shortest_result = remove_trailing_zeros(q, f + 1);
let m_c = (4 * m) << r;
let c_2 = mshift(m_c, upper, lower);
let c = c_2 / 2;
let pick_left = (is_tie_neg_f_safe(f, c_2) && c & 1 == 0) || c_2 & 1 == 0;
let fallback_result = DecimalF64 {
exponent: f,
mantissa: c + u64::from(!pick_left),
};
if shortest {
shortest_result
} else {
fallback_result
}
}
struct UncentredCtx {
m: u64,
f: i32,
r: u32,
upper: u64,
lower: u64,
a: u64,
b: u64,
m_a: u64,
m_b: u64,
}
fn to_decimal_uncentred(e: i32) -> DecimalF64 {
let m = MANTISSA_UNCENTRED;
let f = log10_pow2(e);
let r = log10_pow2_residual(e);
let idx = (f - STORAGE_INDEX_OFFSET) as usize;
let (lower, upper) = MULTIPLIERS[idx];
let m_a = (4 * m - 1) << r;
let m_b = (2 * m + 1) << r;
let b = mshift(m_b, upper, lower);
let a = mshift(m_a, upper, lower) / 2;
let ctx = UncentredCtx {
m,
f,
r,
upper,
lower,
a,
b,
m_a,
m_b,
};
if a < b {
uncentred_a_less_than_b(&ctx)
} else {
uncentred_a_ge_b(&ctx)
}
}
#[inline]
fn uncentred_a_less_than_b(ctx: &UncentredCtx) -> DecimalF64 {
let UncentredCtx {
f,
r,
upper,
lower,
a,
b,
m_a,
..
} = *ctx;
let q = div10(b);
let s = 10 * q;
if uncentred_is_shortest(ctx, s) {
return remove_trailing_zeros(q, f + 1);
}
let log2_m_c = MANTISSA_WIDTH + r + 1;
let c_2 = mshift_pow2(log2_m_c, upper, lower);
let c = c_2 / 2;
if c == a && !is_tie_uncentred(f, m_a) {
return DecimalF64 {
exponent: f,
mantissa: c + 1,
};
}
let pick_left = (is_tie_neg_f(f, c_2) && c % 2 == 0) || c_2 % 2 == 0;
DecimalF64 {
exponent: f,
mantissa: c + u64::from(!pick_left),
}
}
#[inline]
fn uncentred_is_shortest(ctx: &UncentredCtx, s: u64) -> bool {
let UncentredCtx {
m,
f,
a,
b,
m_a,
m_b,
..
} = *ctx;
if !can_test_pow5(f) {
return s > a;
}
if s == b {
return !is_tie_uncentred(f, m_b) || m % 2 == 0;
}
if s == a {
return is_tie_uncentred(f, m_a) && m % 2 == 0;
}
s > a
}
#[inline]
fn uncentred_a_ge_b(ctx: &UncentredCtx) -> DecimalF64 {
let UncentredCtx {
m,
f,
r,
upper,
lower,
a,
m_a,
..
} = *ctx;
if is_tie_uncentred(f, m_a) && m % 2 == 0 {
return remove_trailing_zeros(a, f);
}
let m_c = (40 * m) << r;
let c_2 = mshift(m_c, upper, lower);
let c = c_2 / 2;
let pick_left = (is_tie_neg_f(f, c_2) && c % 2 == 0) || c_2 % 2 == 0;
DecimalF64 {
exponent: f - 1,
mantissa: c + u64::from(!pick_left),
}
}
#[inline]
fn is_tie_neg_f(f: i32, c_2: u64) -> bool {
can_test_pow5(-f) && is_multiple_of_pow5(-f, c_2)
}
#[inline]
fn is_tie_uncentred(f: i32, m: u64) -> bool {
m % 5 == 0 && can_test_pow5(f) && is_multiple_of_pow5(f, m)
}
#[inline]
fn teju(value: f64) -> DecimalF64 {
let b = decompose(value);
let e = b.exponent;
let m = b.mantissa;
if is_small_integer(e, m) {
return to_decimal_small_integer(e, m);
}
if is_centred(e, m) {
return to_decimal_centred(e, m);
}
to_decimal_uncentred(e)
}
pub(crate) const FORMAT_BUF_LEN: usize = 32;
const DIGIT_LUT: &[u8; 200] = b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
static QUAD_LUT: [u8; 40_000] = {
let mut buf = [0u8; 40_000];
let mut n: u32 = 0;
while n < 10_000 {
let off = (n * 4) as usize;
buf[off] = b'0' + (n / 1000) as u8;
buf[off + 1] = b'0' + ((n / 100) % 10) as u8;
buf[off + 2] = b'0' + ((n / 10) % 10) as u8;
buf[off + 3] = b'0' + (n % 10) as u8;
n += 1;
}
buf
};
const DIGIT_THRESHOLDS: [u64; 16] = [
10_000_000_000_000_000, 1_000_000_000_000_000, 100_000_000_000_000, 10_000_000_000_000, 1_000_000_000_000, 100_000_000_000, 10_000_000_000, 1_000_000_000, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, ];
#[inline]
fn mantissa_digit_count(n: u64) -> usize {
debug_assert!(n < 100_000_000_000_000_000); let mut i = 0;
while i < DIGIT_THRESHOLDS.len() {
if n >= DIGIT_THRESHOLDS[i] {
return 17 - i;
}
i += 1;
}
1
}
#[inline]
pub(crate) fn format_finite_to_buf(value: f64, buf: &mut [u8; FORMAT_BUF_LEN]) -> usize {
#[allow(unsafe_code)]
unsafe {
format_finite_to_ptr(value, buf.as_mut_ptr())
}
}
#[allow(unsafe_code)]
pub(crate) unsafe fn format_finite_to_ptr(value: f64, dst: *mut u8) -> usize {
if value == 0.0 {
let (src, len) = if value.is_sign_negative() {
(b"-0.0".as_ptr(), 4)
} else {
(b"0.0".as_ptr(), 3)
};
unsafe { core::ptr::copy_nonoverlapping(src, dst, len) };
return len;
}
let negative = value.is_sign_negative();
let d = teju(value.abs());
let digits_count = mantissa_digit_count(d.mantissa);
unsafe { dst.write(b'-') };
let pos = usize::from(negative);
let point = digits_count as i32 + d.exponent;
if (-6..=21).contains(&point) {
if point <= 0 {
let zeros = (-point) as usize;
unsafe {
dst.add(pos).write(b'0');
dst.add(pos + 1).write(b'.');
ptr_fill(dst.add(pos + 2), b'0', zeros);
write_digits_at_ptr(d.mantissa, dst.add(pos + 2 + zeros), digits_count);
}
pos + 2 + zeros + digits_count
} else if (point as usize) >= digits_count {
let trail_zeros = point as usize - digits_count;
unsafe {
write_digits_at_ptr(d.mantissa, dst.add(pos), digits_count);
ptr_fill(dst.add(pos + digits_count), b'0', trail_zeros);
let tail = dst.add(pos + digits_count + trail_zeros);
tail.write(b'.');
tail.add(1).write(b'0');
}
pos + digits_count + trail_zeros + 2
} else {
let p = point as usize;
unsafe {
write_digits_at_ptr(d.mantissa, dst.add(pos + 1), digits_count);
core::ptr::copy(dst.add(pos + 1), dst.add(pos), p);
dst.add(pos + p).write(b'.');
}
pos + digits_count + 1
}
} else {
unsafe {
write_digits_at_ptr(d.mantissa, dst.add(pos + 1), digits_count);
let lead = dst.add(pos + 1).read();
dst.add(pos).write(lead);
let after_mantissa = if digits_count > 1 {
dst.add(pos + 1).write(b'.');
pos + 1 + digits_count
} else {
pos + 1
};
dst.add(after_mantissa).write(b'e');
let exp = point - 1;
let exp_len = write_exponent_ptr(dst.add(after_mantissa + 1), exp);
after_mantissa + 1 + exp_len
}
}
}
#[inline]
#[allow(unsafe_code)]
unsafe fn ptr_fill(dst: *mut u8, byte: u8, n: usize) {
unsafe { core::ptr::write_bytes(dst, byte, n) };
}
#[inline]
#[allow(unsafe_code, clippy::cast_possible_truncation)]
unsafe fn write_digits_at_ptr(n: u64, dst: *mut u8, digits: usize) {
debug_assert_eq!(mantissa_digit_count(n), digits);
let mut pos = digits;
let lut2 = DIGIT_LUT.as_ptr();
let lut4 = QUAD_LUT.as_ptr();
let mut output32 = if n >> 32 == 0 {
n as u32
} else {
let low = (n - 100_000_000 * (n / 100_000_000)) as u32;
let upper = (n / 100_000_000) as u32;
let lo4 = (low % 10_000) as usize;
let hi4 = (low / 10_000) as usize;
unsafe {
pos -= 4;
core::ptr::copy_nonoverlapping(lut4.add(lo4 * 4), dst.add(pos), 4);
pos -= 4;
core::ptr::copy_nonoverlapping(lut4.add(hi4 * 4), dst.add(pos), 4);
}
upper
};
while output32 >= 10_000 {
let c = (output32 - 10_000 * (output32 / 10_000)) as usize;
output32 /= 10_000;
unsafe {
pos -= 4;
core::ptr::copy_nonoverlapping(lut4.add(c * 4), dst.add(pos), 4);
}
}
if output32 >= 100 {
let c = ((output32 % 100) * 2) as usize;
output32 /= 100;
unsafe {
pos -= 2;
core::ptr::copy_nonoverlapping(lut2.add(c), dst.add(pos), 2);
}
}
if output32 >= 10 {
let c = (output32 * 2) as usize;
unsafe {
pos -= 2;
core::ptr::copy_nonoverlapping(lut2.add(c), dst.add(pos), 2);
}
} else {
unsafe {
pos -= 1;
dst.add(pos).write(b'0' + output32 as u8);
}
}
debug_assert_eq!(pos, 0);
}
#[inline]
#[allow(unsafe_code, clippy::cast_possible_truncation, clippy::cast_sign_loss)]
unsafe fn write_exponent_ptr(dst: *mut u8, exp: i32) -> usize {
let (mag, sign_bytes) = if exp < 0 {
unsafe { dst.write(b'-') };
((-exp) as u32, 1)
} else {
(exp as u32, 0)
};
let lut = DIGIT_LUT.as_ptr();
unsafe {
if mag >= 100 {
let hundreds = mag / 100;
let rem = ((mag % 100) * 2) as usize;
dst.add(sign_bytes).write(b'0' + hundreds as u8);
core::ptr::copy_nonoverlapping(lut.add(rem), dst.add(sign_bytes + 1), 2);
sign_bytes + 3
} else if mag >= 10 {
let r = (mag * 2) as usize;
core::ptr::copy_nonoverlapping(lut.add(r), dst.add(sign_bytes), 2);
sign_bytes + 2
} else {
dst.add(sign_bytes).write(b'0' + mag as u8);
sign_bytes + 1
}
}
}
pub(crate) fn format_finite(f: f64, out: &mut String) {
let mut buf = [0u8; FORMAT_BUF_LEN];
let len = format_finite_to_buf(f, &mut buf);
let s = core::str::from_utf8(&buf[..len]).expect("teju emits ASCII");
out.push_str(s);
}
pub(crate) fn format_finite_fmt<W: fmt::Write + ?Sized>(f: f64, out: &mut W) -> fmt::Result {
let mut buf = [0u8; FORMAT_BUF_LEN];
let len = format_finite_to_buf(f, &mut buf);
let s = core::str::from_utf8(&buf[..len]).expect("teju emits ASCII");
out.write_str(s)
}
#[cfg(feature = "alloc")]
#[allow(unsafe_code)]
#[inline]
pub(crate) fn format_finite_to_vec(f: f64, out: &mut alloc::vec::Vec<u8>) -> bool {
const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
if f.to_bits() & EXP_MASK == EXP_MASK {
return false;
}
let mut buf = [0u8; FORMAT_BUF_LEN];
let len = unsafe { format_finite_to_ptr(f, buf.as_mut_ptr()) };
out.extend_from_slice(&buf[..len]);
true
}
#[cfg(feature = "alloc")]
#[allow(unsafe_code)]
#[inline]
pub(crate) unsafe fn format_finite_to_vec_unchecked(f: f64, out: &mut alloc::vec::Vec<u8>) -> bool {
const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
if f.to_bits() & EXP_MASK == EXP_MASK {
return false;
}
unsafe {
let len = out.len();
let dst = out.as_mut_ptr().add(len);
let written = format_finite_to_ptr(f, dst);
out.set_len(len + written);
}
true
}
#[cfg(feature = "alloc")]
#[allow(unsafe_code)]
#[inline]
pub(crate) unsafe fn format_finite_to_vec_taint(f: f64, out: &mut alloc::vec::Vec<u8>) -> u64 {
const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
let bits = f.to_bits();
let is_nonfinite = (bits & EXP_MASK) == EXP_MASK;
let safe = if is_nonfinite { 1.0_f64 } else { f };
unsafe {
let len = out.len();
let dst = out.as_mut_ptr().add(len);
let written = format_finite_to_ptr(safe, dst);
out.set_len(len + written);
}
u64::from(is_nonfinite)
}
#[cfg(feature = "alloc")]
#[allow(unsafe_code)]
#[inline]
pub(crate) unsafe fn format_finite_to_vec_unchecked_finite(f: f64, out: &mut alloc::vec::Vec<u8>) {
debug_assert!(f.is_finite(), "caller violated finite-input precondition");
unsafe {
let len = out.len();
let dst = out.as_mut_ptr().add(len);
let written = format_finite_to_ptr(f, dst);
out.set_len(len + written);
}
}
const STORAGE_INDEX_OFFSET: i32 = -324;
include!("teju_tables.txt");
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
use alloc::format;
use alloc::string::String;
#[test]
fn teju_output_roundtrips() {
let mut state: u64 = 0xCAFE_BABE_DEAD_BEEF;
for (count, _) in (0..10_000).enumerate() {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
let mantissa = (state >> 11) as f64 / (1u64 << 53) as f64;
let signed = mantissa.mul_add(2.0, -1.0);
let v = match count % 10 {
0 => signed * 1e-6,
1 => signed * 1e6,
_ => signed * 100.0,
};
if !v.is_finite() || v == 0.0 {
continue;
}
let mut out = String::new();
format_finite(v, &mut out);
let parsed: f64 = out.parse().expect("our output parses");
assert_eq!(
parsed.to_bits(),
v.to_bits(),
"roundtrip failed: formatted={out:?} for v={v:e} (bits=0x{:016x})",
v.to_bits()
);
}
}
#[test]
fn teju_matches_libstd_display() {
let cases = [
1.0_f64,
1.5,
0.1,
0.2,
0.3,
100.0,
12.5,
1.5e10,
-2.7e-5,
1234.5678,
1e100,
1e-100,
1.7976931348623157e308,
5e-324,
];
for &v in &cases {
if v == 0.0 || !v.is_finite() {
continue;
}
let mut ours = String::new();
format_finite(v, &mut ours);
let libstd = format!("{v}");
let ours_parsed: f64 = ours.parse().expect("ours parses");
let libstd_parsed: f64 = libstd.parse().expect("libstd parses");
assert_eq!(
ours_parsed.to_bits(),
libstd_parsed.to_bits(),
"ours={ours:?} libstd={libstd:?} for v={v:e}",
);
}
}
#[test]
fn teju_edge_cases() {
let cases: &[(f64, &str)] = &[
(1.0, "1.0"),
(2.0, "2.0"),
(0.5, "0.5"),
(10.0, "10.0"),
(100.0, "100.0"),
(1e20, "100000000000000000000.0"),
(1.5, "1.5"),
];
for &(v, expected) in cases {
let mut out = String::new();
format_finite(v, &mut out);
assert_eq!(out, expected, "for {v}");
}
}
#[test]
fn teju_zero_handling() {
let mut pos = String::new();
format_finite(0.0, &mut pos);
assert_eq!(pos, "0.0");
let mut neg = String::new();
format_finite(-0.0, &mut neg);
assert_eq!(neg, "-0.0");
}
#[test]
fn teju_small_integers() {
for i in 1u32..=1000 {
let v = f64::from(i);
let mut s = String::new();
format_finite(v, &mut s);
let parsed: f64 = s.parse().expect("our output parses");
assert_eq!(
parsed.to_bits(),
v.to_bits(),
"small integer {i}: round-tripped through {s:?}"
);
}
}
#[test]
fn teju_every_uncentred_value_round_trips() {
let mantissa_uncentred = 1u64 << 52;
for biased_exp in 1u64..=0x7FE {
let bits = (biased_exp << 52) | (mantissa_uncentred & ((1 << 52) - 1));
let v = f64::from_bits(bits);
assert!(v.is_finite(), "biased_exp={biased_exp}");
let mut s = String::new();
format_finite(v, &mut s);
let parsed: f64 = s.parse().expect("our output parses");
assert_eq!(
parsed.to_bits(),
v.to_bits(),
"uncentred f64 with biased_exp={biased_exp} round-trip failed: {s:?}"
);
}
}
#[test]
fn vec_unchecked_at_exact_capacity() {
let cases: &[f64] = &[
0.0,
-0.0,
1.0,
-1.0,
1.5,
0.1,
123_456_789.0,
-1.5e10,
-2.7e-5,
1.7976931348623157e308,
5e-324,
];
for &v in cases {
let mut out = alloc::vec::Vec::with_capacity(FORMAT_BUF_LEN);
#[allow(unsafe_code)]
let ok = unsafe { format_finite_to_vec_unchecked(v, &mut out) };
assert!(ok, "finite input rejected: {v}");
assert!(
out.len() <= FORMAT_BUF_LEN,
"len={} > 32 for {v}",
out.len()
);
assert!(!out.is_empty(), "empty output for {v}");
for &b in &out {
assert!(b.is_ascii(), "non-ASCII byte {b:#x} in output for {v}");
}
}
}
#[test]
fn vec_unchecked_rejects_nonfinite_without_touching_buf() {
for v in [f64::INFINITY, f64::NEG_INFINITY, f64::NAN] {
let mut out = alloc::vec::Vec::with_capacity(FORMAT_BUF_LEN);
#[allow(unsafe_code)]
let ok = unsafe { format_finite_to_vec_unchecked(v, &mut out) };
assert!(!ok, "non-finite accepted: {v}");
assert!(out.is_empty(), "buffer touched for non-finite {v}: {out:?}");
}
}
#[test]
fn vec_taint_at_exact_capacity() {
let mut out = alloc::vec::Vec::with_capacity(FORMAT_BUF_LEN);
#[allow(unsafe_code)]
let taint = unsafe { format_finite_to_vec_taint(1.5_f64, &mut out) };
assert_eq!(taint, 0);
assert_eq!(&out[..], b"1.5");
for v in [f64::INFINITY, f64::NEG_INFINITY, f64::NAN] {
let mut out = alloc::vec::Vec::with_capacity(FORMAT_BUF_LEN);
#[allow(unsafe_code)]
let taint = unsafe { format_finite_to_vec_taint(v, &mut out) };
assert_ne!(taint, 0, "taint not set for {v}");
assert!(!out.is_empty(), "substitute did not write for {v}");
assert!(out.len() <= FORMAT_BUF_LEN);
}
}
#[test]
fn vec_unchecked_finite_covers_digit_counts() {
let cases: &[f64] = &[
1.0,
12.0,
123.0,
1234.0,
12345.0,
123_456.0,
1_234_567.0,
12_345_678.0, 123_456_789.0,
1_234_567_890.0,
12_345_678_901.0,
123_456_789_012.0,
1_234_567_890_123.0,
12_345_678_901_234.0,
123_456_789_012_345.0,
1_234_567_890_123_456.0, 12_345_678_901_234_567.0, 1e-300, 1e300, ];
for &v in cases {
assert!(v.is_finite(), "test bug: non-finite case {v}");
let mut out = alloc::vec::Vec::with_capacity(FORMAT_BUF_LEN);
#[allow(unsafe_code)]
unsafe {
format_finite_to_vec_unchecked_finite(v, &mut out);
}
assert!(!out.is_empty(), "no output for {v}");
assert!(out.len() <= FORMAT_BUF_LEN);
for &b in &out {
assert!(b.is_ascii(), "non-ASCII for {v}: {out:?}");
}
}
}
#[test]
fn vec_unchecked_repeated_writes_accumulate() {
let n: u32 = 50;
let cap = 2 + (n as usize) * (FORMAT_BUF_LEN + 1);
let mut out: alloc::vec::Vec<u8> = alloc::vec::Vec::with_capacity(cap);
for i in 0..n {
let v = f64::from(i) + 0.5;
#[allow(unsafe_code)]
unsafe {
format_finite_to_vec_unchecked_finite(v, &mut out);
if i + 1 < n {
let len = out.len();
let dst = out.as_mut_ptr().add(len);
core::ptr::write(dst, b',');
out.set_len(len + 1);
}
}
}
let s = core::str::from_utf8(&out).expect("ASCII");
let parts: alloc::vec::Vec<f64> = s
.split(',')
.map(|x| x.parse::<f64>().expect("our output parses"))
.collect();
assert_eq!(parts.len(), n as usize);
for (i, &v) in parts.iter().enumerate() {
let expected = f64::from(u32::try_from(i).expect("n=50 fits u32")) + 0.5;
assert_eq!(v.to_bits(), expected.to_bits(), "round-trip {i}");
}
}
}