use crate::{
d2s::{DOUBLE_BIAS, DOUBLE_EXPONENT_BITS, DOUBLE_MANTISSA_BITS},
digit_table::DIGIT_TABLE,
pretty::{
format64,
to_fixed::d2fixed_full_table::{
ADDITIONAL_BITS_2, MIN_BLOCK_2, POW10_OFFSET, POW10_OFFSET_2, POW10_SPLIT,
POW10_SPLIT_2,
},
},
};
#[cfg(feature = "no-panic")]
use no_panic::no_panic;
mod d2fixed_full_table;
pub const MAX_BUFFER_SIZE: usize = 132;
pub struct Cursor {
buffer: *mut u8,
len: isize,
index: isize,
}
impl Cursor {
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn new(buffer: *mut u8, len: usize) -> Self {
debug_assert!(!buffer.is_null());
Self {
buffer,
len: len as isize,
index: 0,
}
}
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn append_byte(&mut self, c: u8) {
debug_assert!(self.index < self.len);
*self.buffer.offset(self.index) = c;
self.index += 1;
}
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn append_bytes(&mut self, c: u8, count: usize) {
debug_assert!(self.index + count as isize <= self.len);
self.buffer.offset(self.index).write_bytes(c, count);
self.index += count as isize;
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn index(&self) -> usize {
self.index as usize
}
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn append_nine_digits(&mut self, mut digits: u32) {
let count = 9;
debug_assert!(self.index + count <= self.len);
if digits == 0 {
self.append_bytes(b'0', 9);
return;
}
let result = self.buffer.offset(self.index);
for i in [0, 4] {
let c = digits % 10000;
digits /= 10000;
let c0 = (c % 100) << 1;
let c1 = (c / 100) << 1;
result
.offset(7 - i as isize)
.copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c0 as isize), 2);
result
.offset(5 - i as isize)
.copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c1 as isize), 2);
}
*(result.offset(0)) = b'0' + digits as u8;
self.index += count;
}
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn append_n_digits(&mut self, mut digits: u32) {
let olength = decimal_length9(digits);
debug_assert!(self.index + olength as isize <= self.len);
let result = self.buffer.offset(self.index);
let mut i = 0;
while digits >= 10000 {
let c = digits % 10000;
digits /= 10000;
let c0 = (c % 100) << 1;
let c1 = (c / 100) << 1;
result
.offset(olength as isize - i as isize - 2)
.copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c0 as isize), 2);
result
.offset(olength as isize - i as isize - 4)
.copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c1 as isize), 2);
i += 4;
}
if digits >= 100 {
let c = (digits % 100) << 1;
digits /= 100;
result
.offset(olength as isize - i as isize - 2)
.copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c as isize), 2);
i += 2;
}
if digits >= 10 {
let c = digits << 1;
result
.offset(olength as isize - i as isize - 2)
.copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c as isize), 2);
} else {
*result = b'0' + digits as u8;
}
self.index += olength as isize;
}
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn append_c_digits(&mut self, count: u32, mut digits: u32) {
debug_assert!(self.index + count as isize <= self.len);
let result = self.buffer.offset(self.index);
let mut i: u32 = 0;
while i < count - 1 {
let c: u32 = (digits % 100) << 1;
digits /= 100;
result
.offset((count - i - 2) as isize)
.copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c as isize), 2);
i += 2;
}
if i < count {
let c = b'0' + (digits % 10) as u8;
*result.offset((count - i - 1) as isize) = c;
}
self.index += count as isize;
}
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn get(&mut self, i: isize) -> u8 {
debug_assert!((0..self.len).contains(&i));
*self.buffer.offset(i)
}
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn set(&mut self, i: isize, c: u8) {
debug_assert!((0..self.len).contains(&i));
*self.buffer.offset(i) = c;
}
}
const MAX_EXPONENT: u32 = 0b100_0100_0100; const MIN_EXPONENT: u16 = 0b010_1001_0011;
const MAX_E2: i32 = MAX_EXPONENT as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32;
const MIN_E2: i32 = MIN_EXPONENT as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32;
const MAX_POW10_SPLIT_2_INX: i32 = -MIN_E2 / 16;
const POW10_ADDITIONAL_BITS: u32 = 120;
#[cfg_attr(feature = "no-panic", no_panic)]
fn log10_pow2(e: i32) -> u32 {
debug_assert!((0..=1650).contains(&e));
((e as u32) * 78913) >> 18
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn index_for_exponent(e: u32) -> u32 {
debug_assert!((0..=MAX_E2 as u32).contains(&e));
let result = (e + 15) / 16;
debug_assert!((0..=2).contains(&result));
result
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn pow10_bits_for_index(idx: u32) -> u32 {
16 * idx + POW10_ADDITIONAL_BITS
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn length_for_index(idx: u32) -> u32 {
(log10_pow2(16 * idx as i32) + 1 + 16 + 8) / 9
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn umul256(a: u128, b_hi: u64, b_lo: u64) -> (u128, u128) {
let a_lo = a as u64;
let a_hi = (a >> 64) as u64;
let b00 = (a_lo as u128) * (b_lo as u128);
let b01 = (a_lo as u128) * (b_hi as u128);
let b10 = (a_hi as u128) * (b_lo as u128);
let b11 = (a_hi as u128) * (b_hi as u128);
let b00_lo = b00 as u64;
let b00_hi = (b00 >> 64) as u64;
let mid1 = b10 + b00_hi as u128;
let mid1_lo = (mid1) as u64;
let mid1_hi = (mid1 >> 64) as u64;
let mid2 = b01 + mid1_lo as u128;
let mid2_lo = (mid2) as u64;
let mid2_hi = (mid2 >> 64) as u64;
let p_hi = b11 + mid1_hi as u128 + mid2_hi as u128;
let p_lo = ((mid2_lo as u128) << 64) | b00_lo as u128;
(p_hi, p_lo)
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn umul256_hi(a: u128, b_hi: u64, b_lo: u64) -> u128 {
let (hi, _lo) = umul256(a, b_hi, b_lo);
hi
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn uint128_mod1e9(v: u128) -> u32 {
let multiplied = umul256_hi(v, 0x89705F4136B4A597, 0x31680A88F8953031) as u64;
let shifted = (multiplied >> 29) as u32;
(v as u32).wrapping_sub(1000000000u32.wrapping_mul(shifted))
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn mul_shift_mod1e9(m: u64, mul: &[u64; 3], j: i32) -> u32 {
let b0 = m as u128 * mul[0] as u128; let b1 = m as u128 * mul[1] as u128; let b2 = m as u128 * mul[2] as u128;
debug_assert!((128..=180).contains(&j));
let mid = b1 + ((b0 >> 64) as u64) as u128; let s1 = b2 + ((mid >> 64) as u64) as u128; uint128_mod1e9(s1 >> (j - 128))
}
#[cfg_attr(feature = "no-panic", no_panic)]
fn decimal_length9(v: u32) -> u32 {
debug_assert!(v < 1000000000);
if v >= 100000000 {
9
} else if v >= 10000000 {
8
} else if v >= 1000000 {
7
} else if v >= 100000 {
6
} else if v >= 10000 {
5
} else if v >= 1000 {
4
} else if v >= 100 {
3
} else if v >= 10 {
2
} else {
1
}
}
#[must_use]
#[cfg_attr(feature = "no-panic", no_panic)]
pub unsafe fn format64_to_fixed(f: f64, fraction_digits: u8, result: *mut u8) -> usize {
debug_assert!((0..=100).contains(&fraction_digits));
let f_abs = if f < 0.0 { -f } else { f };
if f_abs >= 1e21 {
return format64(f, result);
}
let mut result = Cursor::new(result, MAX_BUFFER_SIZE);
let bits = f.to_bits();
let sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0;
let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1);
let ieee_exponent =
(bits >> DOUBLE_MANTISSA_BITS) as u32 & ((1u32 << DOUBLE_EXPONENT_BITS) - 1);
if ieee_exponent == 0 && ieee_mantissa == 0 {
result.append_byte(b'0');
if fraction_digits == 0 {
return result.index();
}
result.append_byte(b'.');
result.append_bytes(b'0', fraction_digits as usize);
return result.index();
}
debug_assert!((0..=MAX_EXPONENT).contains(&ieee_exponent));
if sign {
result.append_byte(b'-');
}
let (e2, m2) = if ieee_exponent == 0 {
(1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32, ieee_mantissa)
} else {
(
ieee_exponent as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32,
(1 << DOUBLE_MANTISSA_BITS) | ieee_mantissa,
)
};
debug_assert!((..=MAX_E2).contains(&e2));
let mut nonzero = false;
if e2 >= -(DOUBLE_MANTISSA_BITS as i32) {
let idx = if e2 < 0 {
0
} else {
index_for_exponent(e2 as u32)
};
let p10bits = pow10_bits_for_index(idx);
let len = length_for_index(idx) as i32;
for i in (0..len).rev() {
let j = p10bits as i32 - e2;
let split_idx = *POW10_OFFSET.get_unchecked(idx as usize) as usize;
let mul = POW10_SPLIT.get_unchecked(split_idx + i as usize);
let digits = mul_shift_mod1e9(m2 << 8, mul, j + 8);
if nonzero {
result.append_nine_digits(digits);
} else if digits != 0 {
result.append_n_digits(digits);
nonzero = true;
}
}
}
if !nonzero {
result.append_byte(b'0');
}
if fraction_digits != 0 {
result.append_byte(b'.');
}
if e2 >= 0 {
result.append_bytes(b'0', fraction_digits as usize);
return result.index();
}
let fraction_digits = fraction_digits as u32;
let idx = (-e2 / 16).min(MAX_POW10_SPLIT_2_INX) as usize;
let min_block = MIN_BLOCK_2[idx];
let blocks: u32 = fraction_digits / 9 + 1;
if blocks <= min_block as u32 {
result.append_bytes(b'0', fraction_digits as usize);
return result.index();
}
debug_assert!(idx <= 25);
let mut round_up = false;
for i in 0..blocks {
let p: isize = POW10_OFFSET_2[idx] as isize + i as isize - min_block as isize;
debug_assert!(p >= 0);
let p = p as usize;
if p >= *POW10_OFFSET_2.get_unchecked(idx + 1) as usize {
let fill = fraction_digits as usize - 9 * i as usize;
result.append_bytes(b'0', fill);
break;
}
debug_assert!(p <= 480);
let j: isize = ADDITIONAL_BITS_2 as isize + (-(e2 as isize) - 16 * idx as isize);
let mut digits: u32 =
mul_shift_mod1e9(m2 << 8, POW10_SPLIT_2.get_unchecked(p), j as i32 + 8);
if i < blocks - 1 {
result.append_nine_digits(digits);
} else {
let maximum: u32 = fraction_digits - 9 * i;
let mut last_digit: u32 = 0;
for _k in 0..(9 - maximum) {
last_digit = digits % 10;
digits /= 10;
}
round_up = last_digit >= 5;
if maximum != 0 {
result.append_c_digits(maximum, digits);
}
break;
}
}
if round_up {
let mut round_index = result.index;
let mut dot_index = 0; loop {
round_index -= 1;
let c = result.get(round_index);
if round_index == -1 || c == b'-' {
result.set(round_index + 1, b'1');
if dot_index > 0 {
result.set(dot_index, b'0');
result.set(dot_index + 1, b'.');
}
result.append_byte(b'0');
break;
}
if c == b'.' {
dot_index = round_index;
continue;
} else if c == b'9' {
result.set(round_index, b'0');
continue;
}
result.set(round_index, c + 1);
break;
}
}
result.index()
}