1#![no_std]
44#![doc(html_root_url = "https://docs.rs/zmij/1.0.23")]
45#![deny(unsafe_op_in_unsafe_fn)]
46#![allow(non_camel_case_types, non_snake_case)]
47#![allow(
48 clippy::blocks_in_conditions,
49 clippy::cast_possible_truncation,
50 clippy::cast_possible_wrap,
51 clippy::cast_ptr_alignment,
52 clippy::cast_sign_loss,
53 clippy::doc_markdown,
54 clippy::incompatible_msrv,
55 clippy::items_after_statements,
56 clippy::manual_ilog2,
57 clippy::many_single_char_names,
58 clippy::modulo_one,
59 clippy::must_use_candidate,
60 clippy::needless_doctest_main,
61 clippy::needless_late_init,
62 clippy::never_loop,
63 clippy::redundant_else,
64 clippy::similar_names,
65 clippy::too_many_arguments,
66 clippy::too_many_lines,
67 clippy::unreadable_literal,
68 clippy::used_underscore_items,
69 clippy::while_immutable_condition,
70 clippy::wildcard_imports
71)]
72
73#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
74mod stdarch_x86;
75#[cfg(test)]
76mod tests;
77mod traits;
78
79#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
80use crate::stdarch_x86::{
81 __m128i, _mm_add_epi64, _mm_cmpgt_epi8, _mm_cvtsi128_si64, _mm_load_si128, _mm_movemask_epi8,
82 _mm_mul_epu32, _mm_mulhi_epu16, _mm_mullo_epi16, _mm_or_si128, _mm_set_epi64x,
83 _mm_setzero_si128, _mm_srli_epi64,
84};
85#[cfg(all(
86 target_arch = "x86_64",
87 target_feature = "sse2",
88 target_feature = "sse4.1",
89 not(miri)
90))]
91use crate::stdarch_x86::{
92 _mm_insert_epi64, _mm_mullo_epi32, _mm_shuffle_epi8, _mm_srli_epi32, _mm_storeu_si128,
93};
94#[cfg(all(
95 target_arch = "x86_64",
96 target_feature = "sse2",
97 not(target_feature = "sse4.1"),
98 not(miri)
99))]
100use crate::stdarch_x86::{
101 _mm_shuffle_epi32, _mm_slli_epi16, _mm_slli_epi32, _mm_srli_epi16, _mm_sub_epi16, _MM_SHUFFLE,
102};
103use crate::traits::Float as _;
104#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
105use core::arch::aarch64::{
106 int16x8_t, int32x2_t, int32x4_t, uint16x8_t, uint64x1_t, uint8x16_t, vaddq_u16, vcgtzq_s8,
107 vcombine_s32, vcreate_u64, vdup_n_s32, vdupq_n_s8, vdupq_n_u8, vget_lane_u64, vget_low_u8,
108 vld1q_u8, vmla_n_s32, vmlaq_n_s16, vmlaq_n_s32, vorrq_u8, vqdmulh_n_s32, vqdmulhq_n_s16,
109 vqdmulhq_n_s32, vqtbl1q_u8, vreinterpret_s32_u32, vreinterpret_s32_u64, vreinterpret_u16_s32,
110 vreinterpret_u32_s32, vreinterpret_u64_u8, vreinterpretq_s16_s32, vreinterpretq_s32_u32,
111 vreinterpretq_s8_u8, vreinterpretq_u16_s8, vreinterpretq_u16_u8, vreinterpretq_u64_u8,
112 vreinterpretq_u8_s16, vreinterpretq_u8_u64, vrev64q_u8, vsetq_lane_u64, vshll_n_u16,
113 vshr_n_u32, vshrn_n_u16, vst1q_u8,
114};
115#[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
116use core::arch::asm;
117use core::mem::{self, MaybeUninit};
118use core::ops::RangeInclusive;
119use core::ptr;
120use core::slice;
121use core::str;
122#[cfg(feature = "no-panic")]
123use no_panic::no_panic;
124
125const BUFFER_SIZE: usize = 24;
126const NAN: &str = "NaN";
127const INFINITY: &str = "inf";
128const NEG_INFINITY: &str = "-inf";
129
130struct AArch64Mem<const VALUE: u64> {
133 #[cfg(target_arch = "aarch64")]
134 value: u64,
135}
136
137impl<const VALUE: u64> AArch64Mem<VALUE> {
138 const fn new() -> Self {
139 AArch64Mem {
140 #[cfg(target_arch = "aarch64")]
141 value: VALUE,
142 }
143 }
144
145 #[cfg_attr(not(target_arch = "aarch64"), allow(clippy::unused_self))]
146 const fn get(&self) -> u64 {
147 #[cfg(target_arch = "aarch64")]
148 {
149 self.value
150 }
151
152 #[cfg(not(target_arch = "aarch64"))]
153 {
154 VALUE
155 }
156 }
157}
158
159#[derive(#[automatically_derived]
impl ::core::marker::Copy for uint128 { }Copy, #[automatically_derived]
impl ::core::clone::Clone for uint128 {
#[inline]
fn clone(&self) -> uint128 {
let _: ::core::clone::AssertParamIsClone<u64>;
*self
}
}Clone)]
160#[cfg_attr(test, derive(Debug, PartialEq))]
161struct uint128 {
162 hi: u64,
163 lo: u64,
164}
165
166const USE_UMUL128_HI64: bool = falsecfg!(target_vendor = "apple");
168
169const fn umul128(x: u64, y: u64) -> u128 {
171 x as u128 * y as u128
172}
173
174#[inline]
175const fn umul128_hi64(x: u64, y: u64) -> u64 {
176 (umul128(x, y) >> 64) as u64
177}
178
179#[cfg_attr(feature = "no-panic", no_panic)]
181fn umul128_add_hi64(x: u64, y: u64, c: u64) -> u64 {
182 ((u128::from(x) * u128::from(y) + u128::from(c)) >> 64) as u64
183}
184
185#[cfg_attr(feature = "no-panic", no_panic)]
186fn umul192_hi128(x_hi: u64, x_lo: u64, y: u64) -> uint128 {
187 let p = umul128(x_hi, y);
188 let lo = (p as u64).wrapping_add((umul128(x_lo, y) >> 64) as u64);
189 uint128 {
190 hi: (p >> 64) as u64 + u64::from(lo < p as u64),
191 lo,
192 }
193}
194
195#[cfg_attr(feature = "no-panic", no_panic)]
197fn div10(x: u64) -> u64 {
198 if true {
if !(x < (1 << 62)) {
::core::panicking::panic("assertion failed: x < (1 << 62)")
};
};debug_assert!(x < (1 << 62));
199 const DIV10_SIG64: u64 = (1 << 63) / 5 + 1;
201 umul128_hi64(x, DIV10_SIG64)
202}
203
204const fn compute_dec_exp(bin_exp: i32, regular: bool) -> i32 {
207 if true {
if !(bin_exp >= -1334 && bin_exp <= 2620) {
::core::panicking::panic("assertion failed: bin_exp >= -1334 && bin_exp <= 2620")
};
};debug_assert!(bin_exp >= -1334 && bin_exp <= 2620);
208 const LOG10_3_OVER_4_SIG: i32 = 131_072;
210 const LOG10_2_SIG: i32 = 315_653;
212 const LOG10_2_EXP: i32 = 20;
213 (bin_exp * LOG10_2_SIG - !regular as i32 * LOG10_3_OVER_4_SIG) >> LOG10_2_EXP
214}
215
216trait FloatTraits: traits::Float {
217 const FIXED_DEC_EXP: RangeInclusive<i32>;
219
220 const NUM_BITS: i32;
221 const NUM_SIG_BITS: i32 = Self::MANTISSA_DIGITS as i32 - 1;
222 const NUM_EXP_BITS: i32 = Self::NUM_BITS - Self::NUM_SIG_BITS - 1;
223 const EXP_MASK: i32 = (1 << Self::NUM_EXP_BITS) - 1;
224 const EXP_BIAS: i32 = (1 << (Self::NUM_EXP_BITS - 1)) - 1;
225 const EXP_OFFSET: i32 = Self::EXP_BIAS + Self::NUM_SIG_BITS;
226
227 type SigType: traits::UInt;
228 const IMPLICIT_BIT: Self::SigType;
229
230 type DecDigitsType: Copy;
231
232 #[cfg(any(
233 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
234 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
235 ))]
236 type DecUnshuffledType;
237
238 fn to_bits(self) -> Self::SigType;
239
240 fn is_negative(bits: Self::SigType) -> bool {
241 (bits >> (Self::NUM_BITS - 1)) != Self::SigType::from(0)
242 }
243
244 fn get_sig(bits: Self::SigType) -> Self::SigType {
245 bits & (Self::IMPLICIT_BIT - Self::SigType::from(1))
246 }
247
248 fn get_exp(bits: Self::SigType) -> i64 {
249 (bits << 1u8 >> (Self::NUM_SIG_BITS + 1)).into() as i64
250 }
251
252 fn to_digits(value: u64, d: &Data) -> DecDigits<Self>;
256
257 unsafe fn write_exp_float_simd(
258 buffer: *mut u8,
259 dig: &DecDigits<Self>,
260 last_digit: i32,
261 has_last_digit: bool,
262 has_extra_digit: bool,
263 exp_data: u64,
264 d: &Data,
265 ) -> *mut u8;
266}
267
268impl FloatTraits for f32 {
269 const FIXED_DEC_EXP: RangeInclusive<i32> = -6..=12;
271
272 const NUM_BITS: i32 = 32;
273 const IMPLICIT_BIT: u32 = 1 << Self::NUM_SIG_BITS;
274
275 type SigType = u32;
276
277 type DecDigitsType = u64;
278
279 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
280 type DecUnshuffledType = uint8x16_t;
281 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
282 type DecUnshuffledType = __m128i;
283
284 #[inline]
285 fn to_bits(self) -> Self::SigType {
286 self.to_bits()
287 }
288
289 #[inline]
290 fn to_digits(value: u64, d: &Data) -> DecDigits<Self> {
291 to_digits_32(value, d)
292 }
293
294 #[inline]
295 unsafe fn write_exp_float_simd(
296 buffer: *mut u8,
297 dig: &DecDigits<Self>,
298 last_digit: i32,
299 has_last_digit: bool,
300 has_extra_digit: bool,
301 exp_data: u64,
302 d: &Data,
303 ) -> *mut u8 {
304 unsafe {
305 write_exp_float_simd_32(
306 buffer,
307 dig,
308 last_digit,
309 has_last_digit,
310 has_extra_digit,
311 exp_data,
312 d,
313 )
314 }
315 }
316}
317
318impl FloatTraits for f64 {
319 const FIXED_DEC_EXP: RangeInclusive<i32> = -5..=15;
321
322 const NUM_BITS: i32 = 64;
323 const IMPLICIT_BIT: u64 = 1 << Self::NUM_SIG_BITS;
324
325 type SigType = u64;
326
327 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
328 type DecDigitsType = uint16x8_t;
329 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
330 type DecDigitsType = __m128i;
331 #[cfg(not(any(
332 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
333 all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
334 )))]
335 type DecDigitsType = [u64; 2];
336
337 #[cfg(any(
338 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
339 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
340 ))]
341 type DecUnshuffledType = ();
342
343 #[inline]
344 fn to_bits(self) -> Self::SigType {
345 self.to_bits()
346 }
347
348 #[inline]
349 fn to_digits(value: u64, d: &Data) -> DecDigits<Self> {
350 to_digits_64(value, d)
351 }
352
353 #[inline]
354 unsafe fn write_exp_float_simd(
355 _buffer: *mut u8,
356 _dig: &DecDigits<Self>,
357 _last_digit: i32,
358 _has_last_digit: bool,
359 _has_extra_digit: bool,
360 _exp_data: u64,
361 _d: &Data,
362 ) -> *mut u8 {
363 ptr::null_mut()
364 }
365}
366
367#[rustfmt::skip]
368const POW10_MINOR: [u64; 28] = [
369 0x8000000000000000, 0xa000000000000000, 0xc800000000000000,
370 0xfa00000000000000, 0x9c40000000000000, 0xc350000000000000,
371 0xf424000000000000, 0x9896800000000000, 0xbebc200000000000,
372 0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000,
373 0xe8d4a51000000000, 0x9184e72a00000000, 0xb5e620f480000000,
374 0xe35fa931a0000000, 0x8e1bc9bf04000000, 0xb1a2bc2ec5000000,
375 0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000,
376 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400,
377 0xd3c21bcecceda100, 0x84595161401484a0, 0xa56fa5b99019a5c8,
378 0xcecb8f27f4200f3a,
379];
380
381#[rustfmt::skip]
382const POW10_MAJOR: [uint128; 23] = [
383 uint128 { hi: 0xaf8e5410288e1b6f, lo: 0x07ecf0ae5ee44dda }, uint128 { hi: 0xb1442798f49ffb4a, lo: 0x99cd11cfdf41779d }, uint128 { hi: 0xb2fe3f0b8599ef07, lo: 0x861fa7e6dcb4aa15 }, uint128 { hi: 0xb4bca50b065abe63, lo: 0x0fed077a756b53aa }, uint128 { hi: 0xb67f6455292cbf08, lo: 0x1a3bc84c17b1d543 }, uint128 { hi: 0xb84687c269ef3bfb, lo: 0x3d5d514f40eea742 }, uint128 { hi: 0xba121a4650e4ddeb, lo: 0x92f34d62616ce413 }, uint128 { hi: 0xbbe226efb628afea, lo: 0x890489f70a55368c }, uint128 { hi: 0xbdb6b8e905cb600f, lo: 0x5400e987bbc1c921 }, uint128 { hi: 0xbf8fdb78849a5f96, lo: 0xde98520472bdd034 }, uint128 { hi: 0xc16d9a0095928a27, lo: 0x75b7053c0f178294 }, uint128 { hi: 0xc350000000000000, lo: 0x0000000000000000 }, uint128 { hi: 0xc5371912364ce305, lo: 0x6c28000000000000 }, uint128 { hi: 0xc722f0ef9d80aad6, lo: 0x424d3ad2b7b97ef6 }, uint128 { hi: 0xc913936dd571c84c, lo: 0x03bc3a19cd1e38ea }, uint128 { hi: 0xcb090c8001ab551c, lo: 0x5cadf5bfd3072cc6 }, uint128 { hi: 0xcd036837130890a1, lo: 0x36dba887c37a8c10 }, uint128 { hi: 0xcf02b2c21207ef2e, lo: 0x94f967e45e03f4bc }, uint128 { hi: 0xd106f86e69d785c7, lo: 0xe13336d701beba52 }, uint128 { hi: 0xd31045a8341ca07c, lo: 0x1ede48111209a051 }, uint128 { hi: 0xd51ea6fa85785631, lo: 0x552a74227f3ea566 }, uint128 { hi: 0xd732290fbacaf133, lo: 0xa97c177947ad4096 }, uint128 { hi: 0xd94ad8b1c7380874, lo: 0x18375281ae7822bc }, ];
407
408#[rustfmt::skip]
409const POW10_FIXUPS: [u32; 20] = [
410 0x0a4e363f, 0x00001840, 0x00006400, 0x24200040, 0x00000000,
411 0x0c000000, 0x82c81380, 0x5e4ce01f, 0xd730f60f, 0x0000001b,
412 0x00000000, 0xcdf7fffc, 0x6e8201d8, 0x40cd3fd1, 0xdb642501,
413 0x00000d0d, 0x14042400, 0x53713840, 0x11781db4, 0x00000000,
414];
415
416#[repr(C, align(64))]
418struct Pow10SignificandTable {
419 data: [u64; if Self::COMPRESS {
420 0
421 } else {
422 Self::NUM_POW10S * 2
423 }],
424}
425
426impl Pow10SignificandTable {
427 const COMPRESS: bool = falsecfg!(opt_level = "s");
428 const SPLIT_TABLES: bool = !Self::COMPRESS && falsecfg!(target_arch = "aarch64");
429 const NUM_POW10S: usize = 618;
430
431 #[inline]
433 const fn compute(i: u32) -> uint128 {
434 const STRIDE: u32 = POW10_MINOR.len() as u32;
435 let m = unsafe { *POW10_MINOR.as_ptr().add(((i + 10) % STRIDE) as usize) };
436 let h = unsafe { *POW10_MAJOR.as_ptr().add(((i + 10) / STRIDE) as usize) };
437
438 let h1 = umul128_hi64(h.lo, m);
439
440 let c0 = h.lo.wrapping_mul(m);
441 let c1 = h1.wrapping_add(h.hi.wrapping_mul(m));
442 let c2 = (c1 < h1) as u64 + umul128_hi64(h.hi, m);
443
444 let mut result = if (c2 >> 63) != 0 {
445 uint128 { hi: c2, lo: c1 }
446 } else {
447 uint128 {
448 hi: (c2 << 1) | (c1 >> 63),
449 lo: (c1 << 1) | (c0 >> 63),
450 }
451 };
452 result.lo -=
453 ((unsafe { *POW10_FIXUPS.as_ptr().add((i >> 5) as usize) } >> (i & 31)) & 1) as u64;
454 result
455 }
456
457 const fn new() -> Self {
458 let mut data = [0; if Self::COMPRESS {
459 0
460 } else {
461 Self::NUM_POW10S * 2
462 }];
463
464 let mut i = 0;
465 while i < Self::NUM_POW10S && !Self::COMPRESS {
466 let result = Self::compute(i as u32);
467 if Self::SPLIT_TABLES {
468 data[Self::NUM_POW10S - i - 1] = result.hi;
469 data[Self::NUM_POW10S * 2 - i - 1] = result.lo;
470 } else {
471 data[i * 2] = result.hi;
472 data[i * 2 + 1] = result.lo;
473 }
474 i += 1;
475 }
476
477 Pow10SignificandTable { data }
478 }
479
480 #[inline]
481 unsafe fn get_unchecked(&self, dec_exp: i32) -> uint128 {
482 const DEC_EXP_MIN: i32 = -293;
483 let i = dec_exp - DEC_EXP_MIN;
484 if Self::COMPRESS {
485 return Self::compute(i as u32);
486 }
487 if !Self::SPLIT_TABLES {
488 let p = unsafe { self.data.as_ptr().add((i * 2) as usize) };
489 return uint128 {
490 hi: unsafe { *p },
491 lo: unsafe { *p.add(1) },
492 };
493 }
494
495 unsafe {
496 #[cfg_attr(
500 not(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri))),
501 allow(unused_mut)
502 )]
503 let mut p = self
504 .data
505 .as_ptr()
506 .offset(Self::NUM_POW10S as isize + DEC_EXP_MIN as isize);
507 #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri)))]
508 asm!("/*{0}*/", inout(reg) p);
509 uint128 {
510 hi: *p.offset(!(dec_exp as isize)),
511 lo: *p.offset(!(dec_exp as isize) + Self::NUM_POW10S as isize),
512 }
513 }
514 }
515
516 #[cfg(test)]
517 fn get(&self, dec_exp: i32) -> uint128 {
518 const DEC_EXP_MIN: i32 = -292;
519 assert!((DEC_EXP_MIN..DEC_EXP_MIN + Self::NUM_POW10S as i32).contains(&dec_exp));
520 unsafe { self.get_unchecked(dec_exp) }
521 }
522}
523
524#[inline]
535const fn compute_exp_shift(bin_exp: i32, dec_exp: i32) -> u8 {
536 if true {
if !(dec_exp >= -350 && dec_exp <= 350) {
::core::panicking::panic("assertion failed: dec_exp >= -350 && dec_exp <= 350")
};
};debug_assert!(dec_exp >= -350 && dec_exp <= 350);
537 const LOG2_POW10_SIG: i32 = 217_707;
539 const LOG2_POW10_EXP: i32 = 16;
540 let pow10_bin_exp = (-dec_exp * LOG2_POW10_SIG) >> LOG2_POW10_EXP;
542 (bin_exp + pow10_bin_exp + 1) as u8
544}
545
546struct ExpShiftTable {
547 data: [u8; if Self::ENABLE {
548 f64::EXP_MASK as usize + 1
549 } else {
550 0
551 }],
552}
553
554impl ExpShiftTable {
555 const ENABLE: bool = truecfg!(not(opt_level = "s"));
556 const EXTRA_SHIFT: usize = 6;
559
560 const fn new() -> Self {
561 let mut data = [0u8; if Self::ENABLE {
562 f64::EXP_MASK as usize + 1
563 } else {
564 0
565 }];
566
567 let mut raw_exp = 0;
568 while raw_exp < data.len() && Self::ENABLE {
569 let mut bin_exp = raw_exp as i32 - f64::EXP_OFFSET;
570 if raw_exp == 0 {
571 bin_exp += 1;
572 }
573 let dec_exp = compute_dec_exp(bin_exp, true);
574 data[raw_exp] =
575 compute_exp_shift(bin_exp, dec_exp + 1).wrapping_add(Self::EXTRA_SHIFT as u8);
576 raw_exp += 1;
577 }
578
579 ExpShiftTable { data }
580 }
581}
582
583struct ExpStringTable {
586 data: [u64; if Self::ENABLE {
587 (f64::MAX_10_EXP - Self::MIN_DEC_EXP + 1) as usize
588 } else {
589 0
590 }],
591}
592
593impl ExpStringTable {
594 const ENABLE: bool = truecfg!(not(opt_level = "s"));
595 const MIN_DEC_EXP: i32 = f64::MIN_10_EXP - f64::MAX_DIGITS10 as i32;
596 const OFFSET: i32 = -Self::MIN_DEC_EXP;
597
598 const fn new() -> Self {
599 let mut data = [0u64; if Self::ENABLE {
600 (f64::MAX_10_EXP - Self::MIN_DEC_EXP + 1) as usize
601 } else {
602 0
603 }];
604
605 let mut e = Self::MIN_DEC_EXP;
606 while e <= f64::MAX_10_EXP && Self::ENABLE {
607 let abs_e = e.unsigned_abs() as u64;
608 let mut val = abs_e % 10 + b'0' as u64;
609 if abs_e >= 10 {
610 val = (val << 8) | (abs_e / 10 % 10 + b'0' as u64);
611 }
612 if abs_e >= 100 {
613 val = (val << 8) | (abs_e / 100 + b'0' as u64);
614 }
615 let len = 3 + (abs_e >= 10) as u64 + (abs_e >= 100) as u64;
616 data[(e + Self::OFFSET) as usize] = (len << 48)
617 | (val << 16)
618 | (if e >= 0 { b'+' as u64 } else { b'-' as u64 } << 8)
619 | b'e' as u64;
620 e += 1;
621 }
622
623 ExpStringTable { data }
624 }
625}
626
627#[repr(C, align(16))]
638struct ExpFloatShuffleTable {
639 data: [u8; if Self::ENABLE { 32 * 16 } else { 0 }],
640}
641
642struct ExpFloatShuffleTableEntry {
643 #[cfg_attr(
644 not(any(
645 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
646 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
647 )),
648 allow(dead_code)
649 )]
650 shuffle: *const u8,
651 length: u8,
652}
653
654impl ExpFloatShuffleTable {
655 const ENABLE: bool = falsecfg!(any(
656 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
657 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
658 )) && ExpStringTable::ENABLE;
659
660 const EXP_POS: u8 = 8;
661 const LAST_DIGIT_POS: u8 = 12;
662 const POINT_POS: u8 = 13;
663
664 unsafe fn get_entry(
665 &self,
666 num_digits: i32,
667 has_last_digit: bool,
668 has_extra_digit: bool,
669 ) -> ExpFloatShuffleTableEntry {
670 let idx = (num_digits - 1) * 4 + i32::from(has_last_digit) * 2 + i32::from(has_extra_digit);
671 ExpFloatShuffleTableEntry {
672 shuffle: unsafe { self.data.as_ptr().add(idx as usize * 16) },
673 length: *unsafe { self.data.get_unchecked(idx as usize * 16 + 15) },
674 }
675 }
676
677 const fn new() -> Self {
678 let mut data = [0u8; if Self::ENABLE { 32 * 16 } else { 0 }];
679
680 let mut idx = 0;
681 while idx < 32 && Self::ENABLE {
682 let num_digits = (idx >> 2) + 1;
683 let has_last_digit = ((idx >> 1) & 1) != 0;
684 let has_extra_digit = (idx & 1) != 0;
685
686 let out = idx * 16;
687 let mut i = 0;
688 while i < 16 {
689 data[out + i] = 0x80; i += 1;
691 }
692 let leading_digit_pos = if has_extra_digit { 7 } else { 6 };
693 let mut length = 0;
694 if has_last_digit {
695 data[out + length] = leading_digit_pos;
699 length += 1;
700 data[out + length] = Self::POINT_POS;
701 length += 1;
702 let mut i = leading_digit_pos - 1;
703 loop {
704 data[out + length] = i;
705 length += 1;
706 if i == 0 {
707 break;
708 }
709 i -= 1;
710 }
711 data[out + length] = Self::LAST_DIGIT_POS;
712 length += 1;
713 } else {
714 length = num_digits + has_extra_digit as usize;
715 if length == 2 {
717 length = 1;
718 }
719 data[out] = leading_digit_pos;
720 data[out + 1] = Self::POINT_POS;
721 let mut i = 2;
722 while i < length {
723 data[out + i] = leading_digit_pos + 1 - i as u8;
724 i += 1;
725 }
726 }
727 let mut i = 0;
728 while i < 4 {
729 data[out + length] = Self::EXP_POS + i;
730 length += 1;
731 i += 1;
732 }
733 data[out + 15] = length as u8;
734 idx += 1;
735 }
736
737 ExpFloatShuffleTable { data }
738 }
739}
740
741#[cfg(any(
742 not(any(
743 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
744 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
745 )),
746 all(test, target_endian = "little"),
747))]
748#[cfg_attr(feature = "no-panic", no_panic)]
749fn count_trailing_nonzeros(x: u64) -> usize {
750 (70 - ((x.to_le() << 1) | 1).leading_zeros() as usize) / 8
763}
764
765#[repr(C, align(2))]
768struct Digits2([u8; 200]);
769
770static DIGITS2: Digits2 = Digits2(
771 *b"0001020304050607080910111213141516171819\
772 2021222324252627282930313233343536373839\
773 4041424344454647484950515253545556575859\
774 6061626364656667686970717273747576777879\
775 8081828384858687888990919293949596979899",
776);
777
778#[cfg_attr(feature = "no-panic", no_panic)]
781unsafe fn digits2(value: usize) -> &'static u16 {
782 if true {
if !(value < 100) {
::core::panicking::panic("assertion failed: value < 100")
};
};debug_assert!(value < 100);
783
784 #[allow(clippy::cast_ptr_alignment)]
785 unsafe {
786 &*DIGITS2.0.as_ptr().cast::<u16>().add(value)
787 }
788}
789
790const DIV10K_EXP: i32 = 40;
791const DIV10K_SIG: u32 = ((1u64 << DIV10K_EXP) / 10000 + 1) as u32;
792const NEG10K: u32 = ((1u64 << 32) - 10000) as u32;
793
794const DIV100_EXP: i32 = 19;
795const DIV100_SIG: u32 = (1 << DIV100_EXP) / 100 + 1;
796#[cfg(not(all(
797 target_arch = "x86_64",
798 target_feature = "sse2",
799 not(target_feature = "sse4.1"),
800 not(miri)
801)))]
802const NEG100: u32 = (1 << 16) - 100;
803
804#[cfg(not(any(
805 all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
806 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
807)))]
808const DIV10_EXP: i32 = 10;
809#[cfg(not(any(
810 all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
811 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
812)))]
813const DIV10_SIG: u32 = (1 << DIV10_EXP) / 10 + 1;
814#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2", not(miri))))]
815const NEG10: u32 = (1 << 8) - 10;
816
817const ZEROS: u64 = 0x0101010101010101 * b'0' as u64;
818
819#[repr(C, align(64))]
820struct Data {
821 threshold: AArch64Mem<1_000_000_000_000_000>,
822 biased_half: AArch64Mem<{ (1 << 63) + 6 }>,
824
825 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
826 mul_const: u64,
827 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
828 hundred_million: u64,
829 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
830 multipliers32: int32x4_t,
831 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
832 multipliers16: int16x8_t,
833
834 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
837 div100: u128,
838 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
839 div10: u128,
840 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
841 neg100: u128,
842 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
843 neg10: u128,
844 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
845 bswap: u128,
846 #[cfg(all(
847 target_arch = "x86_64",
848 target_feature = "sse2",
849 not(target_feature = "sse4.1"),
850 not(miri)
851 ))]
852 hundred: u128,
853 #[cfg(all(
854 target_arch = "x86_64",
855 target_feature = "sse2",
856 not(target_feature = "sse4.1"),
857 not(miri)
858 ))]
859 moddiv10: u128,
860 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
861 div10k: u128,
862 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
863 neg10k: u128,
864 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
865 zeros: u128,
866
867 exp_shifts: ExpShiftTable,
868 exp_strings: ExpStringTable,
869 pow10_significands: Pow10SignificandTable,
870 exp_float_shuffles: ExpFloatShuffleTable,
871}
872
873impl Data {
874 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
875 const fn splat64(x: u64) -> u128 {
876 ((x as u128) << 64) | x as u128
877 }
878
879 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
880 const fn splat32(x: u32) -> u128 {
881 Self::splat64(((x as u64) << 32) | x as u64)
882 }
883
884 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
885 const fn splat16(x: u16) -> u128 {
886 Self::splat32(((x as u32) << 16) | x as u32)
887 }
888
889 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
890 const fn pack8(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8, g: u8, h: u8) -> u64 {
891 ((h as u64) << 56)
892 | ((g as u64) << 48)
893 | ((f as u64) << 40)
894 | ((e as u64) << 32)
895 | ((d as u64) << 24)
896 | ((c as u64) << 16)
897 | ((b as u64) << 8)
898 | a as u64
899 }
900
901 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
902 const NEG10K: i32 = 0x10000 - 10000;
903}
904
905static STATIC_DATA: Data = Data {
906 threshold: AArch64Mem::new(),
907 biased_half: AArch64Mem::new(),
908
909 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
910 mul_const: 0xabcc77118461cefd,
911 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
912 hundred_million: 100000000,
913 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
914 multipliers32: unsafe {
915 mem::transmute::<[i32; 4], int32x4_t>([
916 DIV10K_SIG as i32,
917 Data::NEG10K,
918 (DIV100_SIG << 12) as i32,
919 NEG100 as i32,
920 ])
921 },
922 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
923 multipliers16: unsafe {
924 mem::transmute::<[i16; 8], int16x8_t>([0xce0, NEG10 as i16, 0, 0, 0, 0, 0, 0])
925 },
926
927 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
928 div100: Data::splat32(DIV100_SIG),
929 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
930 div10: Data::splat16(((1u32 << 16) / 10 + 1) as u16),
931 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
932 neg100: Data::splat32(NEG100),
933 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
934 neg10: Data::splat16((1 << 8) - 10),
935 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
936 bswap: Data::pack8(15, 14, 13, 12, 11, 10, 9, 8) as u128
937 | (Data::pack8(7, 6, 5, 4, 3, 2, 1, 0) as u128) << 64,
938 #[cfg(all(
939 target_arch = "x86_64",
940 target_feature = "sse2",
941 not(target_feature = "sse4.1"),
942 not(miri)
943 ))]
944 hundred: Data::splat32(100),
945 #[cfg(all(
946 target_arch = "x86_64",
947 target_feature = "sse2",
948 not(target_feature = "sse4.1"),
949 not(miri)
950 ))]
951 moddiv10: Data::splat16(10 * (1 << 8) - 1),
952 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
953 div10k: Data::splat64(DIV10K_SIG as u64),
954 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
955 neg10k: Data::splat64(NEG10K as u64),
956 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
957 zeros: Data::splat64(ZEROS),
958
959 exp_shifts: ExpShiftTable::new(),
960 exp_strings: ExpStringTable::new(),
961 pow10_significands: Pow10SignificandTable::new(),
962 exp_float_shuffles: ExpFloatShuffleTable::new(),
963};
964
965#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
967#[cfg_attr(feature = "no-panic", no_panic)]
968fn to_bcd_4x4(mut efgh_abcd_mnop_ijkl: int32x4_t, d: &Data) -> uint8x16_t {
969 unsafe {
970 asm!("/*{:v}*/", inout(vreg) efgh_abcd_mnop_ijkl);
973
974 let ef_ab_mn_ij: int32x4_t = vqdmulhq_n_s32(
975 efgh_abcd_mnop_ijkl,
976 mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[2],
977 );
978 let gh_ef_cd_ab_op_mn_kl_ij: int16x8_t = vreinterpretq_s16_s32(vmlaq_n_s32(
979 efgh_abcd_mnop_ijkl,
980 ef_ab_mn_ij,
981 mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[3],
982 ));
983 let high_10s: int16x8_t = vqdmulhq_n_s16(
984 gh_ef_cd_ab_op_mn_kl_ij,
985 mem::transmute::<int16x8_t, [i16; 8]>(d.multipliers16)[0],
986 );
987 vreinterpretq_u8_s16(vmlaq_n_s16(
988 gh_ef_cd_ab_op_mn_kl_ij,
989 high_10s,
990 mem::transmute::<int16x8_t, [i16; 8]>(d.multipliers16)[1],
991 ))
992 }
993}
994
995#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
997#[cfg_attr(feature = "no-panic", no_panic)]
998#[inline]
999fn to_unshuffled_digits(value: u64, d: &Data) -> uint8x16_t {
1000 let mut hundred_million = d.hundred_million;
1001
1002 unsafe {
1004 asm!("/*{0}*/", inout(reg) hundred_million);
1005 }
1006
1007 let abcdefgh = (umul128(value, d.mul_const) >> 90) as u64;
1009 let ijklmnop = value - abcdefgh * hundred_million;
1010
1011 unsafe {
1012 let ijklmnop_abcdefgh_64: uint64x1_t =
1013 mem::transmute::<u64, uint64x1_t>((ijklmnop << 32) | abcdefgh);
1014 let abcdefgh_ijklmnop: int32x2_t = vreinterpret_s32_u64(ijklmnop_abcdefgh_64);
1015
1016 let abcd_ijkl: int32x2_t = vreinterpret_s32_u32(vshr_n_u32(
1017 vreinterpret_u32_s32(vqdmulh_n_s32(
1018 abcdefgh_ijklmnop,
1019 mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[0],
1020 )),
1021 9,
1022 ));
1023 let efgh_abcd_mnop_ijkl_32: int32x2_t = vmla_n_s32(
1024 abcdefgh_ijklmnop,
1025 abcd_ijkl,
1026 mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[1],
1027 );
1028
1029 let efgh_abcd_mnop_ijkl: int32x4_t =
1030 vreinterpretq_s32_u32(vshll_n_u16(vreinterpret_u16_s32(efgh_abcd_mnop_ijkl_32), 0));
1031
1032 to_bcd_4x4(efgh_abcd_mnop_ijkl, d)
1033 }
1034}
1035
1036#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
1039#[cfg_attr(feature = "no-panic", no_panic)]
1040fn to_bcd_4x4(y: __m128i, d: &Data) -> __m128i {
1041 unsafe {
1042 let div100 = _mm_load_si128(&raw const d.div100ptr::addr_of!(d.div100).cast::<__m128i>());
1043 let div10 = _mm_load_si128(&raw const d.div10ptr::addr_of!(d.div10).cast::<__m128i>());
1044
1045 #[cfg(target_feature = "sse4.1")]
1046 {
1047 let neg100 = _mm_load_si128(ptr::addr_of!(d.neg100).cast::<__m128i>());
1048 let neg10 = _mm_load_si128(ptr::addr_of!(d.neg10).cast::<__m128i>());
1049
1050 let z: __m128i = _mm_add_epi64(
1052 y,
1053 _mm_mullo_epi32(neg100, _mm_srli_epi32(_mm_mulhi_epu16(y, div100), 3)),
1054 );
1055 _mm_add_epi64(z, _mm_mullo_epi16(neg10, _mm_mulhi_epu16(z, div10)))
1056 }
1057
1058 #[cfg(not(target_feature = "sse4.1"))]
1059 {
1060 let hundred = _mm_load_si128(&raw const d.hundredptr::addr_of!(d.hundred).cast::<__m128i>());
1061 let moddiv10 = _mm_load_si128(&raw const d.moddiv10ptr::addr_of!(d.moddiv10).cast::<__m128i>());
1062
1063 let y_div_100: __m128i = _mm_srli_epi16(_mm_mulhi_epu16(y, div100), 3);
1064 let y_mod_100: __m128i = _mm_sub_epi16(y, _mm_mullo_epi16(y_div_100, hundred));
1065 let z: __m128i = _mm_or_si128(_mm_slli_epi32(y_mod_100, 16), y_div_100);
1066 _mm_sub_epi16(
1067 _mm_slli_epi16(z, 8),
1068 _mm_mullo_epi16(moddiv10, _mm_mulhi_epu16(z, div10)),
1069 )
1070 }
1071 }
1072}
1073
1074#[cfg(not(any(
1075 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1076 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1077)))]
1078struct BcdResult {
1079 bcd: u64,
1080 len: usize,
1081}
1082
1083#[cfg(not(any(
1084 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1085 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1086)))]
1087#[cfg_attr(feature = "no-panic", no_panic)]
1088fn to_bcd8(abcdefgh: u64) -> BcdResult {
1089 #[cfg(not(all(target_arch = "x86_64", target_feature = "sse2", not(miri))))]
1090 let bcd = {
1091 let abcd_efgh =
1099 abcdefgh + u64::from(NEG10K) * ((abcdefgh * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1100 let ab_cd_ef_gh = abcd_efgh
1101 + u64::from(NEG100)
1102 * (((abcd_efgh * u64::from(DIV100_SIG)) >> DIV100_EXP) & 0x7f0000007f);
1103 let a_b_c_d_e_f_g_h = ab_cd_ef_gh
1104 + u64::from(NEG10)
1105 * (((ab_cd_ef_gh * u64::from(DIV10_SIG)) >> DIV10_EXP) & 0xf000f000f000f);
1106 a_b_c_d_e_f_g_h.to_be()
1107 };
1108
1109 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
1110 let bcd = {
1111 let mut d = &raw const STATIC_DATAptr::addr_of!(STATIC_DATA);
1113 let d = unsafe {
1114 asm!("/*{0}*/", inout(reg) d);
1115 &*d
1116 };
1117
1118 let abcd_efgh = (abcdefgh << 32)
1121 - ((10000u64 << 32) - 1) * ((abcdefgh * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1122 let v: __m128i = to_bcd_4x4(_mm_set_epi64x(0, abcd_efgh as i64), d);
1123 (unsafe { _mm_cvtsi128_si64(v) }) as u64
1124 };
1125
1126 BcdResult {
1127 bcd,
1128 len: count_trailing_nonzeros(bcd),
1129 }
1130}
1131
1132struct DecDigits<Float: FloatTraits> {
1133 digits: Float::DecDigitsType,
1134 #[cfg(any(
1136 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1137 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1138 ))]
1139 unshuffled: Float::DecUnshuffledType,
1140 num_digits: usize,
1141}
1142
1143#[cfg_attr(feature = "no-panic", no_panic)]
1144#[inline]
1145fn to_digits_64(value: u64, #[allow(unused_variables)] d: &Data) -> DecDigits<f64> {
1146 #[cfg(not(any(
1147 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1148 all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
1149 )))]
1150 {
1151 let hi = (value / 100_000_000) as u32;
1152 let lo = (value % 100_000_000) as u32;
1153 let hi_bcd = to_bcd8(hi as u64);
1154 if lo == 0 {
1155 return DecDigits {
1156 digits: [hi_bcd.bcd + ZEROS, ZEROS],
1157 num_digits: hi_bcd.len,
1158 };
1159 }
1160 let lo_bcd = to_bcd8(lo as u64);
1161 DecDigits {
1162 digits: [hi_bcd.bcd + ZEROS, lo_bcd.bcd + ZEROS],
1163 num_digits: 8 + lo_bcd.len,
1164 }
1165 }
1166
1167 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
1168 {
1169 unsafe {
1170 let unshuffled_digits = to_unshuffled_digits(value, d);
1171 let digits: uint8x16_t = vrev64q_u8(unshuffled_digits);
1172 let str: uint16x8_t = vaddq_u16(
1173 vreinterpretq_u16_u8(digits),
1174 vreinterpretq_u16_s8(vdupq_n_s8(b'0' as i8)),
1175 );
1176 let is_not_zero: uint16x8_t =
1177 vreinterpretq_u16_u8(vcgtzq_s8(vreinterpretq_s8_u8(digits)));
1178 let nonzero_mask: u64 =
1179 vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(is_not_zero, 4)), 0);
1180 DecDigits {
1181 digits: str,
1182 unshuffled: (),
1183 num_digits: 16 - (nonzero_mask.leading_zeros() as usize >> 2),
1184 }
1185 }
1186 }
1187
1188 #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
1189 {
1190 let hi = (value / 100_000_000) as u32;
1191 let lo = (value % 100_000_000) as u32;
1192
1193 unsafe {
1194 let div10k = _mm_load_si128(&raw const d.div10kptr::addr_of!(d.div10k).cast::<__m128i>());
1195 let neg10k = _mm_load_si128(&raw const d.neg10kptr::addr_of!(d.neg10k).cast::<__m128i>());
1196 let x: __m128i = _mm_set_epi64x(i64::from(hi), i64::from(lo));
1197 #[cfg_attr(target_feature = "sse4.1", allow(unused_mut))]
1198 let mut y: __m128i = _mm_add_epi64(
1199 x,
1200 _mm_mul_epu32(neg10k, _mm_srli_epi64(_mm_mul_epu32(x, div10k), DIV10K_EXP)),
1201 );
1202
1203 #[cfg(not(target_feature = "sse4.1"))]
1205 {
1206 y = _mm_shuffle_epi32(y, _MM_SHUFFLE(0, 1, 2, 3));
1207 }
1208
1209 #[cfg_attr(not(target_feature = "sse4.1"), allow(unused_mut))]
1210 let mut bcd: __m128i = to_bcd_4x4(y, d);
1211 let zeros = _mm_load_si128(&raw const d.zerosptr::addr_of!(d.zeros).cast::<__m128i>());
1212
1213 let mask = _mm_movemask_epi8(_mm_cmpgt_epi8(bcd, _mm_setzero_si128())) as u64;
1217 let len = if falsecfg!(target_feature = "sse4.1") {
1220 16 - mask.trailing_zeros()
1221 } else {
1222 64 - mask.leading_zeros()
1223 };
1224
1225 #[cfg(target_feature = "sse4.1")]
1226 {
1227 bcd = _mm_shuffle_epi8(
1228 bcd,
1229 _mm_load_si128(ptr::addr_of!(d.bswap).cast::<__m128i>()),
1230 ); }
1232
1233 DecDigits {
1234 digits: _mm_or_si128(bcd, zeros),
1235 #[cfg(target_feature = "sse4.1")]
1236 unshuffled: (),
1237 num_digits: len as usize,
1238 }
1239 }
1240 }
1241}
1242
1243#[cfg_attr(feature = "no-panic", no_panic)]
1244#[inline]
1245fn to_digits_32(value: u64, #[allow(unused_variables)] d: &Data) -> DecDigits<f32> {
1246 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
1247 {
1248 let abcd_efgh = value + u64::from(NEG10K) * ((value * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1251 let bcd_xmm = to_bcd_4x4(_mm_set_epi64x(0, abcd_efgh as i64), d);
1252 let unshuffled_bcd = unsafe { _mm_cvtsi128_si64(bcd_xmm) } as u64;
1253 let len = if unshuffled_bcd != 0 {
1254 8 - unshuffled_bcd.trailing_zeros() / 8
1255 } else {
1256 0
1257 };
1258 DecDigits {
1259 digits: unshuffled_bcd.swap_bytes() + ZEROS,
1260 unshuffled: bcd_xmm,
1261 num_digits: len as usize,
1262 }
1263 }
1264
1265 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
1266 {
1267 let abcd_efgh = value + u64::from(NEG10K) * ((value * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1271 let unshuffled: uint8x16_t = unsafe {
1272 let input: int32x4_t =
1273 vcombine_s32(vreinterpret_s32_u64(vcreate_u64(abcd_efgh)), vdup_n_s32(0));
1274 to_bcd_4x4(input, d)
1275 };
1276 let unshuffled_bcd =
1277 unsafe { vget_lane_u64(vreinterpret_u64_u8(vget_low_u8(unshuffled)), 0) };
1278 let len = if unshuffled_bcd != 0 {
1279 8 - unshuffled_bcd.trailing_zeros() / 8
1280 } else {
1281 0
1282 };
1283 DecDigits {
1284 digits: unshuffled_bcd.swap_bytes() + ZEROS,
1285 unshuffled,
1286 num_digits: len as usize,
1287 }
1288 }
1289
1290 #[cfg(not(any(
1291 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1292 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1293 )))]
1294 {
1295 let result = to_bcd8(value);
1296 DecDigits {
1297 digits: result.bcd + ZEROS,
1298 num_digits: result.len,
1299 }
1300 }
1301}
1302
1303#[cfg_attr(feature = "no-panic", no_panic)]
1304unsafe fn write_exp_float_simd_32(
1305 buffer: *mut u8,
1306 dig: &DecDigits<f32>,
1307 last_digit: i32,
1308 has_last_digit: bool,
1309 has_extra_digit: bool,
1310 exp_data: u64,
1311 d: &Data,
1312) -> *mut u8 {
1313 let prefix = (u32::from(b'.') << 8) + u32::from(b'0') + last_digit as u32;
1318 #[cfg_attr(
1319 not(any(
1320 all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1321 all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1322 )),
1323 allow(unused_variables)
1324 )]
1325 let tail = exp_data | (u64::from(prefix) << 32);
1326 let entry = unsafe {
1327 d.exp_float_shuffles
1328 .get_entry(dig.num_digits as i32, has_last_digit, has_extra_digit)
1329 };
1330
1331 #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
1332 unsafe {
1333 let ascii: __m128i = _mm_or_si128(
1334 dig.unshuffled,
1335 _mm_load_si128(ptr::addr_of!(d.zeros).cast::<__m128i>()),
1336 );
1337 let src: __m128i = _mm_insert_epi64(ascii, tail as i64, 1);
1338 let shuffle: __m128i = _mm_load_si128(entry.shuffle.cast::<__m128i>());
1339 let out: __m128i = _mm_shuffle_epi8(src, shuffle);
1340 _mm_storeu_si128(buffer.cast::<__m128i>(), out);
1341 }
1342
1343 #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
1344 unsafe {
1345 let ascii: uint8x16_t = vorrq_u8(dig.unshuffled, vdupq_n_u8(b'0'));
1346 let src: uint8x16_t =
1347 vreinterpretq_u8_u64(vsetq_lane_u64(tail, vreinterpretq_u64_u8(ascii), 1));
1348 let shuffle: uint8x16_t = vld1q_u8(entry.shuffle);
1349 let out: uint8x16_t = vqtbl1q_u8(src, shuffle);
1350 vst1q_u8(buffer, out);
1351 }
1352
1353 let length = entry.length as usize - usize::from((exp_data & 0xff000000) == 0);
1354 unsafe { buffer.add(length) }
1355}
1356
1357struct ToDecimalResult {
1358 sig: i64,
1359 exp: i32,
1360 last_digit: u8,
1361 has_last_digit: bool,
1362}
1363
1364#[cfg_attr(feature = "no-panic", no_panic)]
1368#[inline]
1369fn to_decimal<Float, UInt>(bin_sig: UInt, raw_exp: i64, regular: bool, d: &Data) -> ToDecimalResult
1370where
1371 Float: FloatTraits,
1372 UInt: traits::UInt,
1373{
1374 let bin_exp = raw_exp - i64::from(Float::EXP_OFFSET);
1375 let num_bits = mem::size_of::<UInt>() as i32 * 8;
1376 const EXTRA_SHIFT: usize = ExpShiftTable::EXTRA_SHIFT;
1377
1378 if !regular {
1379 let dec_exp = compute_dec_exp(bin_exp as i32, false);
1380 let shift = compute_exp_shift(bin_exp as i32, dec_exp + 1).wrapping_add(EXTRA_SHIFT as u8);
1381 let pow10 = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) };
1382 let p = umul192_hi128(pow10.hi, pow10.lo, (bin_sig << shift).into());
1383
1384 let mut integral = p.hi >> EXTRA_SHIFT;
1385 let fractional = (p.hi << (64 - EXTRA_SHIFT)) | (p.lo >> EXTRA_SHIFT);
1386
1387 let half_ulp = pow10.hi >> (EXTRA_SHIFT + 1 - shift as usize);
1388 let round_up = half_ulp > u64::MAX - fractional;
1389 let round_down = (half_ulp >> 1) > fractional;
1390 integral += u64::from(round_up);
1391
1392 let mut digit = umul128_add_hi64(fractional, 10, (1 << 63) - 1) as i32;
1393 let lo = umul128_add_hi64(fractional.wrapping_sub(half_ulp >> 1), 10, !0) as i32;
1394 if digit < lo {
1395 digit = lo;
1396 }
1397 return ToDecimalResult {
1398 sig: integral as i64,
1399 exp: dec_exp,
1400 last_digit: digit as u8,
1401 has_last_digit: !(round_up || round_down),
1402 };
1403 }
1404
1405 const LOG10_2_SIG: u64 = 78_913;
1406 const LOG10_2_EXP: i32 = 18;
1407 #[allow(unused_mut)]
1408 let mut dec_exp = if USE_UMUL128_HI64 {
1409 umul128_hi64(bin_exp as u64, LOG10_2_SIG << (64 - LOG10_2_EXP)) as i32
1410 } else {
1411 compute_dec_exp(bin_exp as i32, true)
1412 };
1413 #[cfg(not(miri))]
1414 #[allow(unused_unsafe)]
1415 unsafe {
1416 #[cfg(target_arch = "x86_64")]
1418 asm!("/*{0:e}*/", inout(reg) dec_exp);
1419 #[cfg(target_arch = "aarch64")]
1420 asm!("/*{0:w}*/", inout(reg) dec_exp);
1421 }
1422 let mut shift = if ExpShiftTable::ENABLE {
1423 *unsafe {
1424 d.exp_shifts
1425 .data
1426 .get_unchecked((bin_exp + i64::from(f64::EXP_OFFSET)) as usize)
1427 }
1428 } else {
1429 compute_exp_shift(bin_exp as i32, dec_exp + 1).wrapping_add(EXTRA_SHIFT as u8)
1430 };
1431 let even = UInt::from(1) - (bin_sig & UInt::from(1));
1432
1433 if num_bits == 32 {
1434 const EXTRA_SHIFT: usize = 34;
1435 shift += (EXTRA_SHIFT - ExpShiftTable::EXTRA_SHIFT) as u8;
1436 let pow10_hi = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) }.hi;
1437 let p = umul128_hi64(pow10_hi + 1, bin_sig.into() << shift);
1438
1439 let mut integral = p >> EXTRA_SHIFT;
1440 let fractional = p & ((1u64 << EXTRA_SHIFT) - 1);
1441
1442 let half_ulp = (pow10_hi >> (65 - shift as usize)) + even.into();
1443 let round_up = ((fractional + half_ulp) >> EXTRA_SHIFT) != 0;
1444 let round_down = half_ulp > fractional;
1445 integral += u64::from(round_up);
1446
1447 let mut digit = ((fractional * 10 + (1u64 << (EXTRA_SHIFT - 1))) >> EXTRA_SHIFT) as i32;
1448 if fractional == (1u64 << (EXTRA_SHIFT - 2)) {
1449 digit = 2; }
1451 return ToDecimalResult {
1452 sig: integral as i64,
1453 exp: dec_exp,
1454 last_digit: digit as u8,
1455 has_last_digit: !(round_up || round_down),
1456 };
1457 }
1458
1459 let pow10 = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) };
1483 let p = umul192_hi128(pow10.hi, pow10.lo, (bin_sig << shift).into());
1484
1485 let mut integral = p.hi >> EXTRA_SHIFT;
1486 let fractional = (p.hi << (64 - EXTRA_SHIFT)) | (p.lo >> EXTRA_SHIFT);
1487
1488 let half_ulp = (pow10.hi >> (EXTRA_SHIFT + 1 - shift as usize)) + even.into();
1489 let round_up = fractional.wrapping_add(half_ulp) < fractional;
1490 let round_down = half_ulp > fractional;
1491 integral += u64::from(round_up); let mut digit = umul128_add_hi64(fractional, 10, d.biased_half.get()) as i32;
1495 if fractional == (1u64 << 62) {
1496 digit = 2; }
1498 ToDecimalResult {
1499 sig: integral as i64,
1500 exp: dec_exp,
1501 last_digit: digit as u8,
1502 has_last_digit: !(round_up || round_down),
1503 }
1504}
1505
1506#[cfg_attr(feature = "no-panic", no_panic)]
1509unsafe fn write<Float>(value: Float, mut buffer: *mut u8) -> *mut u8
1510where
1511 Float: FloatTraits,
1512{
1513 let bits = value.to_bits();
1514 let bin_exp = Float::get_exp(bits); let bin_sig = Float::get_sig(bits); unsafe {
1519 *buffer = b'-';
1520 }
1521 buffer = unsafe { buffer.add(usize::from(Float::is_negative(bits))) };
1522
1523 #[allow(unused_mut)]
1524 let mut d = &raw const STATIC_DATAptr::addr_of!(STATIC_DATA);
1525 let d = unsafe {
1526 #[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
1528 asm!("/*{0}*/", inout(reg) d);
1529 &*d
1530 };
1531 let threshold = if Float::NUM_BITS == 64 {
1532 d.threshold.get()
1533 } else {
1534 10_000_000
1535 };
1536
1537 let mut dec;
1538 if bin_exp == 0 {
1539 if bin_sig == Float::SigType::from(0) {
1540 return unsafe {
1541 *buffer = b'0';
1542 *buffer.add(1) = b'.';
1543 *buffer.add(2) = b'0';
1544 buffer.add(3)
1545 };
1546 }
1547 dec = to_decimal::<Float, Float::SigType>(bin_sig, 1, true, d);
1548 let mut dec_sig =
1549 dec.sig * 10 + (-i64::from(dec.has_last_digit) & i64::from(dec.last_digit));
1550 let mut dec_exp = dec.exp;
1551 while dec_sig < threshold as i64 {
1552 dec_sig *= 10;
1553 dec_exp -= 1;
1554 }
1555 let d = div10(dec_sig as u64);
1556 let last_digit = dec_sig - d as i64 * 10;
1557 dec = ToDecimalResult {
1558 sig: d as i64,
1559 exp: dec_exp,
1560 last_digit: last_digit as u8,
1561 has_last_digit: last_digit != 0,
1562 };
1563 } else {
1564 dec = to_decimal::<Float, Float::SigType>(
1565 bin_sig | Float::IMPLICIT_BIT,
1566 bin_exp,
1567 bin_sig != Float::SigType::from(0),
1568 d,
1569 );
1570 }
1571 let mut has_last_digit = dec.has_last_digit;
1572 let has_extra_digit = dec.sig >= threshold as i64;
1573 let mut dec_exp = dec.exp + Float::MAX_DIGITS10 as i32 - 2 + i32::from(has_extra_digit);
1574 if Float::NUM_BITS == 32 && dec.sig < 1_000_000 {
1575 dec.sig = 10 * dec.sig + (-i64::from(has_last_digit) & i64::from(dec.last_digit));
1576 has_last_digit = false;
1577 dec_exp -= 1;
1578 }
1579
1580 let dig = Float::to_digits(dec.sig as u64, d);
1582
1583 if Float::NUM_BITS == 32
1584 && ExpFloatShuffleTable::ENABLE
1585 && !Float::FIXED_DEC_EXP.contains(&dec_exp)
1586 {
1587 unsafe {
1588 let exp_data = *d
1589 .exp_strings
1590 .data
1591 .get_unchecked((dec_exp + ExpStringTable::OFFSET) as usize);
1592 return Float::write_exp_float_simd(
1593 buffer,
1594 &dig,
1595 i32::from(dec.last_digit),
1596 has_last_digit,
1597 has_extra_digit,
1598 exp_data,
1599 d,
1600 );
1601 }
1602 }
1603
1604 let bcd_size = if Float::NUM_BITS == 64 { 16 } else { 8 };
1605 unsafe {
1606 buffer
1607 .add(usize::from(has_extra_digit))
1608 .cast::<Float::DecDigitsType>()
1609 .write_unaligned(dig.digits);
1610 buffer
1611 .add(usize::from(has_extra_digit) + bcd_size)
1612 .write(b'0' + dec.last_digit);
1613 }
1614 let length = usize::from(has_extra_digit)
1615 + if has_last_digit {
1616 bcd_size + 1
1617 } else {
1618 dig.num_digits
1619 }
1620 - 1;
1621
1622 if Float::FIXED_DEC_EXP.contains(&dec_exp) {
1623 if length as i32 - 1 <= dec_exp {
1624 return unsafe {
1626 ptr::copy(buffer.add(1), buffer, length);
1627 ptr::write_bytes(buffer.add(length), b'0', dec_exp as usize + 3 - length);
1628 *buffer.add(dec_exp as usize + 1) = b'.';
1629 buffer.add(dec_exp as usize + 3)
1630 };
1631 } else if 0 <= dec_exp {
1632 return unsafe {
1634 ptr::copy(buffer.add(1), buffer, dec_exp as usize + 1);
1635 *buffer.add(dec_exp as usize + 1) = b'.';
1636 buffer.add(length + 1)
1637 };
1638 } else {
1639 return unsafe {
1641 ptr::copy(buffer.add(1), buffer.add((1 - dec_exp) as usize), length);
1642 ptr::write_bytes(buffer, b'0', (1 - dec_exp) as usize);
1643 *buffer.add(1) = b'.';
1644 buffer.add((1 - dec_exp) as usize + length)
1645 };
1646 }
1647 }
1648
1649 unsafe {
1650 *buffer = *buffer.add(1);
1652 *buffer.add(1) = b'.';
1653 }
1654 buffer = unsafe { buffer.add(length + usize::from(length > 1)) };
1655
1656 if ExpStringTable::ENABLE {
1658 let mut exp_data = unsafe {
1659 *d.exp_strings
1660 .data
1661 .get_unchecked((dec_exp + ExpStringTable::OFFSET) as usize)
1662 };
1663 let len = (exp_data >> 48) as usize;
1664 exp_data = exp_data.to_le();
1665 unsafe {
1666 ptr::copy_nonoverlapping(
1667 &raw const exp_dataptr::addr_of!(exp_data).cast::<u8>(),
1668 buffer,
1669 if Float::MAX_10_EXP >= 100 { 5 } else { 4 },
1670 );
1671 return buffer.add(len);
1672 }
1673 }
1674 let sign_ptr = buffer;
1675 let e_sign = if dec_exp >= 0 {
1676 (u16::from(b'+') << 8) | u16::from(b'e')
1677 } else {
1678 (u16::from(b'-') << 8) | u16::from(b'e')
1679 };
1680 buffer = unsafe { buffer.add(1) };
1681 dec_exp = if dec_exp >= 0 { dec_exp } else { -dec_exp };
1682 buffer = unsafe { buffer.add(usize::from(dec_exp >= 10)) };
1683 if Float::MAX_10_EXP >= 100 {
1684 let digit = if USE_UMUL128_HI64 {
1686 umul128_hi64(dec_exp as u64, 0x290000000000000) as u32
1687 } else {
1688 (dec_exp as u32 * DIV100_SIG) >> DIV100_EXP
1689 };
1690 unsafe {
1691 *buffer = b'0' + digit as u8;
1692 }
1693 buffer = unsafe { buffer.add(usize::from(dec_exp >= 100)) };
1694 dec_exp -= (digit * 100) as i32;
1695 }
1696 unsafe {
1697 buffer
1698 .cast::<u16>()
1699 .write_unaligned(*digits2(dec_exp as usize));
1700 sign_ptr.cast::<u16>().write_unaligned(e_sign.to_le());
1701 buffer.add(2)
1702 }
1703}
1704
1705pub struct Buffer {
1715 bytes: [MaybeUninit<u8>; BUFFER_SIZE],
1716}
1717
1718impl Buffer {
1719 #[inline]
1722 #[cfg_attr(feature = "no-panic", no_panic)]
1723 pub fn new() -> Self {
1724 let bytes = [MaybeUninit::<u8>::uninit(); BUFFER_SIZE];
1725 Buffer { bytes }
1726 }
1727
1728 #[cfg_attr(feature = "no-panic", no_panic)]
1740 pub fn format<F: Float>(&mut self, f: F) -> &str {
1741 if f.is_nonfinite() {
1742 f.format_nonfinite()
1743 } else {
1744 self.format_finite(f)
1745 }
1746 }
1747
1748 #[cfg_attr(feature = "no-panic", no_panic)]
1764 pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
1765 unsafe {
1766 let end = f.write_to_zmij_buffer(self.bytes.as_mut_ptr().cast::<u8>());
1767 let len = end.offset_from(self.bytes.as_ptr().cast::<u8>()) as usize;
1768 let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::<u8>(), len);
1769 str::from_utf8_unchecked(slice)
1770 }
1771 }
1772}
1773
1774#[allow(unknown_lints)] #[allow(private_bounds)]
1781pub trait Float: private::Sealed {}
1782impl Float for f32 {}
1783impl Float for f64 {}
1784
1785mod private {
1786 pub trait Sealed: crate::traits::Float {
1787 fn is_nonfinite(self) -> bool;
1788 fn format_nonfinite(self) -> &'static str;
1789 unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8;
1790 }
1791
1792 impl Sealed for f32 {
1793 #[inline]
1794 fn is_nonfinite(self) -> bool {
1795 const EXP_MASK: u32 = 0x7f800000;
1796 let bits = self.to_bits();
1797 bits & EXP_MASK == EXP_MASK
1798 }
1799
1800 #[cold]
1801 #[cfg_attr(feature = "no-panic", inline)]
1802 fn format_nonfinite(self) -> &'static str {
1803 const MANTISSA_MASK: u32 = 0x007fffff;
1804 const SIGN_MASK: u32 = 0x80000000;
1805 let bits = self.to_bits();
1806 if bits & MANTISSA_MASK != 0 {
1807 crate::NAN
1808 } else if bits & SIGN_MASK != 0 {
1809 crate::NEG_INFINITY
1810 } else {
1811 crate::INFINITY
1812 }
1813 }
1814
1815 #[cfg_attr(feature = "no-panic", inline)]
1816 unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1817 unsafe { crate::write(self, buffer) }
1818 }
1819 }
1820
1821 impl Sealed for f64 {
1822 #[inline]
1823 fn is_nonfinite(self) -> bool {
1824 const EXP_MASK: u64 = 0x7ff0000000000000;
1825 let bits = self.to_bits();
1826 bits & EXP_MASK == EXP_MASK
1827 }
1828
1829 #[cold]
1830 #[cfg_attr(feature = "no-panic", inline)]
1831 fn format_nonfinite(self) -> &'static str {
1832 const MANTISSA_MASK: u64 = 0x000fffffffffffff;
1833 const SIGN_MASK: u64 = 0x8000000000000000;
1834 let bits = self.to_bits();
1835 if bits & MANTISSA_MASK != 0 {
1836 crate::NAN
1837 } else if bits & SIGN_MASK != 0 {
1838 crate::NEG_INFINITY
1839 } else {
1840 crate::INFINITY
1841 }
1842 }
1843
1844 #[cfg_attr(feature = "no-panic", inline)]
1845 unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1846 unsafe { crate::write(self, buffer) }
1847 }
1848 }
1849}
1850
1851impl Default for Buffer {
1852 #[inline]
1853 #[cfg_attr(feature = "no-panic", no_panic)]
1854 fn default() -> Self {
1855 Buffer::new()
1856 }
1857}