├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── performance.png ├── chart ├── .gitignore └── performance.tex ├── tests ├── macros │ └── mod.rs └── binary64_test.rs ├── Cargo.toml ├── benches └── bench.rs ├── LICENSE-Boost ├── src ├── wuint.rs ├── buffer.rs ├── div.rs ├── log.rs ├── to_chars.rs ├── lib.rs └── cache.rs ├── README.md └── LICENSE-Apache2-LLVM /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dtolnay 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Cargo.lock 2 | /target/ 3 | -------------------------------------------------------------------------------- /performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtolnay/dragonbox/HEAD/performance.png -------------------------------------------------------------------------------- /chart/.gitignore: -------------------------------------------------------------------------------- 1 | /*.aux 2 | /*.fdb_latexmk 3 | /*.fls 4 | /*.log 5 | /*.pdf 6 | /*.png 7 | /*.svg 8 | -------------------------------------------------------------------------------- /tests/macros/mod.rs: -------------------------------------------------------------------------------- 1 | macro_rules! check { 2 | ($f:tt) => { 3 | assert_eq!(to_chars($f), stringify!($f)); 4 | }; 5 | (-$f:tt) => { 6 | assert_eq!(to_chars(-$f), concat!("-", stringify!($f))); 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dragonbox" 3 | version = "0.1.10" 4 | authors = ["David Tolnay "] 5 | categories = ["value-formatting", "no-std", "no-std::no-alloc"] 6 | description = "Fast floating point to string conversion" 7 | documentation = "https://docs.rs/dragonbox" 8 | edition = "2021" 9 | exclude = ["performance.png", "chart/**"] 10 | keywords = ["float"] 11 | license = "Apache-2.0 WITH LLVM-exception OR BSL-1.0" 12 | repository = "https://github.com/dtolnay/dragonbox" 13 | rust-version = "1.68" 14 | 15 | [dev-dependencies] 16 | rand = "0.9" 17 | 18 | [target.'cfg(not(miri))'.dev-dependencies] 19 | criterion = { version = "0.8", default-features = false } 20 | 21 | [[bench]] 22 | name = "bench" 23 | harness = false 24 | 25 | [package.metadata.docs.rs] 26 | targets = ["x86_64-unknown-linux-gnu"] 27 | rustdoc-args = [ 28 | "--generate-link-to-definition", 29 | "--generate-macro-expansion", 30 | "--extern-html-root-url=core=https://doc.rust-lang.org", 31 | "--extern-html-root-url=alloc=https://doc.rust-lang.org", 32 | "--extern-html-root-url=std=https://doc.rust-lang.org", 33 | ] 34 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use std::f64; 3 | use std::hint; 4 | use std::io::Write; 5 | 6 | fn do_bench(c: &mut Criterion, group_name: &str, float: f64) { 7 | let mut group = c.benchmark_group(group_name); 8 | group.bench_function("dragonbox", |b| { 9 | let mut buf = dragonbox::Buffer::new(); 10 | b.iter(move || { 11 | let float = hint::black_box(float); 12 | let formatted = buf.format_finite(float); 13 | hint::black_box(formatted); 14 | }); 15 | }); 16 | group.bench_function("std::fmt", |b| { 17 | let mut buf = Vec::with_capacity(20); 18 | b.iter(|| { 19 | buf.clear(); 20 | let float = hint::black_box(float); 21 | write!(&mut buf, "{float}").unwrap(); 22 | hint::black_box(buf.as_slice()); 23 | }); 24 | }); 25 | group.finish(); 26 | } 27 | 28 | fn bench(c: &mut Criterion) { 29 | do_bench(c, "f64[0]", 0f64); 30 | do_bench(c, "f64[short]", 0.1234f64); 31 | do_bench(c, "f64[e]", f64::consts::E); 32 | do_bench(c, "f64[max]", f64::MAX); 33 | } 34 | 35 | criterion_group!(benches, bench); 36 | criterion_main!(benches); 37 | -------------------------------------------------------------------------------- /LICENSE-Boost: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/wuint.rs: -------------------------------------------------------------------------------- 1 | // Translated from C++ to Rust. The original C++ code can be found at 2 | // https://github.com/jk-jeon/dragonbox and carries the following license: 3 | // 4 | // Copyright 2020-2021 Junekey Jeon 5 | // 6 | // The contents of this file may be used under the terms of 7 | // the Apache License v2.0 with LLVM Exceptions. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | use crate::cache::EntryTypeExt as _; 22 | 23 | pub(crate) fn umul128_upper64(x: u64, y: u64) -> u64 { 24 | let p = x as u128 * y as u128; 25 | (p >> 64) as u64 26 | } 27 | 28 | pub(crate) fn umul192_upper64(x: u64, y: u128) -> u64 { 29 | let mut g0 = x as u128 * y.high() as u128; 30 | g0 += umul128_upper64(x, y.low()) as u128; 31 | g0.high() 32 | } 33 | 34 | pub(crate) fn umul192_middle64(x: u64, y: u128) -> u64 { 35 | let g01 = x.wrapping_mul(y.high()); 36 | let g10 = umul128_upper64(x, y.low()); 37 | g01.wrapping_add(g10) 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dragonbox 2 | 3 | [github](https://github.com/dtolnay/dragonbox) 4 | [crates.io](https://crates.io/crates/dragonbox) 5 | [docs.rs](https://docs.rs/dragonbox) 6 | [build status](https://github.com/dtolnay/dragonbox/actions?query=branch%3Amaster) 7 | 8 | This crate contains a basic port of to 9 | Rust for benchmarking purposes. 10 | 11 | Please see the upstream repo for an explanation of the approach and comparison 12 | to the Ryū algorithm. 13 | 14 |
15 | 16 | ## Example 17 | 18 | ```rust 19 | fn main() { 20 | let mut buffer = dragonbox::Buffer::new(); 21 | let printed = buffer.format(1.234); 22 | assert_eq!(printed, "1.234E0"); 23 | } 24 | ``` 25 | 26 |
27 | 28 | ## Performance (lower is better) 29 | 30 | ![performance](https://raw.githubusercontent.com/dtolnay/dragonbox/master/performance.png) 31 | 32 |
33 | 34 | #### License 35 | 36 | 37 | Licensed under either of Apache License, Version 38 | 2.0 with LLVM Exceptions or Boost Software License 39 | Version 1.0 at your option. 40 | 41 | 42 |
43 | 44 | 45 | Unless you explicitly state otherwise, any contribution intentionally submitted 46 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 47 | be dual licensed as above, without any additional terms or conditions. 48 | 49 | -------------------------------------------------------------------------------- /chart/performance.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | \usepackage{pgfplots} 3 | \usepackage{sansmath} 4 | \pgfplotsset{compat=1.16} 5 | \definecolor{dragonbox}{HTML}{3366FF} 6 | \definecolor{std}{HTML}{949494} 7 | \definecolor{bg}{HTML}{CFCFCF} 8 | \begin{document} 9 | \pagecolor{white} 10 | \begin{tikzpicture} 11 | \edef\entries{ 12 | "$0.0$", 13 | "$0.1234$", 14 | "$2.718281828459045$", 15 | "$1.7976931348623157e308$", 16 | } 17 | \begin{axis}[ 18 | width=5in, 19 | height=3.5in, 20 | ybar, 21 | ymin=0, 22 | bar width=24pt, 23 | enlarge x limits={abs=39pt}, 24 | ylabel={nanos for one call to write}, 25 | legend style={ 26 | anchor=north west, 27 | at={(0.025,0.975)}, 28 | legend columns=1, 29 | draw=none, 30 | fill=none, 31 | }, 32 | legend entries={ 33 | dragonbox::Buffer::new().format\_finite(value)\\ 34 | std::write!(\&mut buf, ``\{\}'', value)\\ 35 | }, 36 | legend cell align=left, 37 | xtick={-0.5,0.5,1.5,2.5,3.5,4.5}, 38 | xticklabels={}, 39 | xtick pos=left, 40 | visualization depends on={y \as \rawy}, 41 | every node near coord/.append style={ 42 | shift={(axis direction cs:0,-\rawy/2)}, 43 | rotate=90, 44 | anchor=center, 45 | font=\sansmath\sffamily, 46 | }, 47 | axis background/.style={fill=bg}, 48 | tick label style={font=\sansmath\sffamily}, 49 | every axis label={font=\sansmath\sffamily}, 50 | legend style={font=\sansmath\sffamily}, 51 | label style={font=\sansmath\sffamily}, 52 | ] 53 | \addplot[ 54 | black, 55 | fill=dragonbox, 56 | area legend, 57 | nodes near coords={}, 58 | ] coordinates { 59 | (0, 2) 60 | (1, 14) 61 | (2, 17) 62 | (3, 21) 63 | }; 64 | \addplot[ 65 | black, 66 | fill=std, 67 | area legend, 68 | nodes near coords=\pgfmathsetmacro{\input}{{\entries}[\coordindex]}\input, 69 | ] coordinates { 70 | (0, 20) 71 | (1, 66) 72 | (2, 88) 73 | (3, 114) 74 | }; 75 | \end{axis} 76 | \pgfresetboundingbox\path 77 | (current axis.south west) -- ++(-0.44in,-0.09in) 78 | rectangle (current axis.north east) -- ++(0.05in,0.05in); 79 | \end{tikzpicture} 80 | \end{document} 81 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: [cron: "40 1 * * *"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | env: 13 | RUSTFLAGS: -Dwarnings 14 | 15 | jobs: 16 | pre_ci: 17 | uses: dtolnay/.github/.github/workflows/pre_ci.yml@master 18 | 19 | test: 20 | name: Rust ${{matrix.rust}} 21 | needs: pre_ci 22 | if: needs.pre_ci.outputs.continue 23 | runs-on: ubuntu-latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | rust: [nightly, beta, stable, 1.86.0] 28 | timeout-minutes: 45 29 | steps: 30 | - uses: actions/checkout@v6 31 | - uses: dtolnay/rust-toolchain@master 32 | with: 33 | toolchain: ${{matrix.rust}} 34 | - name: Enable type layout randomization 35 | run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV 36 | if: matrix.rust == 'nightly' 37 | - run: cargo test --release 38 | - run: cargo test 39 | - uses: actions/upload-artifact@v6 40 | if: matrix.rust == 'nightly' && always() 41 | with: 42 | name: Cargo.lock 43 | path: Cargo.lock 44 | continue-on-error: true 45 | 46 | msrv: 47 | name: Rust 1.68.0 48 | needs: pre_ci 49 | if: needs.pre_ci.outputs.continue 50 | runs-on: ubuntu-latest 51 | timeout-minutes: 45 52 | steps: 53 | - uses: actions/checkout@v6 54 | - uses: dtolnay/rust-toolchain@1.68.0 55 | - run: cargo build 56 | 57 | doc: 58 | name: Documentation 59 | needs: pre_ci 60 | if: needs.pre_ci.outputs.continue 61 | runs-on: ubuntu-latest 62 | timeout-minutes: 45 63 | env: 64 | RUSTDOCFLAGS: -Dwarnings 65 | steps: 66 | - uses: actions/checkout@v6 67 | - uses: dtolnay/rust-toolchain@nightly 68 | - uses: dtolnay/install@cargo-docs-rs 69 | - run: cargo docs-rs 70 | 71 | miri: 72 | name: Miri 73 | needs: pre_ci 74 | if: needs.pre_ci.outputs.continue 75 | runs-on: ubuntu-latest 76 | timeout-minutes: 45 77 | steps: 78 | - uses: actions/checkout@v6 79 | - uses: dtolnay/rust-toolchain@miri 80 | - run: cargo miri setup 81 | - run: cargo miri test 82 | env: 83 | MIRIFLAGS: -Zmiri-strict-provenance 84 | 85 | clippy: 86 | name: Clippy 87 | runs-on: ubuntu-latest 88 | if: github.event_name != 'pull_request' 89 | timeout-minutes: 45 90 | steps: 91 | - uses: actions/checkout@v6 92 | - uses: dtolnay/rust-toolchain@clippy 93 | - run: cargo clippy --tests --benches -- -Dclippy::all -Dclippy::pedantic 94 | 95 | outdated: 96 | name: Outdated 97 | runs-on: ubuntu-latest 98 | if: github.event_name != 'pull_request' 99 | timeout-minutes: 45 100 | steps: 101 | - uses: actions/checkout@v6 102 | - uses: dtolnay/rust-toolchain@stable 103 | - uses: dtolnay/install@cargo-outdated 104 | - run: cargo outdated --workspace --exit-code 1 105 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | use crate::{Buffer, Float}; 2 | use core::mem::MaybeUninit; 3 | use core::slice; 4 | use core::str; 5 | 6 | impl Buffer { 7 | /// This is a cheap operation; you don't need to worry about reusing buffers 8 | /// for efficiency. 9 | #[inline] 10 | pub fn new() -> Self { 11 | let bytes = [MaybeUninit::::uninit(); 24]; 12 | Buffer { bytes } 13 | } 14 | 15 | /// Print a floating point number into this buffer and return a reference to 16 | /// its string representation within the buffer. 17 | /// 18 | /// # Special cases 19 | /// 20 | /// This function formats NaN as the string "NaN", positive infinity as 21 | /// "inf", and negative infinity as "-inf" to match std::fmt. 22 | /// 23 | /// If your input is known to be finite, you may get better performance by 24 | /// calling the `format_finite` method instead of `format` to avoid the 25 | /// checks for special cases. 26 | pub fn format(&mut self, f: F) -> &str { 27 | if f.is_nonfinite() { 28 | f.format_nonfinite() 29 | } else { 30 | self.format_finite(f) 31 | } 32 | } 33 | 34 | /// Print a floating point number into this buffer and return a reference to 35 | /// its string representation within the buffer. 36 | /// 37 | /// # Special cases 38 | /// 39 | /// This function **does not** check for NaN or infinity. If the input 40 | /// number is not a finite float, the printed representation will be some 41 | /// correctly formatted but unspecified numerical value. 42 | /// 43 | /// Please check [`is_finite`] yourself before calling this function, or 44 | /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself. 45 | /// 46 | /// [`is_finite`]: f64::is_finite 47 | /// [`is_nan`]: f64::is_nan 48 | /// [`is_infinite`]: f64::is_infinite 49 | #[inline] 50 | pub fn format_finite(&mut self, f: F) -> &str { 51 | unsafe { 52 | let n = f.write_to_dragonbox_buffer(self.bytes.as_mut_ptr().cast::()); 53 | debug_assert!(n <= self.bytes.len()); 54 | let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::(), n); 55 | str::from_utf8_unchecked(slice) 56 | } 57 | } 58 | } 59 | 60 | impl Copy for Buffer {} 61 | 62 | #[allow(clippy::non_canonical_clone_impl)] 63 | impl Clone for Buffer { 64 | #[inline] 65 | fn clone(&self) -> Self { 66 | Buffer::new() 67 | } 68 | } 69 | 70 | impl Default for Buffer { 71 | #[inline] 72 | fn default() -> Self { 73 | Buffer::new() 74 | } 75 | } 76 | 77 | impl Float for f64 {} 78 | 79 | const NAN: &str = "NaN"; 80 | const INFINITY: &str = "inf"; 81 | const NEG_INFINITY: &str = "-inf"; 82 | 83 | pub trait Sealed: Copy { 84 | fn is_nonfinite(self) -> bool; 85 | fn format_nonfinite(self) -> &'static str; 86 | unsafe fn write_to_dragonbox_buffer(self, result: *mut u8) -> usize; 87 | } 88 | 89 | impl Sealed for f64 { 90 | #[inline] 91 | fn is_nonfinite(self) -> bool { 92 | const EXP_MASK: u64 = 0x7ff0000000000000; 93 | let bits = self.to_bits(); 94 | bits & EXP_MASK == EXP_MASK 95 | } 96 | 97 | #[cold] 98 | fn format_nonfinite(self) -> &'static str { 99 | const MANTISSA_MASK: u64 = 0x000fffffffffffff; 100 | const SIGN_MASK: u64 = 0x8000000000000000; 101 | let bits = self.to_bits(); 102 | if bits & MANTISSA_MASK != 0 { 103 | NAN 104 | } else if bits & SIGN_MASK != 0 { 105 | NEG_INFINITY 106 | } else { 107 | INFINITY 108 | } 109 | } 110 | 111 | #[inline] 112 | unsafe fn write_to_dragonbox_buffer(self, buffer: *mut u8) -> usize { 113 | let end = crate::to_chars::to_chars(self, buffer); 114 | end.offset_from(buffer) as usize 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/div.rs: -------------------------------------------------------------------------------- 1 | // Translated from C++ to Rust. The original C++ code can be found at 2 | // https://github.com/jk-jeon/dragonbox and carries the following license: 3 | // 4 | // Copyright 2020-2021 Junekey Jeon 5 | // 6 | // The contents of this file may be used under the terms of 7 | // the Apache License v2.0 with LLVM Exceptions. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | use crate::{CarrierUint, CARRIER_BITS}; 22 | 23 | const fn modular_inverse() -> CarrierUint { 24 | // By Euler's theorem, a^phi(2^n) == 1 (mod 2^n), 25 | // where phi(2^n) = 2^(n-1), so the modular inverse of a is 26 | // a^(2^(n-1) - 1) = a^(1 + 2 + 2^2 + ... + 2^(n-2)). 27 | let mut mod_inverse: CarrierUint = 1; 28 | let mut i = 1; 29 | while i < CARRIER_BITS { 30 | mod_inverse = mod_inverse.wrapping_mul(mod_inverse).wrapping_mul(A); 31 | i += 1; 32 | } 33 | mod_inverse 34 | } 35 | 36 | struct Table { 37 | //assert(a % 2 != 0); 38 | //assert(N > 0); 39 | mod_inv: [CarrierUint; N], 40 | max_quotients: [CarrierUint; N], 41 | } 42 | 43 | impl Table { 44 | const TABLE: Self = { 45 | let mod_inverse = modular_inverse::(); 46 | let mut mod_inv = [0; N]; 47 | let mut max_quotients = [0; N]; 48 | let mut pow_of_mod_inverse: CarrierUint = 1; 49 | let mut pow_of_a = 1; 50 | let mut i = 0; 51 | while i < N { 52 | mod_inv[i] = pow_of_mod_inverse; 53 | max_quotients[i] = CarrierUint::MAX / pow_of_a; 54 | 55 | pow_of_mod_inverse = pow_of_mod_inverse.wrapping_mul(mod_inverse); 56 | pow_of_a *= A; 57 | i += 1; 58 | } 59 | Table { 60 | mod_inv, 61 | max_quotients, 62 | } 63 | }; 64 | } 65 | 66 | pub(crate) unsafe fn divisible_by_power_of_5( 67 | x: CarrierUint, 68 | exp: u32, 69 | ) -> bool { 70 | let table = &Table::<5, TABLE_SIZE>::TABLE; 71 | debug_assert!((exp as usize) < TABLE_SIZE); 72 | (x * *table.mod_inv.get_unchecked(exp as usize)) 73 | <= *table.max_quotients.get_unchecked(exp as usize) 74 | } 75 | 76 | pub(crate) fn divisible_by_power_of_2(x: CarrierUint, exp: u32) -> bool { 77 | debug_assert!(exp >= 1); 78 | debug_assert!(x != 0); 79 | x.trailing_zeros() >= exp 80 | } 81 | 82 | // Replace n by floor(n / 10^N). 83 | // Returns true if and only if n is divisible by 10^N. 84 | // Precondition: n <= 10^(N+1) 85 | pub(crate) fn check_divisibility_and_divide_by_pow10(n: &mut u32) -> bool { 86 | const N: u32 = 2; 87 | debug_assert!(*n <= crate::compute_power32::<{ N + 1 }>(10)); 88 | 89 | struct Info; 90 | impl Info { 91 | const MAGIC_NUMBER: u32 = 0x147c29; 92 | const BITS_FOR_COMPARISON: i32 = 12; 93 | const THRESHOLD: u32 = 0xa3; 94 | const SHIFT_AMOUNT: i32 = 27; 95 | } 96 | 97 | *n *= Info::MAGIC_NUMBER; 98 | 99 | const COMPARISON_MASK: u32 = if Info::BITS_FOR_COMPARISON >= 32 { 100 | u32::MAX 101 | } else { 102 | ((1 << Info::BITS_FOR_COMPARISON) - 1) as u32 103 | }; 104 | 105 | // The lowest N bits of (n & comparison_mask) must be zero, and 106 | // (n >> N) & comparison_mask must be at most threshold. 107 | let c = ((*n >> N) | (*n << (Info::BITS_FOR_COMPARISON as u32 - N))) & COMPARISON_MASK; 108 | 109 | *n >>= Info::SHIFT_AMOUNT; 110 | c <= Info::THRESHOLD 111 | } 112 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | // Translated from C++ to Rust. The original C++ code can be found at 2 | // https://github.com/jk-jeon/dragonbox and carries the following license: 3 | // 4 | // Copyright 2020-2021 Junekey Jeon 5 | // 6 | // The contents of this file may be used under the terms of 7 | // the Apache License v2.0 with LLVM Exceptions. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | //////////////////////////////////////////////////////////////////////////////////////// 22 | // Utilities for fast/constexpr log computation. 23 | //////////////////////////////////////////////////////////////////////////////////////// 24 | 25 | const fn floor_shift(integer_part: u32, fractional_digits: u64, shift_amount: usize) -> i32 { 26 | //debug_assert!(shift_amount < 32); 27 | // Ensure no overflow 28 | //debug_assert!(shift_amount == 0 || integer_part < (1 << (32 - shift_amount))); 29 | 30 | if shift_amount == 0 { 31 | integer_part as i32 32 | } else { 33 | ((integer_part << shift_amount) | (fractional_digits >> (64 - shift_amount)) as u32) as i32 34 | } 35 | } 36 | 37 | // Compute floor(e * c - s). 38 | const fn compute< 39 | const C_INTEGER_PART: u32, 40 | const C_FRACTIONAL_DIGITS: u64, 41 | const SHIFT_AMOUNT: usize, 42 | const MAX_EXPONENT: i32, 43 | const S_INTEGER_PART: u32, 44 | const S_FRACTIONAL_DIGITS: u64, 45 | >( 46 | e: i32, 47 | ) -> i32 { 48 | //debug_assert!(e <= MAX_EXPONENT && e >= -MAX_EXPONENT); 49 | let c = floor_shift(C_INTEGER_PART, C_FRACTIONAL_DIGITS, SHIFT_AMOUNT); 50 | let s = floor_shift(S_INTEGER_PART, S_FRACTIONAL_DIGITS, SHIFT_AMOUNT); 51 | (e * c - s) >> SHIFT_AMOUNT 52 | } 53 | 54 | const LOG10_2_FRACTIONAL_DIGITS: u64 = 0x4d10_4d42_7de7_fbcc; 55 | const LOG10_4_OVER_3_FRACTIONAL_DIGITS: u64 = 0x1ffb_fc2b_bc78_0375; 56 | const FLOOR_LOG10_POW2_SHIFT_AMOUNT: usize = 22; 57 | const FLOOR_LOG10_POW2_INPUT_LIMIT: i32 = 1700; 58 | const FLOOR_LOG10_POW2_MINUS_LOG10_4_OVER_3_INPUT_LIMIT: i32 = 1700; 59 | 60 | const LOG2_10_FRACTIONAL_DIGITS: u64 = 0x5269_e12f_346e_2bf9; 61 | const FLOOR_LOG2_POW10_SHIFT_AMOUNT: usize = 19; 62 | const FLOOR_LOG2_POW10_INPUT_LIMIT: i32 = 1233; 63 | 64 | const LOG5_2_FRACTIONAL_DIGITS: u64 = 0x6e40_d1a4_143d_cb94; 65 | const LOG5_3_FRACTIONAL_DIGITS: u64 = 0xaebf_4791_5d44_3b24; 66 | const FLOOR_LOG5_POW2_SHIFT_AMOUNT: usize = 20; 67 | const FLOOR_LOG5_POW2_INPUT_LIMIT: i32 = 1492; 68 | const FLOOR_LOG5_POW2_MINUS_LOG5_3_INPUT_LIMIT: i32 = 2427; 69 | 70 | pub(crate) const fn floor_log10_pow2(e: i32) -> i32 { 71 | compute::< 72 | 0, 73 | LOG10_2_FRACTIONAL_DIGITS, 74 | FLOOR_LOG10_POW2_SHIFT_AMOUNT, 75 | FLOOR_LOG10_POW2_INPUT_LIMIT, 76 | 0, 77 | 0, 78 | >(e) 79 | } 80 | 81 | pub(crate) const fn floor_log2_pow10(e: i32) -> i32 { 82 | compute::< 83 | 3, 84 | LOG2_10_FRACTIONAL_DIGITS, 85 | FLOOR_LOG2_POW10_SHIFT_AMOUNT, 86 | FLOOR_LOG2_POW10_INPUT_LIMIT, 87 | 0, 88 | 0, 89 | >(e) 90 | } 91 | 92 | pub(crate) const fn floor_log5_pow2(e: i32) -> i32 { 93 | compute::< 94 | 0, 95 | LOG5_2_FRACTIONAL_DIGITS, 96 | FLOOR_LOG5_POW2_SHIFT_AMOUNT, 97 | FLOOR_LOG5_POW2_INPUT_LIMIT, 98 | 0, 99 | 0, 100 | >(e) 101 | } 102 | 103 | pub(crate) const fn floor_log5_pow2_minus_log5_3(e: i32) -> i32 { 104 | compute::< 105 | 0, 106 | LOG5_2_FRACTIONAL_DIGITS, 107 | FLOOR_LOG5_POW2_SHIFT_AMOUNT, 108 | FLOOR_LOG5_POW2_MINUS_LOG5_3_INPUT_LIMIT, 109 | 0, 110 | LOG5_3_FRACTIONAL_DIGITS, 111 | >(e) 112 | } 113 | 114 | pub(crate) const fn floor_log10_pow2_minus_log10_4_over_3(e: i32) -> i32 { 115 | compute::< 116 | 0, 117 | LOG10_2_FRACTIONAL_DIGITS, 118 | FLOOR_LOG10_POW2_SHIFT_AMOUNT, 119 | FLOOR_LOG10_POW2_MINUS_LOG10_4_OVER_3_INPUT_LIMIT, 120 | 0, 121 | LOG10_4_OVER_3_FRACTIONAL_DIGITS, 122 | >(e) 123 | } 124 | -------------------------------------------------------------------------------- /tests/binary64_test.rs: -------------------------------------------------------------------------------- 1 | // Translated from C to Rust. The original C code can be found at 2 | // https://github.com/ulfjack/ryu and carries the following license: 3 | // 4 | // Copyright 2018 Ulf Adams 5 | // 6 | // The contents of this file may be used under the terms of the Apache License, 7 | // Version 2.0. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // http://www.apache.org/licenses/LICENSE-2.0) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | #![allow( 22 | clippy::approx_constant, 23 | clippy::cast_lossless, 24 | clippy::excessive_precision, 25 | clippy::float_cmp, 26 | clippy::int_plus_one, 27 | clippy::non_ascii_literal, 28 | clippy::unreadable_literal, 29 | clippy::unseparated_literal_suffix 30 | )] 31 | 32 | #[macro_use] 33 | mod macros; 34 | 35 | use std::f64; 36 | 37 | fn to_chars(f: f64) -> String { 38 | dragonbox::Buffer::new().format(f).to_owned() 39 | } 40 | 41 | fn ieee_parts_to_double(sign: bool, ieee_exponent: u32, ieee_mantissa: u64) -> f64 { 42 | assert!(ieee_exponent <= 2047); 43 | assert!(ieee_mantissa <= (1u64 << 53) - 1); 44 | f64::from_bits(((sign as u64) << 63) | ((ieee_exponent as u64) << 52) | ieee_mantissa) 45 | } 46 | 47 | #[test] 48 | fn test_dragonbox() { 49 | check!(3E-1); 50 | check!(1.234E15); 51 | check!(2.71828E0); 52 | check!(1.1E128); 53 | check!(1.1E-64); 54 | check!(2.718281828459045E0); 55 | check!(5E-324); 56 | check!(1.7976931348623157E308); 57 | } 58 | 59 | #[test] 60 | fn test_random() { 61 | let n = if cfg!(miri) { 100 } else { 1000000 }; 62 | let mut buffer = dragonbox::Buffer::new(); 63 | for _ in 0..n { 64 | let f: f64 = rand::random(); 65 | assert_eq!(f, buffer.format_finite(f).parse().unwrap()); 66 | } 67 | } 68 | 69 | #[test] 70 | #[cfg_attr(miri, ignore = "too slow for miri")] 71 | fn test_non_finite() { 72 | for i in 0u64..1 << 23 { 73 | let f = f64::from_bits((((1 << 11) - 1) << 52) + (i << 29)); 74 | assert!(!f.is_finite(), "f={f}"); 75 | dragonbox::Buffer::new().format_finite(f); 76 | } 77 | } 78 | 79 | #[test] 80 | fn test_basic() { 81 | check!(0E0); 82 | check!(-0E0); 83 | check!(1E0); 84 | check!(-1E0); 85 | assert_eq!(to_chars(f64::NAN.copysign(1.0)), "NaN"); 86 | assert_eq!(to_chars(f64::NAN.copysign(-1.0)), "NaN"); 87 | assert_eq!(to_chars(f64::INFINITY), "inf"); 88 | assert_eq!(to_chars(f64::NEG_INFINITY), "-inf"); 89 | } 90 | 91 | #[test] 92 | fn test_switch_to_subnormal() { 93 | check!(2.2250738585072014E-308); 94 | } 95 | 96 | #[test] 97 | fn test_min_and_max() { 98 | assert_eq!(f64::from_bits(0x7fefffffffffffff), 1.7976931348623157E308); 99 | check!(1.7976931348623157E308); 100 | assert_eq!(f64::from_bits(1), 5E-324); 101 | check!(5E-324); 102 | } 103 | 104 | #[test] 105 | fn test_lots_of_trailing_zeros() { 106 | check!(2.9802322387695313E-8); 107 | } 108 | 109 | #[test] 110 | fn test_regression() { 111 | check!(-2.109808898695963E16); 112 | check!(4.940656E-318); 113 | check!(1.18575755E-316); 114 | check!(2.989102097996E-312); 115 | check!(9.0608011534336E15); 116 | check!(4.708356024711512E18); 117 | check!(9.409340012568248E18); 118 | check!(1.2345678E0); 119 | } 120 | 121 | #[test] 122 | fn test_looks_like_pow5() { 123 | // These numbers have a mantissa that is a multiple of the largest power of 124 | // 5 that fits, and an exponent that causes the computation for q to result 125 | // in 22, which is a corner case for Ryū. 126 | assert_eq!(f64::from_bits(0x4830F0CF064DD592), 5.764607523034235E39); 127 | check!(5.764607523034235E39); 128 | assert_eq!(f64::from_bits(0x4840F0CF064DD592), 1.152921504606847E40); 129 | check!(1.152921504606847E40); 130 | assert_eq!(f64::from_bits(0x4850F0CF064DD592), 2.305843009213694E40); 131 | check!(2.305843009213694E40); 132 | } 133 | 134 | #[test] 135 | fn test_output_length() { 136 | check!(1E0); // already tested in Basic 137 | check!(1.2E0); 138 | check!(1.23E0); 139 | check!(1.234E0); 140 | check!(1.2345E0); 141 | check!(1.23456E0); 142 | check!(1.234567E0); 143 | check!(1.2345678E0); // already tested in Regression 144 | check!(1.23456789E0); 145 | check!(1.234567895E0); // 1.234567890 would be trimmed 146 | check!(1.2345678901E0); 147 | check!(1.23456789012E0); 148 | check!(1.234567890123E0); 149 | check!(1.2345678901234E0); 150 | check!(1.23456789012345E0); 151 | check!(1.234567890123456E0); 152 | check!(1.2345678901234567E0); 153 | 154 | // Test 32-bit chunking 155 | check!(4.294967294E0); // 2^32 - 2 156 | check!(4.294967295E0); // 2^32 - 1 157 | check!(4.294967296E0); // 2^32 158 | check!(4.294967297E0); // 2^32 + 1 159 | check!(4.294967298E0); // 2^32 + 2 160 | } 161 | 162 | // Test min, max shift values in shiftright128 163 | #[test] 164 | fn test_min_max_shift() { 165 | let max_mantissa = (1u64 << 53) - 1; 166 | 167 | // 32-bit opt-size=0: 49 <= dist <= 50 168 | // 32-bit opt-size=1: 30 <= dist <= 50 169 | // 64-bit opt-size=0: 50 <= dist <= 50 170 | // 64-bit opt-size=1: 30 <= dist <= 50 171 | assert_eq!(1.7800590868057611E-307, ieee_parts_to_double(false, 4, 0)); 172 | check!(1.7800590868057611E-307); 173 | // 32-bit opt-size=0: 49 <= dist <= 49 174 | // 32-bit opt-size=1: 28 <= dist <= 49 175 | // 64-bit opt-size=0: 50 <= dist <= 50 176 | // 64-bit opt-size=1: 28 <= dist <= 50 177 | assert_eq!( 178 | 2.8480945388892175E-306, 179 | ieee_parts_to_double(false, 6, max_mantissa) 180 | ); 181 | check!(2.8480945388892175E-306); 182 | // 32-bit opt-size=0: 52 <= dist <= 53 183 | // 32-bit opt-size=1: 2 <= dist <= 53 184 | // 64-bit opt-size=0: 53 <= dist <= 53 185 | // 64-bit opt-size=1: 2 <= dist <= 53 186 | assert_eq!(2.446494580089078E-296, ieee_parts_to_double(false, 41, 0)); 187 | check!(2.446494580089078E-296); 188 | // 32-bit opt-size=0: 52 <= dist <= 52 189 | // 32-bit opt-size=1: 2 <= dist <= 52 190 | // 64-bit opt-size=0: 53 <= dist <= 53 191 | // 64-bit opt-size=1: 2 <= dist <= 53 192 | assert_eq!( 193 | 4.8929891601781557E-296, 194 | ieee_parts_to_double(false, 40, max_mantissa) 195 | ); 196 | check!(4.8929891601781557E-296); 197 | 198 | // 32-bit opt-size=0: 57 <= dist <= 58 199 | // 32-bit opt-size=1: 57 <= dist <= 58 200 | // 64-bit opt-size=0: 58 <= dist <= 58 201 | // 64-bit opt-size=1: 58 <= dist <= 58 202 | assert_eq!(1.8014398509481984E16, ieee_parts_to_double(false, 1077, 0)); 203 | check!(1.8014398509481984E16); 204 | // 32-bit opt-size=0: 57 <= dist <= 57 205 | // 32-bit opt-size=1: 57 <= dist <= 57 206 | // 64-bit opt-size=0: 58 <= dist <= 58 207 | // 64-bit opt-size=1: 58 <= dist <= 58 208 | assert_eq!( 209 | 3.6028797018963964E16, 210 | ieee_parts_to_double(false, 1076, max_mantissa) 211 | ); 212 | check!(3.6028797018963964E16); 213 | // 32-bit opt-size=0: 51 <= dist <= 52 214 | // 32-bit opt-size=1: 51 <= dist <= 59 215 | // 64-bit opt-size=0: 52 <= dist <= 52 216 | // 64-bit opt-size=1: 52 <= dist <= 59 217 | assert_eq!(2.9008355198595578E-216, ieee_parts_to_double(false, 307, 0)); 218 | check!(2.9008355198595578E-216); 219 | // 32-bit opt-size=0: 51 <= dist <= 51 220 | // 32-bit opt-size=1: 51 <= dist <= 59 221 | // 64-bit opt-size=0: 52 <= dist <= 52 222 | // 64-bit opt-size=1: 52 <= dist <= 59 223 | assert_eq!( 224 | 5.801671039719115E-216, 225 | ieee_parts_to_double(false, 306, max_mantissa) 226 | ); 227 | check!(5.801671039719115E-216); 228 | 229 | // https://github.com/ulfjack/ryu/commit/19e44d16d80236f5de25800f56d82606d1be00b9#commitcomment-30146483 230 | // 32-bit opt-size=0: 49 <= dist <= 49 231 | // 32-bit opt-size=1: 44 <= dist <= 49 232 | // 64-bit opt-size=0: 50 <= dist <= 50 233 | // 64-bit opt-size=1: 44 <= dist <= 50 234 | assert_eq!( 235 | 3.196104012172126E-27, 236 | ieee_parts_to_double(false, 934, 0x000FA7161A4D6E0C) 237 | ); 238 | check!(3.196104012172126E-27); 239 | } 240 | 241 | #[test] 242 | fn test_small_integers() { 243 | check!(9.007199254740991E15); // 2^53-1 244 | check!(9.007199254740992E15); // 2^53 245 | 246 | check!(1E0); 247 | check!(1.2E1); 248 | check!(1.23E2); 249 | check!(1.234E3); 250 | check!(1.2345E4); 251 | check!(1.23456E5); 252 | check!(1.234567E6); 253 | check!(1.2345678E7); 254 | check!(1.23456789E8); 255 | check!(1.23456789E9); 256 | check!(1.234567895E10); 257 | check!(1.2345678901E11); 258 | check!(1.23456789012E12); 259 | check!(1.234567890123E13); 260 | check!(1.2345678901234E14); 261 | check!(1.23456789012345E15); 262 | check!(1.234567890123456E16); 263 | 264 | // 10^i 265 | check!(1E0); 266 | check!(1E1); 267 | check!(1E2); 268 | check!(1E3); 269 | check!(1E4); 270 | check!(1E5); 271 | check!(1E6); 272 | check!(1E7); 273 | check!(1E8); 274 | check!(1E9); 275 | check!(1E10); 276 | check!(1E11); 277 | check!(1E12); 278 | check!(1E13); 279 | check!(1E14); 280 | check!(1E15); 281 | 282 | // 10^15 + 10^i 283 | check!(1.000000000000001E15); 284 | check!(1.00000000000001E15); 285 | check!(1.0000000000001E15); 286 | check!(1.000000000001E15); 287 | check!(1.00000000001E15); 288 | check!(1.0000000001E15); 289 | check!(1.000000001E15); 290 | check!(1.00000001E15); 291 | check!(1.0000001E15); 292 | check!(1.000001E15); 293 | check!(1.00001E15); 294 | check!(1.0001E15); 295 | check!(1.001E15); 296 | check!(1.01E15); 297 | check!(1.1E15); 298 | 299 | // Largest power of 2 <= 10^(i+1) 300 | check!(8E0); 301 | check!(6.4E1); 302 | check!(5.12E2); 303 | check!(8.192E3); 304 | check!(6.5536E4); 305 | check!(5.24288E5); 306 | check!(8.388608E6); 307 | check!(6.7108864E7); 308 | check!(5.36870912E8); 309 | check!(8.589934592E9); 310 | check!(6.8719476736E10); 311 | check!(5.49755813888E11); 312 | check!(8.796093022208E12); 313 | check!(7.0368744177664E13); 314 | check!(5.62949953421312E14); 315 | check!(9.007199254740992E15); 316 | 317 | // 1000 * (Largest power of 2 <= 10^(i+1)) 318 | check!(8E3); 319 | check!(6.4E4); 320 | check!(5.12E5); 321 | check!(8.192E6); 322 | check!(6.5536E7); 323 | check!(5.24288E8); 324 | check!(8.388608E9); 325 | check!(6.7108864E10); 326 | check!(5.36870912E11); 327 | check!(8.589934592E12); 328 | check!(6.8719476736E13); 329 | check!(5.49755813888E14); 330 | check!(8.796093022208E15); 331 | } 332 | -------------------------------------------------------------------------------- /LICENSE-Apache2-LLVM: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | 179 | --- LLVM Exceptions to the Apache 2.0 License ---- 180 | 181 | As an exception, if, as a result of your compiling your source code, portions 182 | of this Software are embedded into an Object form of such source code, you 183 | may redistribute such embedded portions in such Object form without complying 184 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 185 | 186 | In addition, if you combine or link compiled forms of this Software with 187 | software that is licensed under the GPLv2 ("Combined Software") and if a 188 | court of competent jurisdiction determines that the patent provision (Section 189 | 3), the indemnity provision (Section 9) or other Section of the License 190 | conflicts with the conditions of the GPLv2, you may retroactively and 191 | prospectively choose to deem waived or otherwise exclude such Section(s) of 192 | the License, but only in their entirety and only with respect to the Combined 193 | Software. 194 | -------------------------------------------------------------------------------- /src/to_chars.rs: -------------------------------------------------------------------------------- 1 | // Translated from C++ to Rust. The original C++ code can be found at 2 | // https://github.com/jk-jeon/dragonbox and carries the following license: 3 | // 4 | // Copyright 2020-2021 Junekey Jeon 5 | // 6 | // The contents of this file may be used under the terms of 7 | // the Apache License v2.0 with LLVM Exceptions. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | use core::ptr; 22 | 23 | // sign(1) + significand(17) + decimal_point(1) + exp_marker(1) + exp_sign(1) + exp(3) 24 | pub(crate) const MAX_OUTPUT_STRING_LENGTH: usize = 1 + 17 + 1 + 1 + 1 + 3; 25 | 26 | pub(crate) unsafe fn to_chars(x: f64, mut buffer: *mut u8) -> *mut u8 { 27 | let br = x.to_bits(); 28 | let exponent_bits = crate::extract_exponent_bits(br); 29 | let s = crate::remove_exponent_bits(br, exponent_bits); 30 | 31 | if crate::is_negative(s) { 32 | *buffer = b'-'; 33 | buffer = buffer.add(1); 34 | } 35 | 36 | if crate::is_nonzero(br) { 37 | let result = crate::to_decimal(x); 38 | to_chars_detail(result.significand, result.exponent, buffer) 39 | } else { 40 | ptr::copy_nonoverlapping(b"0E0".as_ptr(), buffer, 3); 41 | buffer.add(3) 42 | } 43 | } 44 | 45 | #[rustfmt::skip] 46 | static RADIX_100_TABLE: [u8; 200] = [ 47 | b'0', b'0', b'0', b'1', b'0', b'2', b'0', b'3', b'0', b'4', 48 | b'0', b'5', b'0', b'6', b'0', b'7', b'0', b'8', b'0', b'9', 49 | b'1', b'0', b'1', b'1', b'1', b'2', b'1', b'3', b'1', b'4', 50 | b'1', b'5', b'1', b'6', b'1', b'7', b'1', b'8', b'1', b'9', 51 | b'2', b'0', b'2', b'1', b'2', b'2', b'2', b'3', b'2', b'4', 52 | b'2', b'5', b'2', b'6', b'2', b'7', b'2', b'8', b'2', b'9', 53 | b'3', b'0', b'3', b'1', b'3', b'2', b'3', b'3', b'3', b'4', 54 | b'3', b'5', b'3', b'6', b'3', b'7', b'3', b'8', b'3', b'9', 55 | b'4', b'0', b'4', b'1', b'4', b'2', b'4', b'3', b'4', b'4', 56 | b'4', b'5', b'4', b'6', b'4', b'7', b'4', b'8', b'4', b'9', 57 | b'5', b'0', b'5', b'1', b'5', b'2', b'5', b'3', b'5', b'4', 58 | b'5', b'5', b'5', b'6', b'5', b'7', b'5', b'8', b'5', b'9', 59 | b'6', b'0', b'6', b'1', b'6', b'2', b'6', b'3', b'6', b'4', 60 | b'6', b'5', b'6', b'6', b'6', b'7', b'6', b'8', b'6', b'9', 61 | b'7', b'0', b'7', b'1', b'7', b'2', b'7', b'3', b'7', b'4', 62 | b'7', b'5', b'7', b'6', b'7', b'7', b'7', b'8', b'7', b'9', 63 | b'8', b'0', b'8', b'1', b'8', b'2', b'8', b'3', b'8', b'4', 64 | b'8', b'5', b'8', b'6', b'8', b'7', b'8', b'8', b'8', b'9', 65 | b'9', b'0', b'9', b'1', b'9', b'2', b'9', b'3', b'9', b'4', 66 | b'9', b'5', b'9', b'6', b'9', b'7', b'9', b'8', b'9', b'9', 67 | ]; 68 | 69 | #[rustfmt::skip] 70 | static TRAILING_ZERO_COUNT_TABLE: [i8; 100] = [ 71 | 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81 | ]; 82 | 83 | fn decimal_length_minus_1(v: u32) -> i32 { 84 | debug_assert!(v < 1000000000); 85 | if v >= 100000000 { 86 | 8 87 | } else if v >= 10000000 { 88 | 7 89 | } else if v >= 1000000 { 90 | 6 91 | } else if v >= 100000 { 92 | 5 93 | } else if v >= 10000 { 94 | 4 95 | } else if v >= 1000 { 96 | 3 97 | } else if v >= 100 { 98 | 2 99 | } else if v >= 10 { 100 | 1 101 | } else { 102 | 0 103 | } 104 | } 105 | 106 | // Granlund-Montgomery style fast division 107 | struct QuotientRemainderPair { 108 | quotient: u32, 109 | remainder: u32, 110 | } 111 | 112 | fn fast_div( 113 | n: u32, 114 | ) -> QuotientRemainderPair { 115 | debug_assert!(MAX_PRECISION > 0 && MAX_PRECISION <= 32); 116 | debug_assert!(n < (1 << MAX_PRECISION)); 117 | 118 | let left_end = ((1u32 << (MAX_PRECISION + ADDITIONAL_PRECISION)) + DIVISOR - 1) / DIVISOR; 119 | let right_end = ((1u32 << ADDITIONAL_PRECISION) * ((1 << MAX_PRECISION) + 1)) / DIVISOR; 120 | 121 | // Ensures sufficient precision. 122 | debug_assert!(left_end <= right_end); 123 | // Ensures no overflow. 124 | debug_assert!(left_end <= (1 << (32 - MAX_PRECISION)) as u32); 125 | 126 | let quotient = (n * left_end) >> (MAX_PRECISION + ADDITIONAL_PRECISION); 127 | let remainder = n - DIVISOR * quotient; 128 | QuotientRemainderPair { 129 | quotient, 130 | remainder, 131 | } 132 | } 133 | 134 | unsafe fn to_chars_detail(significand: u64, mut exponent: i32, mut buffer: *mut u8) -> *mut u8 { 135 | let mut s32: u32; 136 | let mut remaining_digits_minus_1: i32; 137 | let mut exponent_position: i32; 138 | let mut may_have_more_trailing_zeros = false; 139 | 140 | if significand >> 32 != 0 { 141 | // Since significand is at most 10^17, the quotient is at most 10^9, so 142 | // it fits inside 32-bit integer 143 | s32 = (significand / 1_0000_0000) as u32; 144 | let mut r = (significand as u32).wrapping_sub(s32.wrapping_mul(1_0000_0000)); 145 | 146 | remaining_digits_minus_1 = decimal_length_minus_1(s32) + 8; 147 | exponent += remaining_digits_minus_1; 148 | exponent_position = remaining_digits_minus_1 + 2; 149 | 150 | if r != 0 { 151 | // Print 8 digits 152 | // `c = r % 1_0000` https://bugs.llvm.org/show_bug.cgi?id=38217 153 | let c = r - 1_0000 * (r / 1_0000); 154 | r /= 1_0000; 155 | 156 | // c1 = r / 100; c2 = r % 100; 157 | let QuotientRemainderPair { 158 | quotient: c1, 159 | remainder: c2, 160 | } = fast_div::<100, 14, 5>(r); 161 | // c3 = c / 100; c4 = c % 100; 162 | let QuotientRemainderPair { 163 | quotient: c3, 164 | remainder: c4, 165 | } = fast_div::<100, 14, 5>(c); 166 | 167 | 'after_print_label: loop { 168 | 'print_c1_label: loop { 169 | 'print_c2_label: loop { 170 | 'print_c3_label: loop { 171 | 'print_c4_label: loop { 172 | let mut tz = *TRAILING_ZERO_COUNT_TABLE.get_unchecked(c4 as usize); 173 | if tz == 0 { 174 | break 'print_c4_label; 175 | } else if tz == 1 { 176 | *buffer.offset(remaining_digits_minus_1 as isize) = 177 | *RADIX_100_TABLE.get_unchecked(c4 as usize * 2); 178 | exponent_position -= 1; 179 | break 'print_c3_label; 180 | } 181 | 182 | tz = *TRAILING_ZERO_COUNT_TABLE.get_unchecked(c3 as usize); 183 | if tz == 0 { 184 | exponent_position -= 2; 185 | break 'print_c3_label; 186 | } else if tz == 1 { 187 | *buffer.offset(remaining_digits_minus_1 as isize - 2) = 188 | *RADIX_100_TABLE.get_unchecked(c3 as usize * 2); 189 | exponent_position -= 3; 190 | break 'print_c2_label; 191 | } 192 | 193 | tz = *TRAILING_ZERO_COUNT_TABLE.get_unchecked(c2 as usize); 194 | if tz == 0 { 195 | exponent_position -= 4; 196 | break 'print_c2_label; 197 | } else if tz == 1 { 198 | *buffer.offset(remaining_digits_minus_1 as isize - 4) = 199 | *RADIX_100_TABLE.get_unchecked(c2 as usize * 2); 200 | exponent_position -= 5; 201 | break 'print_c1_label; 202 | } 203 | 204 | tz = *TRAILING_ZERO_COUNT_TABLE.get_unchecked(c1 as usize); 205 | if tz == 0 { 206 | exponent_position -= 6; 207 | break 'print_c1_label; 208 | } 209 | // We assumed r != 0, so c1 cannot be zero in this case. 210 | debug_assert!(tz == 1); 211 | *buffer.offset(remaining_digits_minus_1 as isize - 6) = 212 | *RADIX_100_TABLE.get_unchecked(c1 as usize * 2); 213 | exponent_position -= 7; 214 | break 'after_print_label; 215 | } 216 | 217 | ptr::copy_nonoverlapping( 218 | RADIX_100_TABLE.as_ptr().add(c4 as usize * 2), 219 | buffer.offset(remaining_digits_minus_1 as isize), 220 | 2, 221 | ); 222 | break; 223 | } 224 | 225 | ptr::copy_nonoverlapping( 226 | RADIX_100_TABLE.as_ptr().add(c3 as usize * 2), 227 | buffer.offset(remaining_digits_minus_1 as isize - 2), 228 | 2, 229 | ); 230 | break; 231 | } 232 | 233 | ptr::copy_nonoverlapping( 234 | RADIX_100_TABLE.as_ptr().add(c2 as usize * 2), 235 | buffer.offset(remaining_digits_minus_1 as isize - 4), 236 | 2, 237 | ); 238 | break; 239 | } 240 | 241 | ptr::copy_nonoverlapping( 242 | RADIX_100_TABLE.as_ptr().add(c1 as usize * 2), 243 | buffer.offset(remaining_digits_minus_1 as isize - 6), 244 | 2, 245 | ); 246 | break; 247 | } 248 | } 249 | // r != 0 250 | else { 251 | // r == 0 252 | exponent_position -= 8; 253 | may_have_more_trailing_zeros = true; 254 | } 255 | remaining_digits_minus_1 -= 8; 256 | } else { 257 | s32 = significand as u32; 258 | if s32 >= 10_0000_0000 { 259 | remaining_digits_minus_1 = 9; 260 | } else { 261 | remaining_digits_minus_1 = decimal_length_minus_1(s32); 262 | } 263 | exponent += remaining_digits_minus_1; 264 | exponent_position = remaining_digits_minus_1 + 2; 265 | may_have_more_trailing_zeros = true; 266 | } 267 | 268 | while remaining_digits_minus_1 >= 4 { 269 | // c = s32 % 1_0000` https://bugs.llvm.org/show_bug.cgi?id=38217 270 | let c = s32 - 1_0000 * (s32 / 1_0000); 271 | s32 /= 1_0000; 272 | 273 | // c1 = c / 100; c2 = c % 100; 274 | let QuotientRemainderPair { 275 | quotient: c1, 276 | remainder: c2, 277 | } = fast_div::<100, 14, 5>(c); 278 | 279 | 'inside_loop_after_print_label: loop { 280 | 'inside_loop_print_c1_label: loop { 281 | 'inside_loop_print_c2_label: loop { 282 | if may_have_more_trailing_zeros { 283 | let mut tz = *TRAILING_ZERO_COUNT_TABLE.get_unchecked(c2 as usize); 284 | if tz == 0 { 285 | may_have_more_trailing_zeros = false; 286 | break 'inside_loop_print_c2_label; 287 | } else if tz == 1 { 288 | may_have_more_trailing_zeros = false; 289 | exponent_position -= 1; 290 | *buffer.offset(remaining_digits_minus_1 as isize) = 291 | *RADIX_100_TABLE.get_unchecked(c2 as usize * 2); 292 | break 'inside_loop_print_c1_label; 293 | } 294 | 295 | tz = *TRAILING_ZERO_COUNT_TABLE.get_unchecked(c1 as usize); 296 | if tz == 0 { 297 | may_have_more_trailing_zeros = false; 298 | exponent_position -= 2; 299 | break 'inside_loop_print_c1_label; 300 | } else if tz == 1 { 301 | may_have_more_trailing_zeros = false; 302 | exponent_position -= 3; 303 | *buffer.offset(remaining_digits_minus_1 as isize - 2) = 304 | *RADIX_100_TABLE.get_unchecked(c1 as usize * 2); 305 | break 'inside_loop_after_print_label; 306 | } 307 | exponent_position -= 4; 308 | break 'inside_loop_after_print_label; 309 | } 310 | break; 311 | } 312 | 313 | ptr::copy_nonoverlapping( 314 | RADIX_100_TABLE.as_ptr().add(c2 as usize * 2), 315 | buffer.offset(remaining_digits_minus_1 as isize), 316 | 2, 317 | ); 318 | break; 319 | } 320 | 321 | ptr::copy_nonoverlapping( 322 | RADIX_100_TABLE.as_ptr().add(c1 as usize * 2), 323 | buffer.offset(remaining_digits_minus_1 as isize - 2), 324 | 2, 325 | ); 326 | break; 327 | } 328 | remaining_digits_minus_1 -= 4; 329 | } 330 | if remaining_digits_minus_1 >= 2 { 331 | // c1 = s32 / 100; c2 = s32 % 100; 332 | let QuotientRemainderPair { 333 | quotient: c1, 334 | remainder: c2, 335 | } = fast_div::<100, 14, 5>(s32); 336 | s32 = c1; 337 | 338 | if may_have_more_trailing_zeros { 339 | let tz = *TRAILING_ZERO_COUNT_TABLE.get_unchecked(c2 as usize); 340 | exponent_position -= tz as i32; 341 | if tz == 0 { 342 | ptr::copy_nonoverlapping( 343 | RADIX_100_TABLE.as_ptr().add(c2 as usize * 2), 344 | buffer.offset(remaining_digits_minus_1 as isize), 345 | 2, 346 | ); 347 | may_have_more_trailing_zeros = false; 348 | } else if tz == 1 { 349 | *buffer.offset(remaining_digits_minus_1 as isize) = 350 | *RADIX_100_TABLE.get_unchecked(c2 as usize * 2); 351 | may_have_more_trailing_zeros = false; 352 | } 353 | } else { 354 | ptr::copy_nonoverlapping( 355 | RADIX_100_TABLE.as_ptr().add(c2 as usize * 2), 356 | buffer.offset(remaining_digits_minus_1 as isize), 357 | 2, 358 | ); 359 | } 360 | 361 | remaining_digits_minus_1 -= 2; 362 | } 363 | if remaining_digits_minus_1 > 0 { 364 | debug_assert!(remaining_digits_minus_1 == 1); 365 | // d1 = s32 / 10; d2 = s32 % 10; 366 | let QuotientRemainderPair { 367 | quotient: d1, 368 | remainder: d2, 369 | } = fast_div::<10, 7, 3>(s32); 370 | 371 | *buffer = b'0' + d1 as u8; 372 | if may_have_more_trailing_zeros && d2 == 0 { 373 | buffer = buffer.add(1); 374 | } else { 375 | *buffer.add(1) = b'.'; 376 | *buffer.add(2) = b'0' + d2 as u8; 377 | buffer = buffer.offset(exponent_position as isize); 378 | } 379 | } else { 380 | *buffer = b'0' + s32 as u8; 381 | 382 | if may_have_more_trailing_zeros { 383 | buffer = buffer.add(1); 384 | } else { 385 | *buffer.add(1) = b'.'; 386 | buffer = buffer.offset(exponent_position as isize); 387 | } 388 | } 389 | 390 | // Print exponent and return 391 | if exponent < 0 { 392 | ptr::copy_nonoverlapping(b"E-".as_ptr(), buffer, 2); 393 | buffer = buffer.add(2); 394 | exponent = -exponent; 395 | } else { 396 | *buffer = b'E'; 397 | buffer = buffer.add(1); 398 | } 399 | 400 | if exponent >= 100 { 401 | // d1 = exponent / 10; d2 = exponent % 10; 402 | let QuotientRemainderPair { 403 | quotient: d1, 404 | remainder: d2, 405 | } = fast_div::<10, 10, 3>(exponent as u32); 406 | ptr::copy_nonoverlapping(RADIX_100_TABLE.as_ptr().add(d1 as usize * 2), buffer, 2); 407 | *buffer.add(2) = b'0' + d2 as u8; 408 | buffer = buffer.add(3); 409 | } else if exponent >= 10 { 410 | ptr::copy_nonoverlapping( 411 | RADIX_100_TABLE.as_ptr().add(exponent as usize * 2), 412 | buffer, 413 | 2, 414 | ); 415 | buffer = buffer.add(2); 416 | } else { 417 | *buffer = b'0' + exponent as u8; 418 | buffer = buffer.add(1); 419 | } 420 | 421 | buffer 422 | } 423 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Translated from C++ to Rust. The original C++ code can be found at 2 | // https://github.com/jk-jeon/dragonbox and carries the following license: 3 | // 4 | // Copyright 2020-2021 Junekey Jeon 5 | // 6 | // The contents of this file may be used under the terms of 7 | // the Apache License v2.0 with LLVM Exceptions. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | //! [![github]](https://github.com/dtolnay/dragonbox) [![crates-io]](https://crates.io/crates/dragonbox) [![docs-rs]](https://docs.rs/dragonbox) 22 | //! 23 | //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github 24 | //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust 25 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs 26 | //! 27 | //!
28 | //! 29 | //! This crate contains a basic port of 30 | //! to Rust for benchmarking purposes. 31 | //! 32 | //! Please see the upstream repo for an explanation of the approach and 33 | //! comparison to the Ryū algorithm. 34 | //! 35 | //! # Example 36 | //! 37 | //! ``` 38 | //! fn main() { 39 | //! let mut buffer = dragonbox::Buffer::new(); 40 | //! let printed = buffer.format(1.234); 41 | //! assert_eq!(printed, "1.234E0"); 42 | //! } 43 | //! ``` 44 | //! 45 | //! ## Performance (lower is better) 46 | //! 47 | //! ![performance](https://raw.githubusercontent.com/dtolnay/dragonbox/master/performance.png) 48 | 49 | #![no_std] 50 | #![doc(html_root_url = "https://docs.rs/dragonbox/0.1.10")] 51 | #![allow(unsafe_op_in_unsafe_fn)] 52 | #![allow( 53 | clippy::bool_to_int_with_if, 54 | clippy::cast_lossless, 55 | clippy::cast_possible_truncation, 56 | clippy::cast_possible_wrap, 57 | clippy::cast_sign_loss, 58 | clippy::comparison_chain, 59 | clippy::doc_markdown, 60 | clippy::expl_impl_clone_on_copy, 61 | clippy::if_not_else, 62 | clippy::items_after_statements, 63 | clippy::manual_range_contains, 64 | clippy::must_use_candidate, 65 | clippy::needless_doctest_main, 66 | clippy::never_loop, 67 | clippy::shadow_unrelated, 68 | clippy::similar_names, 69 | clippy::too_many_lines, 70 | clippy::toplevel_ref_arg, 71 | clippy::unreadable_literal, 72 | clippy::unusual_byte_groupings 73 | )] 74 | 75 | mod buffer; 76 | mod cache; 77 | mod div; 78 | mod log; 79 | mod to_chars; 80 | mod wuint; 81 | 82 | use crate::buffer::Sealed; 83 | use crate::cache::EntryTypeExt as _; 84 | use core::mem::MaybeUninit; 85 | 86 | /// Buffer correctly sized to hold the text representation of any floating point 87 | /// value. 88 | /// 89 | /// ## Example 90 | /// 91 | /// ``` 92 | /// let mut buffer = dragonbox::Buffer::new(); 93 | /// let printed = buffer.format_finite(1.234); 94 | /// assert_eq!(printed, "1.234E0"); 95 | /// ``` 96 | pub struct Buffer { 97 | bytes: [MaybeUninit; to_chars::MAX_OUTPUT_STRING_LENGTH], 98 | } 99 | 100 | /// A floating point number that can be written into a 101 | /// [`dragonbox::Buffer`][Buffer]. 102 | /// 103 | /// This trait is sealed and cannot be implemented for types outside of the 104 | /// `dragonbox` crate. 105 | pub trait Float: Sealed {} 106 | 107 | // IEEE754-binary64 108 | const SIGNIFICAND_BITS: usize = 52; 109 | const EXPONENT_BITS: usize = 11; 110 | const MIN_EXPONENT: i32 = -1022; 111 | const EXPONENT_BIAS: i32 = -1023; 112 | 113 | // Defines an unsigned integer type that is large enough 114 | // to carry a variable of type f64. 115 | // Most of the operations will be done on this integer type. 116 | type CarrierUint = u64; 117 | 118 | // Defines a signed integer type for holding significand bits together with the 119 | // sign bit. 120 | type SignedSignificand = i64; 121 | 122 | // Number of bits in the above unsigned integer type. 123 | const CARRIER_BITS: usize = 64; 124 | 125 | // Extract exponent bits from a bit pattern. The result must be aligned to the 126 | // LSB so that there is no additional zero paddings on the right. This function 127 | // does not do bias adjustment. 128 | const fn extract_exponent_bits(u: CarrierUint) -> u32 { 129 | const EXPONENT_BITS_MASK: u32 = (1 << EXPONENT_BITS) - 1; 130 | (u >> SIGNIFICAND_BITS) as u32 & EXPONENT_BITS_MASK 131 | } 132 | 133 | // Remove the exponent bits and extract significand bits together with the sign 134 | // bit. 135 | const fn remove_exponent_bits(u: CarrierUint, exponent_bits: u32) -> SignedSignificand { 136 | (u ^ ((exponent_bits as CarrierUint) << SIGNIFICAND_BITS)) as SignedSignificand 137 | } 138 | 139 | // Shift the obtained signed significand bits to the left by 1 to remove the 140 | // sign bit. 141 | const fn remove_sign_bit_and_shift(s: SignedSignificand) -> CarrierUint { 142 | (s as CarrierUint) << 1 143 | } 144 | 145 | const fn is_nonzero(u: CarrierUint) -> bool { 146 | (u << 1) != 0 147 | } 148 | 149 | const fn is_negative(s: SignedSignificand) -> bool { 150 | s < 0 151 | } 152 | 153 | const fn has_even_significand_bits(s: SignedSignificand) -> bool { 154 | s % 2 == 0 155 | } 156 | 157 | const fn compute_power32(a: u32) -> u32 { 158 | // assert!(k >= 0); 159 | let mut p = 1; 160 | let mut i = 0; 161 | while i < K { 162 | p *= a; 163 | i += 1; 164 | } 165 | p 166 | } 167 | 168 | const fn compute_power64(a: u64) -> u64 { 169 | // assert!(k >= 0); 170 | let mut p = 1; 171 | let mut i = 0; 172 | while i < K { 173 | p *= a; 174 | i += 1; 175 | } 176 | p 177 | } 178 | 179 | const fn count_factors(mut n: usize) -> u32 { 180 | // assert!(a > 1); 181 | let mut c = 0; 182 | while n % A == 0 { 183 | n /= A; 184 | c += 1; 185 | } 186 | c 187 | } 188 | 189 | fn break_rounding_tie(significand: &mut u64) { 190 | *significand = if *significand % 2 == 0 { 191 | *significand 192 | } else { 193 | *significand - 1 194 | }; 195 | } 196 | 197 | // Compute floor(n / 10^N) for small N. 198 | // Precondition: n <= 2^a * 5^b (a = max_pow2, b = max_pow5) 199 | fn divide_by_pow10(n: u64) -> u64 { 200 | // Ensure no overflow. 201 | assert!(MAX_POW2 + (log::floor_log2_pow10(MAX_POW5) - MAX_POW5) < 64); 202 | 203 | // Specialize for 64-bit division by 1000. 204 | // Ensure that the correctness condition is met. 205 | if N == 3 206 | && MAX_POW2 + (log::floor_log2_pow10(N as i32 + MAX_POW5) - (N as i32 + MAX_POW5)) < 70 207 | { 208 | wuint::umul128_upper64(n, 0x8312_6e97_8d4f_df3c) >> 9 209 | } else { 210 | struct Divisor; 211 | impl Divisor { 212 | const VALUE: u64 = compute_power64::(10); 213 | } 214 | n / Divisor::::VALUE 215 | } 216 | } 217 | 218 | struct Decimal { 219 | significand: u64, 220 | exponent: i32, 221 | } 222 | 223 | const KAPPA: u32 = 2; 224 | 225 | // The main algorithm assumes the input is a normal/subnormal finite number 226 | fn compute_nearest_normal( 227 | two_fc: CarrierUint, 228 | exponent: i32, 229 | has_even_significand_bits: bool, 230 | ) -> Decimal { 231 | ////////////////////////////////////////////////////////////////////// 232 | // Step 1: Schubfach multiplier calculation 233 | ////////////////////////////////////////////////////////////////////// 234 | 235 | // Compute k and beta. 236 | let minus_k = log::floor_log10_pow2(exponent) - KAPPA as i32; 237 | let ref cache = unsafe { cache::get(-minus_k) }; 238 | let beta_minus_1 = exponent + log::floor_log2_pow10(-minus_k); 239 | 240 | // Compute zi and deltai. 241 | // 10^kappa <= deltai < 10^(kappa + 1) 242 | let deltai = compute_delta(cache, beta_minus_1); 243 | let two_fr = two_fc | 1; 244 | let zi = compute_mul(two_fr << beta_minus_1, cache); 245 | 246 | ////////////////////////////////////////////////////////////////////// 247 | // Step 2: Try larger divisor; remove trailing zeros if necessary 248 | ////////////////////////////////////////////////////////////////////// 249 | 250 | const BIG_DIVISOR: u32 = compute_power32::<{ KAPPA + 1 }>(10); 251 | const SMALL_DIVISOR: u32 = compute_power32::(10); 252 | 253 | // Using an upper bound on zi, we might be able to optimize the division 254 | // better than the compiler; we are computing zi / big_divisor here. 255 | let mut significand = divide_by_pow10::< 256 | { KAPPA + 1 }, 257 | { SIGNIFICAND_BITS as i32 + KAPPA as i32 + 2 }, 258 | { KAPPA as i32 + 1 }, 259 | >(zi); 260 | let mut r = (zi - BIG_DIVISOR as u64 * significand) as u32; 261 | 262 | 'small_divisor_case_label: loop { 263 | if r > deltai { 264 | break 'small_divisor_case_label; 265 | } else if r < deltai { 266 | // Exclude the right endpoint if necessary. 267 | if r == 0 268 | && !has_even_significand_bits 269 | && is_product_integer_fc_pm_half(two_fr, exponent, minus_k) 270 | { 271 | significand -= 1; 272 | r = BIG_DIVISOR; 273 | break 'small_divisor_case_label; 274 | } 275 | } else { 276 | // r == deltai; compare fractional parts. 277 | // Check conditions in the order different from the paper to take 278 | // advantage of short-circuiting. 279 | let two_fl = two_fc - 1; 280 | if (!has_even_significand_bits 281 | || !is_product_integer_fc_pm_half(two_fl, exponent, minus_k)) 282 | && !compute_mul_parity(two_fl, cache, beta_minus_1) 283 | { 284 | break 'small_divisor_case_label; 285 | } 286 | } 287 | let exponent = minus_k + KAPPA as i32 + 1; 288 | 289 | return Decimal { 290 | significand, 291 | exponent, 292 | }; 293 | } 294 | 295 | ////////////////////////////////////////////////////////////////////// 296 | // Step 3: Find the significand with the smaller divisor 297 | ////////////////////////////////////////////////////////////////////// 298 | 299 | significand *= 10; 300 | let exponent = minus_k + KAPPA as i32; 301 | 302 | let mut dist = r - (deltai / 2) + (SMALL_DIVISOR / 2); 303 | let approx_y_parity = ((dist ^ (SMALL_DIVISOR / 2)) & 1) != 0; 304 | 305 | // Is dist divisible by 10^kappa? 306 | let divisible_by_10_to_the_kappa = div::check_divisibility_and_divide_by_pow10(&mut dist); 307 | 308 | // Add dist / 10^kappa to the significand. 309 | significand += dist as CarrierUint; 310 | 311 | if divisible_by_10_to_the_kappa { 312 | // Check z^(f) >= epsilon^(f) 313 | // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, 314 | // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) 315 | // Since there are only 2 possibilities, we only need to care about the parity. 316 | // Also, zi and r should have the same parity since the divisor 317 | // is an even number. 318 | if compute_mul_parity(two_fc, cache, beta_minus_1) != approx_y_parity { 319 | significand -= 1; 320 | } else { 321 | // If z^(f) >= epsilon^(f), we might have a tie 322 | // when z^(f) == epsilon^(f), or equivalently, when y is an integer. 323 | // For tie-to-up case, we can just choose the upper one. 324 | if is_product_integer_fc(two_fc, exponent, minus_k) { 325 | break_rounding_tie(&mut significand); 326 | } 327 | } 328 | } 329 | 330 | Decimal { 331 | significand, 332 | exponent, 333 | } 334 | } 335 | 336 | fn compute_nearest_shorter(exponent: i32) -> Decimal { 337 | // Compute k and beta. 338 | let minus_k = log::floor_log10_pow2_minus_log10_4_over_3(exponent); 339 | let beta_minus_1 = exponent + log::floor_log2_pow10(-minus_k); 340 | 341 | // Compute xi and zi. 342 | let ref cache = unsafe { cache::get(-minus_k) }; 343 | 344 | let mut xi = compute_left_endpoint_for_shorter_interval_case(cache, beta_minus_1); 345 | let zi = compute_right_endpoint_for_shorter_interval_case(cache, beta_minus_1); 346 | 347 | // If the left endpoint is not an integer, increase it. 348 | if !is_left_endpoint_integer_shorter_interval(exponent) { 349 | xi += 1; 350 | } 351 | 352 | // Try bigger divisor. 353 | let significand = zi / 10; 354 | 355 | // If succeed, remove trailing zeros if necessary and return. 356 | if significand * 10 >= xi { 357 | return Decimal { 358 | significand, 359 | exponent: minus_k + 1, 360 | }; 361 | } 362 | 363 | // Otherwise, compute the round-up of y. 364 | let mut significand = compute_round_up_for_shorter_interval_case(cache, beta_minus_1); 365 | let exponent = minus_k; 366 | 367 | // When tie occurs, choose one of them according to the rule. 368 | const SHORTER_INTERVAL_TIE_LOWER_THRESHOLD: i32 = 369 | -log::floor_log5_pow2_minus_log5_3(SIGNIFICAND_BITS as i32 + 4) 370 | - 2 371 | - SIGNIFICAND_BITS as i32; 372 | const SHORTER_INTERVAL_TIE_UPPER_THRESHOLD: i32 = 373 | -log::floor_log5_pow2(SIGNIFICAND_BITS as i32 + 2) - 2 - SIGNIFICAND_BITS as i32; 374 | if exponent >= SHORTER_INTERVAL_TIE_LOWER_THRESHOLD 375 | && exponent <= SHORTER_INTERVAL_TIE_UPPER_THRESHOLD 376 | { 377 | break_rounding_tie(&mut significand); 378 | } else if significand < xi { 379 | significand += 1; 380 | } 381 | 382 | Decimal { 383 | significand, 384 | exponent, 385 | } 386 | } 387 | 388 | fn compute_mul(u: CarrierUint, cache: &cache::EntryType) -> CarrierUint { 389 | wuint::umul192_upper64(u, *cache) 390 | } 391 | 392 | fn compute_delta(cache: &cache::EntryType, beta_minus_1: i32) -> u32 { 393 | (cache.high() >> ((CARRIER_BITS - 1) as i32 - beta_minus_1)) as u32 394 | } 395 | 396 | fn compute_mul_parity(two_f: CarrierUint, cache: &cache::EntryType, beta_minus_1: i32) -> bool { 397 | debug_assert!(beta_minus_1 >= 1); 398 | debug_assert!(beta_minus_1 < 64); 399 | 400 | ((wuint::umul192_middle64(two_f, *cache) >> (64 - beta_minus_1)) & 1) != 0 401 | } 402 | 403 | fn compute_left_endpoint_for_shorter_interval_case( 404 | cache: &cache::EntryType, 405 | beta_minus_1: i32, 406 | ) -> CarrierUint { 407 | (cache.high() - (cache.high() >> (SIGNIFICAND_BITS + 2))) 408 | >> ((CARRIER_BITS - SIGNIFICAND_BITS - 1) as i32 - beta_minus_1) 409 | } 410 | 411 | fn compute_right_endpoint_for_shorter_interval_case( 412 | cache: &cache::EntryType, 413 | beta_minus_1: i32, 414 | ) -> CarrierUint { 415 | (cache.high() + (cache.high() >> (SIGNIFICAND_BITS + 2))) 416 | >> ((CARRIER_BITS - SIGNIFICAND_BITS - 1) as i32 - beta_minus_1) 417 | } 418 | 419 | fn compute_round_up_for_shorter_interval_case( 420 | cache: &cache::EntryType, 421 | beta_minus_1: i32, 422 | ) -> CarrierUint { 423 | ((cache.high() >> ((CARRIER_BITS - SIGNIFICAND_BITS - 2) as i32 - beta_minus_1)) + 1) / 2 424 | } 425 | 426 | const MAX_POWER_OF_FACTOR_OF_5: i32 = log::floor_log5_pow2(SIGNIFICAND_BITS as i32 + 2); 427 | const DIVISIBILITY_CHECK_BY_5_THRESHOLD: i32 = 428 | log::floor_log2_pow10(MAX_POWER_OF_FACTOR_OF_5 + KAPPA as i32 + 1); 429 | 430 | fn is_product_integer_fc_pm_half(two_f: CarrierUint, exponent: i32, minus_k: i32) -> bool { 431 | const CASE_FC_PM_HALF_LOWER_THRESHOLD: i32 = 432 | -(KAPPA as i32) - log::floor_log5_pow2(KAPPA as i32); 433 | const CASE_FC_PM_HALF_UPPER_THRESHOLD: i32 = log::floor_log2_pow10(KAPPA as i32 + 1); 434 | 435 | // Case I: f = fc +- 1/2 436 | if exponent < CASE_FC_PM_HALF_LOWER_THRESHOLD { 437 | false 438 | } 439 | // For k >= 0 440 | else if exponent <= CASE_FC_PM_HALF_UPPER_THRESHOLD { 441 | true 442 | } 443 | // For k < 0 444 | else if exponent > DIVISIBILITY_CHECK_BY_5_THRESHOLD { 445 | false 446 | } else { 447 | unsafe { 448 | div::divisible_by_power_of_5::<{ MAX_POWER_OF_FACTOR_OF_5 as usize + 1 }>( 449 | two_f, 450 | minus_k as u32, 451 | ) 452 | } 453 | } 454 | } 455 | 456 | fn is_product_integer_fc(two_f: CarrierUint, exponent: i32, minus_k: i32) -> bool { 457 | const CASE_FC_LOWER_THRESHOLD: i32 = 458 | -(KAPPA as i32) - 1 - log::floor_log5_pow2(KAPPA as i32 + 1); 459 | const CASE_FC_UPPER_THRESHOLD: i32 = log::floor_log2_pow10(KAPPA as i32 + 1); 460 | 461 | // Case II: f = fc + 1 462 | // Case III: f = fc 463 | // Exponent for 5 is negative 464 | if exponent > DIVISIBILITY_CHECK_BY_5_THRESHOLD { 465 | false 466 | } else if exponent > CASE_FC_UPPER_THRESHOLD { 467 | unsafe { 468 | div::divisible_by_power_of_5::<{ MAX_POWER_OF_FACTOR_OF_5 as usize + 1 }>( 469 | two_f, 470 | minus_k as u32, 471 | ) 472 | } 473 | } 474 | // Both exponents are nonnegative 475 | else if exponent >= CASE_FC_LOWER_THRESHOLD { 476 | true 477 | } 478 | // Exponent for 2 is negative 479 | else { 480 | div::divisible_by_power_of_2(two_f, (minus_k - exponent + 1) as u32) 481 | } 482 | } 483 | 484 | const fn floor_log2(mut n: u64) -> i32 { 485 | let mut count = -1; 486 | while n != 0 { 487 | count += 1; 488 | n >>= 1; 489 | } 490 | count 491 | } 492 | 493 | fn is_left_endpoint_integer_shorter_interval(exponent: i32) -> bool { 494 | const CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_LOWER_THRESHOLD: i32 = 2; 495 | const CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_UPPER_THRESHOLD: i32 = 2 + floor_log2( 496 | compute_power64::<{ count_factors::<5>((1 << (SIGNIFICAND_BITS + 2)) - 1) + 1 }>(10) / 3, 497 | ); 498 | exponent >= CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_LOWER_THRESHOLD 499 | && exponent <= CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_UPPER_THRESHOLD 500 | } 501 | 502 | fn to_decimal(x: f64) -> Decimal { 503 | let br = x.to_bits(); 504 | let exponent_bits = extract_exponent_bits(br); 505 | let signed_significand_bits = remove_exponent_bits(br, exponent_bits); 506 | 507 | let mut two_fc = remove_sign_bit_and_shift(signed_significand_bits); 508 | let mut exponent = exponent_bits as i32; 509 | 510 | // Is the input a normal number? 511 | if exponent != 0 { 512 | exponent += EXPONENT_BIAS - SIGNIFICAND_BITS as i32; 513 | 514 | // Shorter interval case; proceed like Schubfach. One might think this 515 | // condition is wrong, since when exponent_bits == 1 and two_fc == 0, 516 | // the interval is actually regular. However, it turns out that this 517 | // seemingly wrong condition is actually fine, because the end result is 518 | // anyway the same. 519 | // 520 | // [binary32] 521 | // floor( (fc-1/2) * 2^e ) = 1.175'494'28... * 10^-38 522 | // floor( (fc-1/4) * 2^e ) = 1.175'494'31... * 10^-38 523 | // floor( fc * 2^e ) = 1.175'494'35... * 10^-38 524 | // floor( (fc+1/2) * 2^e ) = 1.175'494'42... * 10^-38 525 | // 526 | // Hence, shorter_interval_case will return 1.175'494'4 * 10^-38. 527 | // 1.175'494'3 * 10^-38 is also a correct shortest representation that 528 | // will be rejected if we assume shorter interval, but 1.175'494'4 * 529 | // 10^-38 is closer to the true value so it doesn't matter. 530 | // 531 | // [binary64] 532 | // floor( (fc-1/2) * 2^e ) = 2.225'073'858'507'201'13... * 10^-308 533 | // floor( (fc-1/4) * 2^e ) = 2.225'073'858'507'201'25... * 10^-308 534 | // floor( fc * 2^e ) = 2.225'073'858'507'201'38... * 10^-308 535 | // floor( (fc+1/2) * 2^e ) = 2.225'073'858'507'201'63... * 10^-308 536 | // 537 | // Hence, shorter_interval_case will return 2.225'073'858'507'201'4 * 10^-308. 538 | // This is indeed of the shortest length, and it is the unique one 539 | // closest to the true value among valid representations of the same 540 | // length. 541 | if two_fc == 0 { 542 | return compute_nearest_shorter(exponent); 543 | } 544 | 545 | two_fc |= 1 << (SIGNIFICAND_BITS + 1); 546 | } 547 | // Is the input a subnormal number? 548 | else { 549 | exponent = MIN_EXPONENT - SIGNIFICAND_BITS as i32; 550 | } 551 | 552 | compute_nearest_normal( 553 | two_fc, 554 | exponent, 555 | has_even_significand_bits(signed_significand_bits), 556 | ) 557 | } 558 | -------------------------------------------------------------------------------- /src/cache.rs: -------------------------------------------------------------------------------- 1 | // Translated from C++ to Rust. The original C++ code can be found at 2 | // https://github.com/jk-jeon/dragonbox and carries the following license: 3 | // 4 | // Copyright 2020-2021 Junekey Jeon 5 | // 6 | // The contents of this file may be used under the terms of 7 | // the Apache License v2.0 with LLVM Exceptions. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | pub(crate) type EntryType = u128; 22 | const MIN_K: i32 = -292; 23 | const MAX_K: i32 = 326; 24 | 25 | pub(crate) unsafe fn get(k: i32) -> EntryType { 26 | debug_assert!(k >= MIN_K && k <= MAX_K); 27 | *CACHE.get_unchecked((k - MIN_K) as usize) 28 | } 29 | 30 | pub(crate) trait EntryTypeExt { 31 | fn high(&self) -> u64; 32 | fn low(&self) -> u64; 33 | } 34 | 35 | impl EntryTypeExt for EntryType { 36 | fn high(&self) -> u64 { 37 | (self >> 64) as u64 38 | } 39 | fn low(&self) -> u64 { 40 | *self as u64 41 | } 42 | } 43 | 44 | static CACHE: [EntryType; 619] = [ 45 | 0xff77b1fcbebcdc4f_25e8e89c13bb0f7b, 46 | 0x9faacf3df73609b1_77b191618c54e9ad, 47 | 0xc795830d75038c1d_d59df5b9ef6a2418, 48 | 0xf97ae3d0d2446f25_4b0573286b44ad1e, 49 | 0x9becce62836ac577_4ee367f9430aec33, 50 | 0xc2e801fb244576d5_229c41f793cda740, 51 | 0xf3a20279ed56d48a_6b43527578c11110, 52 | 0x9845418c345644d6_830a13896b78aaaa, 53 | 0xbe5691ef416bd60c_23cc986bc656d554, 54 | 0xedec366b11c6cb8f_2cbfbe86b7ec8aa9, 55 | 0x94b3a202eb1c3f39_7bf7d71432f3d6aa, 56 | 0xb9e08a83a5e34f07_daf5ccd93fb0cc54, 57 | 0xe858ad248f5c22c9_d1b3400f8f9cff69, 58 | 0x91376c36d99995be_23100809b9c21fa2, 59 | 0xb58547448ffffb2d_abd40a0c2832a78b, 60 | 0xe2e69915b3fff9f9_16c90c8f323f516d, 61 | 0x8dd01fad907ffc3b_ae3da7d97f6792e4, 62 | 0xb1442798f49ffb4a_99cd11cfdf41779d, 63 | 0xdd95317f31c7fa1d_40405643d711d584, 64 | 0x8a7d3eef7f1cfc52_482835ea666b2573, 65 | 0xad1c8eab5ee43b66_da3243650005eed0, 66 | 0xd863b256369d4a40_90bed43e40076a83, 67 | 0x873e4f75e2224e68_5a7744a6e804a292, 68 | 0xa90de3535aaae202_711515d0a205cb37, 69 | 0xd3515c2831559a83_0d5a5b44ca873e04, 70 | 0x8412d9991ed58091_e858790afe9486c3, 71 | 0xa5178fff668ae0b6_626e974dbe39a873, 72 | 0xce5d73ff402d98e3_fb0a3d212dc81290, 73 | 0x80fa687f881c7f8e_7ce66634bc9d0b9a, 74 | 0xa139029f6a239f72_1c1fffc1ebc44e81, 75 | 0xc987434744ac874e_a327ffb266b56221, 76 | 0xfbe9141915d7a922_4bf1ff9f0062baa9, 77 | 0x9d71ac8fada6c9b5_6f773fc3603db4aa, 78 | 0xc4ce17b399107c22_cb550fb4384d21d4, 79 | 0xf6019da07f549b2b_7e2a53a146606a49, 80 | 0x99c102844f94e0fb_2eda7444cbfc426e, 81 | 0xc0314325637a1939_fa911155fefb5309, 82 | 0xf03d93eebc589f88_793555ab7eba27cb, 83 | 0x96267c7535b763b5_4bc1558b2f3458df, 84 | 0xbbb01b9283253ca2_9eb1aaedfb016f17, 85 | 0xea9c227723ee8bcb_465e15a979c1cadd, 86 | 0x92a1958a7675175f_0bfacd89ec191eca, 87 | 0xb749faed14125d36_cef980ec671f667c, 88 | 0xe51c79a85916f484_82b7e12780e7401b, 89 | 0x8f31cc0937ae58d2_d1b2ecb8b0908811, 90 | 0xb2fe3f0b8599ef07_861fa7e6dcb4aa16, 91 | 0xdfbdcece67006ac9_67a791e093e1d49b, 92 | 0x8bd6a141006042bd_e0c8bb2c5c6d24e1, 93 | 0xaecc49914078536d_58fae9f773886e19, 94 | 0xda7f5bf590966848_af39a475506a899f, 95 | 0x888f99797a5e012d_6d8406c952429604, 96 | 0xaab37fd7d8f58178_c8e5087ba6d33b84, 97 | 0xd5605fcdcf32e1d6_fb1e4a9a90880a65, 98 | 0x855c3be0a17fcd26_5cf2eea09a550680, 99 | 0xa6b34ad8c9dfc06f_f42faa48c0ea481f, 100 | 0xd0601d8efc57b08b_f13b94daf124da27, 101 | 0x823c12795db6ce57_76c53d08d6b70859, 102 | 0xa2cb1717b52481ed_54768c4b0c64ca6f, 103 | 0xcb7ddcdda26da268_a9942f5dcf7dfd0a, 104 | 0xfe5d54150b090b02_d3f93b35435d7c4d, 105 | 0x9efa548d26e5a6e1_c47bc5014a1a6db0, 106 | 0xc6b8e9b0709f109a_359ab6419ca1091c, 107 | 0xf867241c8cc6d4c0_c30163d203c94b63, 108 | 0x9b407691d7fc44f8_79e0de63425dcf1e, 109 | 0xc21094364dfb5636_985915fc12f542e5, 110 | 0xf294b943e17a2bc4_3e6f5b7b17b2939e, 111 | 0x979cf3ca6cec5b5a_a705992ceecf9c43, 112 | 0xbd8430bd08277231_50c6ff782a838354, 113 | 0xece53cec4a314ebd_a4f8bf5635246429, 114 | 0x940f4613ae5ed136_871b7795e136be9a, 115 | 0xb913179899f68584_28e2557b59846e40, 116 | 0xe757dd7ec07426e5_331aeada2fe589d0, 117 | 0x9096ea6f3848984f_3ff0d2c85def7622, 118 | 0xb4bca50b065abe63_0fed077a756b53aa, 119 | 0xe1ebce4dc7f16dfb_d3e8495912c62895, 120 | 0x8d3360f09cf6e4bd_64712dd7abbbd95d, 121 | 0xb080392cc4349dec_bd8d794d96aacfb4, 122 | 0xdca04777f541c567_ecf0d7a0fc5583a1, 123 | 0x89e42caaf9491b60_f41686c49db57245, 124 | 0xac5d37d5b79b6239_311c2875c522ced6, 125 | 0xd77485cb25823ac7_7d633293366b828c, 126 | 0x86a8d39ef77164bc_ae5dff9c02033198, 127 | 0xa8530886b54dbdeb_d9f57f830283fdfd, 128 | 0xd267caa862a12d66_d072df63c324fd7c, 129 | 0x8380dea93da4bc60_4247cb9e59f71e6e, 130 | 0xa46116538d0deb78_52d9be85f074e609, 131 | 0xcd795be870516656_67902e276c921f8c, 132 | 0x806bd9714632dff6_00ba1cd8a3db53b7, 133 | 0xa086cfcd97bf97f3_80e8a40eccd228a5, 134 | 0xc8a883c0fdaf7df0_6122cd128006b2ce, 135 | 0xfad2a4b13d1b5d6c_796b805720085f82, 136 | 0x9cc3a6eec6311a63_cbe3303674053bb1, 137 | 0xc3f490aa77bd60fc_bedbfc4411068a9d, 138 | 0xf4f1b4d515acb93b_ee92fb5515482d45, 139 | 0x991711052d8bf3c5_751bdd152d4d1c4b, 140 | 0xbf5cd54678eef0b6_d262d45a78a0635e, 141 | 0xef340a98172aace4_86fb897116c87c35, 142 | 0x9580869f0e7aac0e_d45d35e6ae3d4da1, 143 | 0xbae0a846d2195712_8974836059cca10a, 144 | 0xe998d258869facd7_2bd1a438703fc94c, 145 | 0x91ff83775423cc06_7b6306a34627ddd0, 146 | 0xb67f6455292cbf08_1a3bc84c17b1d543, 147 | 0xe41f3d6a7377eeca_20caba5f1d9e4a94, 148 | 0x8e938662882af53e_547eb47b7282ee9d, 149 | 0xb23867fb2a35b28d_e99e619a4f23aa44, 150 | 0xdec681f9f4c31f31_6405fa00e2ec94d5, 151 | 0x8b3c113c38f9f37e_de83bc408dd3dd05, 152 | 0xae0b158b4738705e_9624ab50b148d446, 153 | 0xd98ddaee19068c76_3badd624dd9b0958, 154 | 0x87f8a8d4cfa417c9_e54ca5d70a80e5d7, 155 | 0xa9f6d30a038d1dbc_5e9fcf4ccd211f4d, 156 | 0xd47487cc8470652b_7647c32000696720, 157 | 0x84c8d4dfd2c63f3b_29ecd9f40041e074, 158 | 0xa5fb0a17c777cf09_f468107100525891, 159 | 0xcf79cc9db955c2cc_7182148d4066eeb5, 160 | 0x81ac1fe293d599bf_c6f14cd848405531, 161 | 0xa21727db38cb002f_b8ada00e5a506a7d, 162 | 0xca9cf1d206fdc03b_a6d90811f0e4851d, 163 | 0xfd442e4688bd304a_908f4a166d1da664, 164 | 0x9e4a9cec15763e2e_9a598e4e043287ff, 165 | 0xc5dd44271ad3cdba_40eff1e1853f29fe, 166 | 0xf7549530e188c128_d12bee59e68ef47d, 167 | 0x9a94dd3e8cf578b9_82bb74f8301958cf, 168 | 0xc13a148e3032d6e7_e36a52363c1faf02, 169 | 0xf18899b1bc3f8ca1_dc44e6c3cb279ac2, 170 | 0x96f5600f15a7b7e5_29ab103a5ef8c0ba, 171 | 0xbcb2b812db11a5de_7415d448f6b6f0e8, 172 | 0xebdf661791d60f56_111b495b3464ad22, 173 | 0x936b9fcebb25c995_cab10dd900beec35, 174 | 0xb84687c269ef3bfb_3d5d514f40eea743, 175 | 0xe65829b3046b0afa_0cb4a5a3112a5113, 176 | 0x8ff71a0fe2c2e6dc_47f0e785eaba72ac, 177 | 0xb3f4e093db73a093_59ed216765690f57, 178 | 0xe0f218b8d25088b8_306869c13ec3532d, 179 | 0x8c974f7383725573_1e414218c73a13fc, 180 | 0xafbd2350644eeacf_e5d1929ef90898fb, 181 | 0xdbac6c247d62a583_df45f746b74abf3a, 182 | 0x894bc396ce5da772_6b8bba8c328eb784, 183 | 0xab9eb47c81f5114f_066ea92f3f326565, 184 | 0xd686619ba27255a2_c80a537b0efefebe, 185 | 0x8613fd0145877585_bd06742ce95f5f37, 186 | 0xa798fc4196e952e7_2c48113823b73705, 187 | 0xd17f3b51fca3a7a0_f75a15862ca504c6, 188 | 0x82ef85133de648c4_9a984d73dbe722fc, 189 | 0xa3ab66580d5fdaf5_c13e60d0d2e0ebbb, 190 | 0xcc963fee10b7d1b3_318df905079926a9, 191 | 0xffbbcfe994e5c61f_fdf17746497f7053, 192 | 0x9fd561f1fd0f9bd3_feb6ea8bedefa634, 193 | 0xc7caba6e7c5382c8_fe64a52ee96b8fc1, 194 | 0xf9bd690a1b68637b_3dfdce7aa3c673b1, 195 | 0x9c1661a651213e2d_06bea10ca65c084f, 196 | 0xc31bfa0fe5698db8_486e494fcff30a63, 197 | 0xf3e2f893dec3f126_5a89dba3c3efccfb, 198 | 0x986ddb5c6b3a76b7_f89629465a75e01d, 199 | 0xbe89523386091465_f6bbb397f1135824, 200 | 0xee2ba6c0678b597f_746aa07ded582e2d, 201 | 0x94db483840b717ef_a8c2a44eb4571cdd, 202 | 0xba121a4650e4ddeb_92f34d62616ce414, 203 | 0xe896a0d7e51e1566_77b020baf9c81d18, 204 | 0x915e2486ef32cd60_0ace1474dc1d122f, 205 | 0xb5b5ada8aaff80b8_0d819992132456bb, 206 | 0xe3231912d5bf60e6_10e1fff697ed6c6a, 207 | 0x8df5efabc5979c8f_ca8d3ffa1ef463c2, 208 | 0xb1736b96b6fd83b3_bd308ff8a6b17cb3, 209 | 0xddd0467c64bce4a0_ac7cb3f6d05ddbdf, 210 | 0x8aa22c0dbef60ee4_6bcdf07a423aa96c, 211 | 0xad4ab7112eb3929d_86c16c98d2c953c7, 212 | 0xd89d64d57a607744_e871c7bf077ba8b8, 213 | 0x87625f056c7c4a8b_11471cd764ad4973, 214 | 0xa93af6c6c79b5d2d_d598e40d3dd89bd0, 215 | 0xd389b47879823479_4aff1d108d4ec2c4, 216 | 0x843610cb4bf160cb_cedf722a585139bb, 217 | 0xa54394fe1eedb8fe_c2974eb4ee658829, 218 | 0xce947a3da6a9273e_733d226229feea33, 219 | 0x811ccc668829b887_0806357d5a3f5260, 220 | 0xa163ff802a3426a8_ca07c2dcb0cf26f8, 221 | 0xc9bcff6034c13052_fc89b393dd02f0b6, 222 | 0xfc2c3f3841f17c67_bbac2078d443ace3, 223 | 0x9d9ba7832936edc0_d54b944b84aa4c0e, 224 | 0xc5029163f384a931_0a9e795e65d4df12, 225 | 0xf64335bcf065d37d_4d4617b5ff4a16d6, 226 | 0x99ea0196163fa42e_504bced1bf8e4e46, 227 | 0xc06481fb9bcf8d39_e45ec2862f71e1d7, 228 | 0xf07da27a82c37088_5d767327bb4e5a4d, 229 | 0x964e858c91ba2655_3a6a07f8d510f870, 230 | 0xbbe226efb628afea_890489f70a55368c, 231 | 0xeadab0aba3b2dbe5_2b45ac74ccea842f, 232 | 0x92c8ae6b464fc96f_3b0b8bc90012929e, 233 | 0xb77ada0617e3bbcb_09ce6ebb40173745, 234 | 0xe55990879ddcaabd_cc420a6a101d0516, 235 | 0x8f57fa54c2a9eab6_9fa946824a12232e, 236 | 0xb32df8e9f3546564_47939822dc96abfa, 237 | 0xdff9772470297ebd_59787e2b93bc56f8, 238 | 0x8bfbea76c619ef36_57eb4edb3c55b65b, 239 | 0xaefae51477a06b03_ede622920b6b23f2, 240 | 0xdab99e59958885c4_e95fab368e45ecee, 241 | 0x88b402f7fd75539b_11dbcb0218ebb415, 242 | 0xaae103b5fcd2a881_d652bdc29f26a11a, 243 | 0xd59944a37c0752a2_4be76d3346f04960, 244 | 0x857fcae62d8493a5_6f70a4400c562ddc, 245 | 0xa6dfbd9fb8e5b88e_cb4ccd500f6bb953, 246 | 0xd097ad07a71f26b2_7e2000a41346a7a8, 247 | 0x825ecc24c873782f_8ed400668c0c28c9, 248 | 0xa2f67f2dfa90563b_728900802f0f32fb, 249 | 0xcbb41ef979346bca_4f2b40a03ad2ffba, 250 | 0xfea126b7d78186bc_e2f610c84987bfa9, 251 | 0x9f24b832e6b0f436_0dd9ca7d2df4d7ca, 252 | 0xc6ede63fa05d3143_91503d1c79720dbc, 253 | 0xf8a95fcf88747d94_75a44c6397ce912b, 254 | 0x9b69dbe1b548ce7c_c986afbe3ee11abb, 255 | 0xc24452da229b021b_fbe85badce996169, 256 | 0xf2d56790ab41c2a2_fae27299423fb9c4, 257 | 0x97c560ba6b0919a5_dccd879fc967d41b, 258 | 0xbdb6b8e905cb600f_5400e987bbc1c921, 259 | 0xed246723473e3813_290123e9aab23b69, 260 | 0x9436c0760c86e30b_f9a0b6720aaf6522, 261 | 0xb94470938fa89bce_f808e40e8d5b3e6a, 262 | 0xe7958cb87392c2c2_b60b1d1230b20e05, 263 | 0x90bd77f3483bb9b9_b1c6f22b5e6f48c3, 264 | 0xb4ecd5f01a4aa828_1e38aeb6360b1af4, 265 | 0xe2280b6c20dd5232_25c6da63c38de1b1, 266 | 0x8d590723948a535f_579c487e5a38ad0f, 267 | 0xb0af48ec79ace837_2d835a9df0c6d852, 268 | 0xdcdb1b2798182244_f8e431456cf88e66, 269 | 0x8a08f0f8bf0f156b_1b8e9ecb641b5900, 270 | 0xac8b2d36eed2dac5_e272467e3d222f40, 271 | 0xd7adf884aa879177_5b0ed81dcc6abb10, 272 | 0x86ccbb52ea94baea_98e947129fc2b4ea, 273 | 0xa87fea27a539e9a5_3f2398d747b36225, 274 | 0xd29fe4b18e88640e_8eec7f0d19a03aae, 275 | 0x83a3eeeef9153e89_1953cf68300424ad, 276 | 0xa48ceaaab75a8e2b_5fa8c3423c052dd8, 277 | 0xcdb02555653131b6_3792f412cb06794e, 278 | 0x808e17555f3ebf11_e2bbd88bbee40bd1, 279 | 0xa0b19d2ab70e6ed6_5b6aceaeae9d0ec5, 280 | 0xc8de047564d20a8b_f245825a5a445276, 281 | 0xfb158592be068d2e_eed6e2f0f0d56713, 282 | 0x9ced737bb6c4183d_55464dd69685606c, 283 | 0xc428d05aa4751e4c_aa97e14c3c26b887, 284 | 0xf53304714d9265df_d53dd99f4b3066a9, 285 | 0x993fe2c6d07b7fab_e546a8038efe402a, 286 | 0xbf8fdb78849a5f96_de98520472bdd034, 287 | 0xef73d256a5c0f77c_963e66858f6d4441, 288 | 0x95a8637627989aad_dde7001379a44aa9, 289 | 0xbb127c53b17ec159_5560c018580d5d53, 290 | 0xe9d71b689dde71af_aab8f01e6e10b4a7, 291 | 0x9226712162ab070d_cab3961304ca70e9, 292 | 0xb6b00d69bb55c8d1_3d607b97c5fd0d23, 293 | 0xe45c10c42a2b3b05_8cb89a7db77c506b, 294 | 0x8eb98a7a9a5b04e3_77f3608e92adb243, 295 | 0xb267ed1940f1c61c_55f038b237591ed4, 296 | 0xdf01e85f912e37a3_6b6c46dec52f6689, 297 | 0x8b61313bbabce2c6_2323ac4b3b3da016, 298 | 0xae397d8aa96c1b77_abec975e0a0d081b, 299 | 0xd9c7dced53c72255_96e7bd358c904a22, 300 | 0x881cea14545c7575_7e50d64177da2e55, 301 | 0xaa242499697392d2_dde50bd1d5d0b9ea, 302 | 0xd4ad2dbfc3d07787_955e4ec64b44e865, 303 | 0x84ec3c97da624ab4_bd5af13bef0b113f, 304 | 0xa6274bbdd0fadd61_ecb1ad8aeacdd58f, 305 | 0xcfb11ead453994ba_67de18eda5814af3, 306 | 0x81ceb32c4b43fcf4_80eacf948770ced8, 307 | 0xa2425ff75e14fc31_a1258379a94d028e, 308 | 0xcad2f7f5359a3b3e_096ee45813a04331, 309 | 0xfd87b5f28300ca0d_8bca9d6e188853fd, 310 | 0x9e74d1b791e07e48_775ea264cf55347e, 311 | 0xc612062576589dda_95364afe032a819e, 312 | 0xf79687aed3eec551_3a83ddbd83f52205, 313 | 0x9abe14cd44753b52_c4926a9672793543, 314 | 0xc16d9a0095928a27_75b7053c0f178294, 315 | 0xf1c90080baf72cb1_5324c68b12dd6339, 316 | 0x971da05074da7bee_d3f6fc16ebca5e04, 317 | 0xbce5086492111aea_88f4bb1ca6bcf585, 318 | 0xec1e4a7db69561a5_2b31e9e3d06c32e6, 319 | 0x9392ee8e921d5d07_3aff322e62439fd0, 320 | 0xb877aa3236a4b449_09befeb9fad487c3, 321 | 0xe69594bec44de15b_4c2ebe687989a9b4, 322 | 0x901d7cf73ab0acd9_0f9d37014bf60a11, 323 | 0xb424dc35095cd80f_538484c19ef38c95, 324 | 0xe12e13424bb40e13_2865a5f206b06fba, 325 | 0x8cbccc096f5088cb_f93f87b7442e45d4, 326 | 0xafebff0bcb24aafe_f78f69a51539d749, 327 | 0xdbe6fecebdedd5be_b573440e5a884d1c, 328 | 0x89705f4136b4a597_31680a88f8953031, 329 | 0xabcc77118461cefc_fdc20d2b36ba7c3e, 330 | 0xd6bf94d5e57a42bc_3d32907604691b4d, 331 | 0x8637bd05af6c69b5_a63f9a49c2c1b110, 332 | 0xa7c5ac471b478423_0fcf80dc33721d54, 333 | 0xd1b71758e219652b_d3c36113404ea4a9, 334 | 0x83126e978d4fdf3b_645a1cac083126ea, 335 | 0xa3d70a3d70a3d70a_3d70a3d70a3d70a4, 336 | 0xcccccccccccccccc_cccccccccccccccd, 337 | 0x8000000000000000_0000000000000000, 338 | 0xa000000000000000_0000000000000000, 339 | 0xc800000000000000_0000000000000000, 340 | 0xfa00000000000000_0000000000000000, 341 | 0x9c40000000000000_0000000000000000, 342 | 0xc350000000000000_0000000000000000, 343 | 0xf424000000000000_0000000000000000, 344 | 0x9896800000000000_0000000000000000, 345 | 0xbebc200000000000_0000000000000000, 346 | 0xee6b280000000000_0000000000000000, 347 | 0x9502f90000000000_0000000000000000, 348 | 0xba43b74000000000_0000000000000000, 349 | 0xe8d4a51000000000_0000000000000000, 350 | 0x9184e72a00000000_0000000000000000, 351 | 0xb5e620f480000000_0000000000000000, 352 | 0xe35fa931a0000000_0000000000000000, 353 | 0x8e1bc9bf04000000_0000000000000000, 354 | 0xb1a2bc2ec5000000_0000000000000000, 355 | 0xde0b6b3a76400000_0000000000000000, 356 | 0x8ac7230489e80000_0000000000000000, 357 | 0xad78ebc5ac620000_0000000000000000, 358 | 0xd8d726b7177a8000_0000000000000000, 359 | 0x878678326eac9000_0000000000000000, 360 | 0xa968163f0a57b400_0000000000000000, 361 | 0xd3c21bcecceda100_0000000000000000, 362 | 0x84595161401484a0_0000000000000000, 363 | 0xa56fa5b99019a5c8_0000000000000000, 364 | 0xcecb8f27f4200f3a_0000000000000000, 365 | 0x813f3978f8940984_4000000000000000, 366 | 0xa18f07d736b90be5_5000000000000000, 367 | 0xc9f2c9cd04674ede_a400000000000000, 368 | 0xfc6f7c4045812296_4d00000000000000, 369 | 0x9dc5ada82b70b59d_f020000000000000, 370 | 0xc5371912364ce305_6c28000000000000, 371 | 0xf684df56c3e01bc6_c732000000000000, 372 | 0x9a130b963a6c115c_3c7f400000000000, 373 | 0xc097ce7bc90715b3_4b9f100000000000, 374 | 0xf0bdc21abb48db20_1e86d40000000000, 375 | 0x96769950b50d88f4_1314448000000000, 376 | 0xbc143fa4e250eb31_17d955a000000000, 377 | 0xeb194f8e1ae525fd_5dcfab0800000000, 378 | 0x92efd1b8d0cf37be_5aa1cae500000000, 379 | 0xb7abc627050305ad_f14a3d9e40000000, 380 | 0xe596b7b0c643c719_6d9ccd05d0000000, 381 | 0x8f7e32ce7bea5c6f_e4820023a2000000, 382 | 0xb35dbf821ae4f38b_dda2802c8a800000, 383 | 0xe0352f62a19e306e_d50b2037ad200000, 384 | 0x8c213d9da502de45_4526f422cc340000, 385 | 0xaf298d050e4395d6_9670b12b7f410000, 386 | 0xdaf3f04651d47b4c_3c0cdd765f114000, 387 | 0x88d8762bf324cd0f_a5880a69fb6ac800, 388 | 0xab0e93b6efee0053_8eea0d047a457a00, 389 | 0xd5d238a4abe98068_72a4904598d6d880, 390 | 0x85a36366eb71f041_47a6da2b7f864750, 391 | 0xa70c3c40a64e6c51_999090b65f67d924, 392 | 0xd0cf4b50cfe20765_fff4b4e3f741cf6d, 393 | 0x82818f1281ed449f_bff8f10e7a8921a4, 394 | 0xa321f2d7226895c7_aff72d52192b6a0d, 395 | 0xcbea6f8ceb02bb39_9bf4f8a69f764490, 396 | 0xfee50b7025c36a08_02f236d04753d5b4, 397 | 0x9f4f2726179a2245_01d762422c946590, 398 | 0xc722f0ef9d80aad6_424d3ad2b7b97ef5, 399 | 0xf8ebad2b84e0d58b_d2e0898765a7deb2, 400 | 0x9b934c3b330c8577_63cc55f49f88eb2f, 401 | 0xc2781f49ffcfa6d5_3cbf6b71c76b25fb, 402 | 0xf316271c7fc3908a_8bef464e3945ef7a, 403 | 0x97edd871cfda3a56_97758bf0e3cbb5ac, 404 | 0xbde94e8e43d0c8ec_3d52eeed1cbea317, 405 | 0xed63a231d4c4fb27_4ca7aaa863ee4bdd, 406 | 0x945e455f24fb1cf8_8fe8caa93e74ef6a, 407 | 0xb975d6b6ee39e436_b3e2fd538e122b44, 408 | 0xe7d34c64a9c85d44_60dbbca87196b616, 409 | 0x90e40fbeea1d3a4a_bc8955e946fe31cd, 410 | 0xb51d13aea4a488dd_6babab6398bdbe41, 411 | 0xe264589a4dcdab14_c696963c7eed2dd1, 412 | 0x8d7eb76070a08aec_fc1e1de5cf543ca2, 413 | 0xb0de65388cc8ada8_3b25a55f43294bcb, 414 | 0xdd15fe86affad912_49ef0eb713f39ebe, 415 | 0x8a2dbf142dfcc7ab_6e3569326c784337, 416 | 0xacb92ed9397bf996_49c2c37f07965404, 417 | 0xd7e77a8f87daf7fb_dc33745ec97be906, 418 | 0x86f0ac99b4e8dafd_69a028bb3ded71a3, 419 | 0xa8acd7c0222311bc_c40832ea0d68ce0c, 420 | 0xd2d80db02aabd62b_f50a3fa490c30190, 421 | 0x83c7088e1aab65db_792667c6da79e0fa, 422 | 0xa4b8cab1a1563f52_577001b891185938, 423 | 0xcde6fd5e09abcf26_ed4c0226b55e6f86, 424 | 0x80b05e5ac60b6178_544f8158315b05b4, 425 | 0xa0dc75f1778e39d6_696361ae3db1c721, 426 | 0xc913936dd571c84c_03bc3a19cd1e38e9, 427 | 0xfb5878494ace3a5f_04ab48a04065c723, 428 | 0x9d174b2dcec0e47b_62eb0d64283f9c76, 429 | 0xc45d1df942711d9a_3ba5d0bd324f8394, 430 | 0xf5746577930d6500_ca8f44ec7ee36479, 431 | 0x9968bf6abbe85f20_7e998b13cf4e1ecb, 432 | 0xbfc2ef456ae276e8_9e3fedd8c321a67e, 433 | 0xefb3ab16c59b14a2_c5cfe94ef3ea101e, 434 | 0x95d04aee3b80ece5_bba1f1d158724a12, 435 | 0xbb445da9ca61281f_2a8a6e45ae8edc97, 436 | 0xea1575143cf97226_f52d09d71a3293bd, 437 | 0x924d692ca61be758_593c2626705f9c56, 438 | 0xb6e0c377cfa2e12e_6f8b2fb00c77836c, 439 | 0xe498f455c38b997a_0b6dfb9c0f956447, 440 | 0x8edf98b59a373fec_4724bd4189bd5eac, 441 | 0xb2977ee300c50fe7_58edec91ec2cb657, 442 | 0xdf3d5e9bc0f653e1_2f2967b66737e3ed, 443 | 0x8b865b215899f46c_bd79e0d20082ee74, 444 | 0xae67f1e9aec07187_ecd8590680a3aa11, 445 | 0xda01ee641a708de9_e80e6f4820cc9495, 446 | 0x884134fe908658b2_3109058d147fdcdd, 447 | 0xaa51823e34a7eede_bd4b46f0599fd415, 448 | 0xd4e5e2cdc1d1ea96_6c9e18ac7007c91a, 449 | 0x850fadc09923329e_03e2cf6bc604ddb0, 450 | 0xa6539930bf6bff45_84db8346b786151c, 451 | 0xcfe87f7cef46ff16_e612641865679a63, 452 | 0x81f14fae158c5f6e_4fcb7e8f3f60c07e, 453 | 0xa26da3999aef7749_e3be5e330f38f09d, 454 | 0xcb090c8001ab551c_5cadf5bfd3072cc5, 455 | 0xfdcb4fa002162a63_73d9732fc7c8f7f6, 456 | 0x9e9f11c4014dda7e_2867e7fddcdd9afa, 457 | 0xc646d63501a1511d_b281e1fd541501b8, 458 | 0xf7d88bc24209a565_1f225a7ca91a4226, 459 | 0x9ae757596946075f_3375788de9b06958, 460 | 0xc1a12d2fc3978937_0052d6b1641c83ae, 461 | 0xf209787bb47d6b84_c0678c5dbd23a49a, 462 | 0x9745eb4d50ce6332_f840b7ba963646e0, 463 | 0xbd176620a501fbff_b650e5a93bc3d898, 464 | 0xec5d3fa8ce427aff_a3e51f138ab4cebe, 465 | 0x93ba47c980e98cdf_c66f336c36b10137, 466 | 0xb8a8d9bbe123f017_b80b0047445d4184, 467 | 0xe6d3102ad96cec1d_a60dc059157491e5, 468 | 0x9043ea1ac7e41392_87c89837ad68db2f, 469 | 0xb454e4a179dd1877_29babe4598c311fb, 470 | 0xe16a1dc9d8545e94_f4296dd6fef3d67a, 471 | 0x8ce2529e2734bb1d_1899e4a65f58660c, 472 | 0xb01ae745b101e9e4_5ec05dcff72e7f8f, 473 | 0xdc21a1171d42645d_76707543f4fa1f73, 474 | 0x899504ae72497eba_6a06494a791c53a8, 475 | 0xabfa45da0edbde69_0487db9d17636892, 476 | 0xd6f8d7509292d603_45a9d2845d3c42b6, 477 | 0x865b86925b9bc5c2_0b8a2392ba45a9b2, 478 | 0xa7f26836f282b732_8e6cac7768d7141e, 479 | 0xd1ef0244af2364ff_3207d795430cd926, 480 | 0x8335616aed761f1f_7f44e6bd49e807b8, 481 | 0xa402b9c5a8d3a6e7_5f16206c9c6209a6, 482 | 0xcd036837130890a1_36dba887c37a8c0f, 483 | 0x802221226be55a64_c2494954da2c9789, 484 | 0xa02aa96b06deb0fd_f2db9baa10b7bd6c, 485 | 0xc83553c5c8965d3d_6f92829494e5acc7, 486 | 0xfa42a8b73abbf48c_cb772339ba1f17f9, 487 | 0x9c69a97284b578d7_ff2a760414536efb, 488 | 0xc38413cf25e2d70d_fef5138519684aba, 489 | 0xf46518c2ef5b8cd1_7eb258665fc25d69, 490 | 0x98bf2f79d5993802_ef2f773ffbd97a61, 491 | 0xbeeefb584aff8603_aafb550ffacfd8fa, 492 | 0xeeaaba2e5dbf6784_95ba2a53f983cf38, 493 | 0x952ab45cfa97a0b2_dd945a747bf26183, 494 | 0xba756174393d88df_94f971119aeef9e4, 495 | 0xe912b9d1478ceb17_7a37cd5601aab85d, 496 | 0x91abb422ccb812ee_ac62e055c10ab33a, 497 | 0xb616a12b7fe617aa_577b986b314d6009, 498 | 0xe39c49765fdf9d94_ed5a7e85fda0b80b, 499 | 0x8e41ade9fbebc27d_14588f13be847307, 500 | 0xb1d219647ae6b31c_596eb2d8ae258fc8, 501 | 0xde469fbd99a05fe3_6fca5f8ed9aef3bb, 502 | 0x8aec23d680043bee_25de7bb9480d5854, 503 | 0xada72ccc20054ae9_af561aa79a10ae6a, 504 | 0xd910f7ff28069da4_1b2ba1518094da04, 505 | 0x87aa9aff79042286_90fb44d2f05d0842, 506 | 0xa99541bf57452b28_353a1607ac744a53, 507 | 0xd3fa922f2d1675f2_42889b8997915ce8, 508 | 0x847c9b5d7c2e09b7_69956135febada11, 509 | 0xa59bc234db398c25_43fab9837e699095, 510 | 0xcf02b2c21207ef2e_94f967e45e03f4bb, 511 | 0x8161afb94b44f57d_1d1be0eebac278f5, 512 | 0xa1ba1ba79e1632dc_6462d92a69731732, 513 | 0xca28a291859bbf93_7d7b8f7503cfdcfe, 514 | 0xfcb2cb35e702af78_5cda735244c3d43e, 515 | 0x9defbf01b061adab_3a0888136afa64a7, 516 | 0xc56baec21c7a1916_088aaa1845b8fdd0, 517 | 0xf6c69a72a3989f5b_8aad549e57273d45, 518 | 0x9a3c2087a63f6399_36ac54e2f678864b, 519 | 0xc0cb28a98fcf3c7f_84576a1bb416a7dd, 520 | 0xf0fdf2d3f3c30b9f_656d44a2a11c51d5, 521 | 0x969eb7c47859e743_9f644ae5a4b1b325, 522 | 0xbc4665b596706114_873d5d9f0dde1fee, 523 | 0xeb57ff22fc0c7959_a90cb506d155a7ea, 524 | 0x9316ff75dd87cbd8_09a7f12442d588f2, 525 | 0xb7dcbf5354e9bece_0c11ed6d538aeb2f, 526 | 0xe5d3ef282a242e81_8f1668c8a86da5fa, 527 | 0x8fa475791a569d10_f96e017d694487bc, 528 | 0xb38d92d760ec4455_37c981dcc395a9ac, 529 | 0xe070f78d3927556a_85bbe253f47b1417, 530 | 0x8c469ab843b89562_93956d7478ccec8e, 531 | 0xaf58416654a6babb_387ac8d1970027b2, 532 | 0xdb2e51bfe9d0696a_06997b05fcc0319e, 533 | 0x88fcf317f22241e2_441fece3bdf81f03, 534 | 0xab3c2fddeeaad25a_d527e81cad7626c3, 535 | 0xd60b3bd56a5586f1_8a71e223d8d3b074, 536 | 0x85c7056562757456_f6872d5667844e49, 537 | 0xa738c6bebb12d16c_b428f8ac016561db, 538 | 0xd106f86e69d785c7_e13336d701beba52, 539 | 0x82a45b450226b39c_ecc0024661173473, 540 | 0xa34d721642b06084_27f002d7f95d0190, 541 | 0xcc20ce9bd35c78a5_31ec038df7b441f4, 542 | 0xff290242c83396ce_7e67047175a15271, 543 | 0x9f79a169bd203e41_0f0062c6e984d386, 544 | 0xc75809c42c684dd1_52c07b78a3e60868, 545 | 0xf92e0c3537826145_a7709a56ccdf8a82, 546 | 0x9bbcc7a142b17ccb_88a66076400bb691, 547 | 0xc2abf989935ddbfe_6acff893d00ea435, 548 | 0xf356f7ebf83552fe_0583f6b8c4124d43, 549 | 0x98165af37b2153de_c3727a337a8b704a, 550 | 0xbe1bf1b059e9a8d6_744f18c0592e4c5c, 551 | 0xeda2ee1c7064130c_1162def06f79df73, 552 | 0x9485d4d1c63e8be7_8addcb5645ac2ba8, 553 | 0xb9a74a0637ce2ee1_6d953e2bd7173692, 554 | 0xe8111c87c5c1ba99_c8fa8db6ccdd0437, 555 | 0x910ab1d4db9914a0_1d9c9892400a22a2, 556 | 0xb54d5e4a127f59c8_2503beb6d00cab4b, 557 | 0xe2a0b5dc971f303a_2e44ae64840fd61d, 558 | 0x8da471a9de737e24_5ceaecfed289e5d2, 559 | 0xb10d8e1456105dad_7425a83e872c5f47, 560 | 0xdd50f1996b947518_d12f124e28f77719, 561 | 0x8a5296ffe33cc92f_82bd6b70d99aaa6f, 562 | 0xace73cbfdc0bfb7b_636cc64d1001550b, 563 | 0xd8210befd30efa5a_3c47f7e05401aa4e, 564 | 0x8714a775e3e95c78_65acfaec34810a71, 565 | 0xa8d9d1535ce3b396_7f1839a741a14d0d, 566 | 0xd31045a8341ca07c_1ede48111209a050, 567 | 0x83ea2b892091e44d_934aed0aab460432, 568 | 0xa4e4b66b68b65d60_f81da84d5617853f, 569 | 0xce1de40642e3f4b9_36251260ab9d668e, 570 | 0x80d2ae83e9ce78f3_c1d72b7c6b426019, 571 | 0xa1075a24e4421730_b24cf65b8612f81f, 572 | 0xc94930ae1d529cfc_dee033f26797b627, 573 | 0xfb9b7cd9a4a7443c_169840ef017da3b1, 574 | 0x9d412e0806e88aa5_8e1f289560ee864e, 575 | 0xc491798a08a2ad4e_f1a6f2bab92a27e2, 576 | 0xf5b5d7ec8acb58a2_ae10af696774b1db, 577 | 0x9991a6f3d6bf1765_acca6da1e0a8ef29, 578 | 0xbff610b0cc6edd3f_17fd090a58d32af3, 579 | 0xeff394dcff8a948e_ddfc4b4cef07f5b0, 580 | 0x95f83d0a1fb69cd9_4abdaf101564f98e, 581 | 0xbb764c4ca7a4440f_9d6d1ad41abe37f1, 582 | 0xea53df5fd18d5513_84c86189216dc5ed, 583 | 0x92746b9be2f8552c_32fd3cf5b4e49bb4, 584 | 0xb7118682dbb66a77_3fbc8c33221dc2a1, 585 | 0xe4d5e82392a40515_0fabaf3feaa5334a, 586 | 0x8f05b1163ba6832d_29cb4d87f2a7400e, 587 | 0xb2c71d5bca9023f8_743e20e9ef511012, 588 | 0xdf78e4b2bd342cf6_914da9246b255416, 589 | 0x8bab8eefb6409c1a_1ad089b6c2f7548e, 590 | 0xae9672aba3d0c320_a184ac2473b529b1, 591 | 0xda3c0f568cc4f3e8_c9e5d72d90a2741e, 592 | 0x8865899617fb1871_7e2fa67c7a658892, 593 | 0xaa7eebfb9df9de8d_ddbb901b98feeab7, 594 | 0xd51ea6fa85785631_552a74227f3ea565, 595 | 0x8533285c936b35de_d53a88958f87275f, 596 | 0xa67ff273b8460356_8a892abaf368f137, 597 | 0xd01fef10a657842c_2d2b7569b0432d85, 598 | 0x8213f56a67f6b29b_9c3b29620e29fc73, 599 | 0xa298f2c501f45f42_8349f3ba91b47b8f, 600 | 0xcb3f2f7642717713_241c70a936219a73, 601 | 0xfe0efb53d30dd4d7_ed238cd383aa0110, 602 | 0x9ec95d1463e8a506_f4363804324a40aa, 603 | 0xc67bb4597ce2ce48_b143c6053edcd0d5, 604 | 0xf81aa16fdc1b81da_dd94b7868e94050a, 605 | 0x9b10a4e5e9913128_ca7cf2b4191c8326, 606 | 0xc1d4ce1f63f57d72_fd1c2f611f63a3f0, 607 | 0xf24a01a73cf2dccf_bc633b39673c8cec, 608 | 0x976e41088617ca01_d5be0503e085d813, 609 | 0xbd49d14aa79dbc82_4b2d8644d8a74e18, 610 | 0xec9c459d51852ba2_ddf8e7d60ed1219e, 611 | 0x93e1ab8252f33b45_cabb90e5c942b503, 612 | 0xb8da1662e7b00a17_3d6a751f3b936243, 613 | 0xe7109bfba19c0c9d_0cc512670a783ad4, 614 | 0x906a617d450187e2_27fb2b80668b24c5, 615 | 0xb484f9dc9641e9da_b1f9f660802dedf6, 616 | 0xe1a63853bbd26451_5e7873f8a0396973, 617 | 0x8d07e33455637eb2_db0b487b6423e1e8, 618 | 0xb049dc016abc5e5f_91ce1a9a3d2cda62, 619 | 0xdc5c5301c56b75f7_7641a140cc7810fb, 620 | 0x89b9b3e11b6329ba_a9e904c87fcb0a9d, 621 | 0xac2820d9623bf429_546345fa9fbdcd44, 622 | 0xd732290fbacaf133_a97c177947ad4095, 623 | 0x867f59a9d4bed6c0_49ed8eabcccc485d, 624 | 0xa81f301449ee8c70_5c68f256bfff5a74, 625 | 0xd226fc195c6a2f8c_73832eec6fff3111, 626 | 0x83585d8fd9c25db7_c831fd53c5ff7eab, 627 | 0xa42e74f3d032f525_ba3e7ca8b77f5e55, 628 | 0xcd3a1230c43fb26f_28ce1bd2e55f35eb, 629 | 0x80444b5e7aa7cf85_7980d163cf5b81b3, 630 | 0xa0555e361951c366_d7e105bcc332621f, 631 | 0xc86ab5c39fa63440_8dd9472bf3fefaa7, 632 | 0xfa856334878fc150_b14f98f6f0feb951, 633 | 0x9c935e00d4b9d8d2_6ed1bf9a569f33d3, 634 | 0xc3b8358109e84f07_0a862f80ec4700c8, 635 | 0xf4a642e14c6262c8_cd27bb612758c0fa, 636 | 0x98e7e9cccfbd7dbd_8038d51cb897789c, 637 | 0xbf21e44003acdd2c_e0470a63e6bd56c3, 638 | 0xeeea5d5004981478_1858ccfce06cac74, 639 | 0x95527a5202df0ccb_0f37801e0c43ebc8, 640 | 0xbaa718e68396cffd_d30560258f54e6ba, 641 | 0xe950df20247c83fd_47c6b82ef32a2069, 642 | 0x91d28b7416cdd27e_4cdc331d57fa5441, 643 | 0xb6472e511c81471d_e0133fe4adf8e952, 644 | 0xe3d8f9e563a198e5_58180fddd97723a6, 645 | 0x8e679c2f5e44ff8f_570f09eaa7ea7648, 646 | 0xb201833b35d63f73_2cd2cc6551e513da, 647 | 0xde81e40a034bcf4f_f8077f7ea65e58d1, 648 | 0x8b112e86420f6191_fb04afaf27faf782, 649 | 0xadd57a27d29339f6_79c5db9af1f9b563, 650 | 0xd94ad8b1c7380874_18375281ae7822bc, 651 | 0x87cec76f1c830548_8f2293910d0b15b5, 652 | 0xa9c2794ae3a3c69a_b2eb3875504ddb22, 653 | 0xd433179d9c8cb841_5fa60692a46151eb, 654 | 0x849feec281d7f328_dbc7c41ba6bcd333, 655 | 0xa5c7ea73224deff3_12b9b522906c0800, 656 | 0xcf39e50feae16bef_d768226b34870a00, 657 | 0x81842f29f2cce375_e6a1158300d46640, 658 | 0xa1e53af46f801c53_60495ae3c1097fd0, 659 | 0xca5e89b18b602368_385bb19cb14bdfc4, 660 | 0xfcf62c1dee382c42_46729e03dd9ed7b5, 661 | 0x9e19db92b4e31ba9_6c07a2c26a8346d1, 662 | 0xc5a05277621be293_c7098b7305241885, 663 | 0xf70867153aa2db38_b8cbee4fc66d1ea7, 664 | ]; 665 | --------------------------------------------------------------------------------