lexical_write_float/
shared.rs1use crate::options::{Options, RoundMode};
4use lexical_util::digit::{char_to_valid_digit_const, digit_to_char_const};
5use lexical_util::format::NumberFormat;
6use lexical_write_integer::write::WriteInteger;
7
8#[inline(always)]
10pub fn min_exact_digits(digit_count: usize, options: &Options) -> usize {
11 let mut exact_count: usize = digit_count;
12 if let Some(min_digits) = options.min_significant_digits() {
13 exact_count = min_digits.get().max(exact_count);
14 }
15 exact_count
16}
17
18#[cfg_attr(not(feature = "compact"), inline)]
27pub unsafe fn round_up(digits: &mut [u8], count: usize, radix: u32) -> (usize, bool) {
28 debug_assert!(count <= digits.len());
29
30 let mut index = count;
31 let max_char = digit_to_char_const(radix - 1, radix);
32 while index != 0 {
33 let c = unsafe { index_unchecked!(digits[index - 1]) };
36 if c < max_char {
37 let digit = char_to_valid_digit_const(c, radix);
39 let rounded = digit_to_char_const(digit + 1, radix);
40 unsafe { index_unchecked_mut!(digits[index - 1]) = rounded };
41 return (index, false);
42 }
43 index -= 1;
46 }
47
48 unsafe { index_unchecked_mut!(digits[0]) = b'1' };
51
52 (1, true)
53}
54
55#[allow(clippy::comparison_chain)]
65#[cfg_attr(not(feature = "compact"), inline)]
66pub unsafe fn truncate_and_round_decimal(
67 digits: &mut [u8],
68 digit_count: usize,
69 options: &Options,
70) -> (usize, bool) {
71 debug_assert!(digit_count <= digits.len());
72
73 let max_digits = if let Some(digits) = options.max_significant_digits() {
74 digits.get()
75 } else {
76 return (digit_count, false);
77 };
78 if max_digits >= digit_count {
79 return (digit_count, false);
80 }
81
82 if options.round_mode() == RoundMode::Truncate {
84 return (max_digits, false);
86 }
87
88 let truncated = unsafe { index_unchecked!(digits[max_digits]) };
95 let (digits, carried) = if truncated < b'5' {
96 (max_digits, false)
98 } else if truncated > b'5' {
99 unsafe { round_up(digits, max_digits, 10) }
102 } else {
103 let (is_odd, is_above) = unsafe {
106 let to_round = &index_unchecked!(digits[max_digits - 1..digit_count]);
107 let is_odd = index_unchecked!(to_round[0]) % 2 == 1;
108 let is_above = index_unchecked!(to_round[2..]).iter().any(|&x| x != b'0');
109 (is_odd, is_above)
110 };
111 if is_odd || is_above {
112 unsafe { round_up(digits, max_digits, 10) }
114 } else {
115 (max_digits, false)
116 }
117 };
118
119 (digits, carried)
120}
121
122#[cfg_attr(not(feature = "compact"), inline)]
129pub unsafe fn write_exponent_sign<const FORMAT: u128>(
130 bytes: &mut [u8],
131 cursor: &mut usize,
132 exp: i32,
133) -> u32 {
134 let format = NumberFormat::<{ FORMAT }> {};
135 if exp < 0 {
136 unsafe { index_unchecked_mut!(bytes[*cursor]) = b'-' };
138 *cursor += 1;
139 exp.wrapping_neg() as u32
140 } else if cfg!(feature = "format") && format.required_exponent_sign() {
141 unsafe { index_unchecked_mut!(bytes[*cursor]) = b'+' };
143 *cursor += 1;
144 exp as u32
145 } else {
146 exp as u32
147 }
148}
149
150#[cfg_attr(not(feature = "compact"), inline)]
157pub unsafe fn write_exponent<const FORMAT: u128>(
158 bytes: &mut [u8],
159 cursor: &mut usize,
160 exp: i32,
161 exponent_character: u8,
162) {
163 *cursor += unsafe {
164 index_unchecked_mut!(bytes[*cursor]) = exponent_character;
165 *cursor += 1;
166 let positive_exp = write_exponent_sign::<FORMAT>(bytes, cursor, exp);
167 positive_exp.write_exponent::<u32, FORMAT>(&mut index_unchecked_mut!(bytes[*cursor..]))
168 };
169}
170
171macro_rules! write_float {
173 (
174 $format:ident,
175 $sci_exp:ident,
176 $options:ident,
177 $write_scientific:ident,
178 $write_positive:ident,
179 $write_negative:ident,
180 $(generic => $generic:tt,)?
181 args => $($args:expr,)*
182 ) => {{
183 use lexical_util::format::NumberFormat;
184
185 let format = NumberFormat::<{ $format }> {};
186 let min_exp = $options.negative_exponent_break().map_or(-5, |x| x.get());
187 let max_exp = $options.positive_exponent_break().map_or(9, |x| x.get());
188
189 let outside_break = $sci_exp < min_exp || $sci_exp > max_exp;
190 let require_exponent = format.required_exponent_notation() || outside_break;
191 if !format.no_exponent_notation() && require_exponent {
192 unsafe { $write_scientific::<$($generic,)? FORMAT>($($args,)*) }
195 } else if $sci_exp >= 0 {
196 unsafe { $write_positive::<$($generic,)? FORMAT>($($args,)*) }
199 } else {
200 unsafe { $write_negative::<$($generic,)? FORMAT>($($args,)*) }
203 }
204 }};
205}