use super::zmij::{DIGIT_SCRATCH, Digits, digits_f32, digits_f64};
pub(crate) const MAX_FLOAT_BYTES: usize = 48;
const BLOCK: usize = 24;
const MIN_FIXED: i32 = -4;
const MAX_FIXED_F64: i32 = 15;
const MAX_FIXED_F32: i32 = 6;
#[inline(always)]
fn copy_block(src: &[u8; DIGIT_SCRATCH], from: usize, dst: &mut [u8; MAX_FLOAT_BYTES], to: usize) {
debug_assert!(from + BLOCK <= DIGIT_SCRATCH && to + BLOCK <= MAX_FLOAT_BYTES);
let block: [u8; BLOCK] = src[from..from + BLOCK].try_into().unwrap();
dst[to..to + BLOCK].copy_from_slice(&block);
}
fn render(negative: bool, d: &Digits, max_fixed: i32, out: &mut [u8; MAX_FLOAT_BYTES]) -> usize {
out[0] = b'-';
let mut p = negative as usize;
let nd = d.len;
let start = d.start;
if d.e10 >= MIN_FIXED && d.e10 <= max_fixed {
if d.e10 >= 0 {
let int_digits = d.e10 as usize + 1;
copy_block(&d.buf, start, out, p);
if int_digits >= nd {
for i in nd..int_digits {
out[p + i] = b'0';
}
p += int_digits;
} else {
p += int_digits;
out[p] = b'.';
p += 1;
copy_block(&d.buf, start + int_digits, out, p);
p += nd - int_digits;
}
} else {
out[p] = b'0';
out[p + 1] = b'.';
p += 2;
let zeros = (-d.e10 - 1) as usize;
for i in 0..zeros {
out[p + i] = b'0';
}
p += zeros;
copy_block(&d.buf, start, out, p);
p += nd;
}
} else {
out[p] = d.buf[start];
p += 1;
if nd > 1 {
out[p] = b'.';
p += 1;
copy_block(&d.buf, start + 1, out, p);
p += nd - 1;
}
out[p] = b'E';
p += 1;
let mut e = d.e10;
if e < 0 {
out[p] = b'-';
p += 1;
e = -e;
}
if e >= 100 {
out[p] = b'0' + (e / 100) as u8;
p += 1;
}
if e >= 10 {
out[p] = b'0' + ((e / 10) % 10) as u8;
p += 1;
}
out[p] = b'0' + (e % 10) as u8;
p += 1;
}
p
}
#[inline]
fn write_zero(negative: bool, out: &mut [u8; MAX_FLOAT_BYTES]) -> usize {
out[0] = b'-';
out[negative as usize] = b'0';
negative as usize + 1
}
#[inline]
pub(crate) fn write_f64(v: f64, out: &mut [u8; MAX_FLOAT_BYTES]) -> Option<usize> {
if !v.is_finite() {
return None;
}
let negative = v.is_sign_negative();
let mag = if negative { -v } else { v };
if mag == 0.0 {
return Some(write_zero(negative, out));
}
Some(render(negative, &digits_f64(mag), MAX_FIXED_F64, out))
}
#[inline]
pub(crate) fn write_f32(v: f32, out: &mut [u8; MAX_FLOAT_BYTES]) -> Option<usize> {
if !v.is_finite() {
return None;
}
let negative = v.is_sign_negative();
let mag = if negative { -v } else { v };
if mag == 0.0 {
return Some(write_zero(negative, out));
}
Some(render(negative, &digits_f32(mag), MAX_FIXED_F32, out))
}