├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── extras ├── data-tests │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs └── simple-bench │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── main.rs │ └── random.rs ├── fuzz ├── Cargo.toml ├── float.dict └── fuzz_targets │ ├── fast_float.rs │ └── roundtrip_f64.rs ├── src ├── binary.rs ├── common.rs ├── decimal.rs ├── float.rs ├── lib.rs ├── number.rs ├── parse.rs ├── simple.rs └── table.rs └── tests ├── test_api.rs ├── test_basic.rs ├── test_exhaustive.rs └── test_random.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | name: Rust ${{matrix.rust}} 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | rust: [1.37.0, stable, nightly] 15 | steps: 16 | - uses: actions/checkout@v2 17 | with: 18 | submodules: recursive 19 | - uses: dtolnay/rust-toolchain@master 20 | with: 21 | toolchain: ${{matrix.rust}} 22 | - run: cargo check 23 | - run: cargo test 24 | - run: cd extras/data-tests && cargo run --release 25 | 26 | cross: 27 | name: Rust ${{matrix.target}} 28 | runs-on: ubuntu-latest 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | target: 33 | - powerpc-unknown-linux-gnu 34 | - powerpc64le-unknown-linux-gnu 35 | - aarch64-unknown-linux-gnu 36 | - armv7-unknown-linux-gnueabihf 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions-rs/toolchain@v1 40 | with: 41 | toolchain: stable 42 | target: ${{matrix.target}} 43 | override: true 44 | - uses: actions-rs/cargo@v1 45 | with: 46 | use-cross: true 47 | command: check 48 | args: --target ${{matrix.target}} 49 | - uses: actions-rs/cargo@v1 50 | with: 51 | use-cross: true 52 | command: test 53 | args: --target ${{matrix.target}} 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/target/ 3 | Cargo.lock 4 | scratch/ 5 | .idea/ 6 | .DS_Store 7 | corpus/ 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extras/data-tests/ext"] 2 | path = extras/data-tests/ext 3 | url = https://github.com/lemire/fast_float_supplemental_tests.git 4 | 5 | [submodule "extras/simple-bench/ext"] 6 | path = extras/simple-bench/ext 7 | url = https://github.com/lemire/simple_fastfloat_benchmark.git 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.0 2 | 3 | - Fixed an edge case where long decimals with trailing zeros were truncated. 4 | - Minor micro-optimization fixes in the fast path parser. 5 | - Remove the use of unsafe when querying power-of-10 tables. 6 | - Added float64 roundtrip fuzz target. 7 | - Added tests for the power-of-5 table using num-bigint. 8 | - Improvements and new options in the bench tool. 9 | - Updated benchmark timings, added Apple M1 and AMD Rome timings. 10 | 11 | ## 0.1.0 12 | 13 | Initial release, fully tested and benchmarked. 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast-float" 3 | version = "0.2.0" 4 | authors = ["Ivan Smirnov "] 5 | repository = "https://github.com/aldanor/fast-float-rust" 6 | documentation = "https://docs.rs/fast-float" 7 | description = "Fast floating-point number parser." 8 | keywords = ["parser", "parsing", "parse", "float", "no-std"] 9 | categories = ["parser-implementations", "parsing", "text-processing", "algorithms", "no-std"] 10 | readme = "README.md" 11 | license = "MIT OR Apache-2.0" 12 | autobenches = false 13 | edition = "2018" 14 | exclude = ["benches/*", "extras/*"] 15 | 16 | [features] 17 | default = ["std"] 18 | std = [] 19 | 20 | [dev-dependencies] 21 | lexical-core = "0.7" 22 | hexf-parse = "0.1" 23 | ryu = "1.0" 24 | fastrand = "1.4" 25 | num-bigint = "0.3" 26 | 27 | [workspace] 28 | members = [".", "extras/data-tests", "extras/simple-bench"] 29 | 30 | [profile.release] 31 | lto = "fat" 32 | codegen-units = 1 33 | 34 | [package.metadata.docs.rs] 35 | features = ["std"] 36 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 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 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 Ivan Smirnov 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fast-float 2 | ========== 3 | 4 | [![Build](https://github.com/aldanor/fast-float-rust/workflows/CI/badge.svg)](https://github.com/aldanor/fast-float-rust/actions?query=branch%3Amaster) 5 | [![Latest Version](https://img.shields.io/crates/v/fast-float.svg)](https://crates.io/crates/fast-float) 6 | [![Documentation](https://docs.rs/fast-float/badge.svg)](https://docs.rs/fast-float) 7 | [![Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 8 | [![MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 9 | [![Rustc 1.37+](https://img.shields.io/badge/rustc-1.37+-lightgray.svg)](https://blog.rust-lang.org/2019/08/15/Rust-1.37.0.html) 10 | 11 | This crate provides a super-fast decimal number parser from strings into floats. 12 | 13 | ```toml 14 | [dependencies] 15 | fast-float = "0.2" 16 | ``` 17 | 18 | There are no dependencies and the crate can be used in a no_std context by disabling the "std" feature. 19 | 20 | *Compiler support: rustc 1.37+.* 21 | 22 | ## Usage 23 | 24 | There's two top-level functions provided: 25 | [`parse()`](https://docs.rs/fast-float/latest/fast_float/fn.parse.html) and 26 | [`parse_partial()`](https://docs.rs/fast-float/latest/fast_float/fn.parse_partial.html), both taking 27 | either a string or a bytes slice and parsing the input into either `f32` or `f64`: 28 | 29 | - `parse()` treats the whole string as a decimal number and returns an error if there are 30 | invalid characters or if the string is empty. 31 | - `parse_partial()` tries to find the longest substring at the beginning of the given input 32 | string that can be parsed as a decimal number and, in the case of success, returns the parsed 33 | value along the number of characters processed; an error is returned if the string doesn't 34 | start with a decimal number or if it is empty. This function is most useful as a building 35 | block when constructing more complex parsers, or when parsing streams of data. 36 | 37 | Example: 38 | 39 | ```rust 40 | // Parse the entire string as a decimal number. 41 | let s = "1.23e-02"; 42 | let x: f32 = fast_float::parse(s).unwrap(); 43 | assert_eq!(x, 0.0123); 44 | 45 | // Parse as many characters as possible as a decimal number. 46 | let s = "1.23e-02foo"; 47 | let (x, n) = fast_float::parse_partial::(s).unwrap(); 48 | assert_eq!(x, 0.0123); 49 | assert_eq!(n, 8); 50 | assert_eq!(&s[n..], "foo"); 51 | ``` 52 | 53 | ## Details 54 | 55 | This crate is a direct port of Daniel Lemire's [`fast_float`](https://github.com/fastfloat/fast_float) 56 | C++ library (valuable discussions with Daniel while porting it helped shape the crate and get it to 57 | the performance level it's at now), with some Rust-specific tweaks. Please see the original 58 | repository for many useful details regarding the algorithm and the implementation. 59 | 60 | The parser is locale-independent. The resulting value is the closest floating-point values (using either 61 | `f32` or `f64`), using the "round to even" convention for values that would otherwise fall right in-between 62 | two values. That is, we provide exact parsing according to the IEEE standard. 63 | 64 | Infinity and NaN values can be parsed, along with scientific notation. 65 | 66 | Both little-endian and big-endian platforms are equally supported, with extra optimizations enabled 67 | on little-endian architectures. 68 | 69 | ## Testing 70 | 71 | There are a few ways this crate is tested: 72 | 73 | - A suite of explicit tests (taken from the original library) covering lots of edge cases. 74 | - A file-based test suite (taken from the original library; credits to Nigel Tao), ~5M tests. 75 | - All 4B float32 numbers are exhaustively roundtripped via ryu formatter. 76 | - Roundtripping a large quantity of random float64 numbers via ryu formatter. 77 | - Roundtripping float64 numbers and fuzzing random input strings via cargo-fuzz. 78 | - All explicit test suites run on CI; roundtripping and fuzzing are run manually. 79 | 80 | ## Performance 81 | 82 | The presented parser seems to beat all of the existing C/C++/Rust float parsers known to us at the 83 | moment by a large margin, in all of the datasets we tested it on so far – see detailed benchmarks 84 | below (the only exception being the original fast_float C++ library, of course – performance of 85 | which is within noise bounds of this crate). On modern machines like Apple M1, parsing throughput 86 | can reach up to 1.5 GB/s. 87 | 88 | In particular, it is faster than Rust standard library's `FromStr::from_str()` by a factor of 2-8x 89 | (larger factor for longer float strings), and is typically 2-3x faster than the nearest competitors. 90 | 91 | While various details regarding the algorithm can be found in the repository for the original 92 | C++ library, here are few brief notes: 93 | 94 | - The parser is specialized to work lightning-fast on inputs with at most 19 significant digits 95 | (which constitutes the so called "fast-path"). We believe that most real-life inputs should 96 | fall under this category, and we treat longer inputs as "degenerate" edge cases since it 97 | inevitable causes overflows and loss of precision. 98 | - If the significand happens to be longer than 19 digits, the parser falls back to the "slow path", 99 | in which case its performance roughly matches that of the top Rust/C++ libraries (and still 100 | beats them most of the time, although not by a lot). 101 | - On little-endian systems, there's additional optimizations for numbers with more than 8 digits 102 | after the decimal point. 103 | 104 | ## Benchmarks 105 | 106 | Below are tables of best timings in nanoseconds for parsing a single number 107 | into a 64-bit float. 108 | 109 | #### Intel i7-4771 110 | 111 | Intel i7-4771 3.5GHz, macOS, Rust 1.49. 112 | 113 | | | `canada` | `mesh` | `uniform` | `iidi` | `iei` | `rec32` | 114 | | ---------------- | -------- | -------- | --------- | ------ | ------ | ------- | 115 | | fast-float | 21.58 | 10.70 | 19.36 | 40.50 | 26.07 | 29.13 | 116 | | lexical | 65.90 | 23.28 | 54.75 | 75.80 | 52.18 | 75.36 | 117 | | from_str | 174.43 | 22.30 | 99.93 | 227.76 | 111.31 | 204.46 | 118 | | fast_float (C++) | 22.78 | 10.99 | 20.05 | 41.12 | 27.51 | 30.85 | 119 | | abseil (C++) | 42.66 | 32.88 | 46.01 | 50.83 | 46.33 | 49.95 | 120 | | netlib (C) | 57.53 | 24.86 | 64.72 | 56.63 | 36.20 | 67.29 | 121 | | strtod (C) | 286.10 | 31.15 | 258.73 | 295.73 | 205.72 | 315.95 | 122 | 123 | #### Apple M1 124 | 125 | Apple M1, macOS, Rust 1.49. 126 | 127 | | | `canada` | `mesh` | `uniform` | `iidi` | `iei` | `rec32` | 128 | | ---------------- | -------- | -------- | --------- | ------ | ------ | ------- | 129 | | fast-float | 14.84 | 5.98 | 11.24 | 33.24 | 21.30 | 17.86 | 130 | | lexical | 47.09 | 16.51 | 43.46 | 56.06 | 36.68 | 55.48 | 131 | | from_str | 136.00 | 13.84 | 74.64 | 179.87 | 77.91 | 154.53 | 132 | | fast_float (C++) | 13.71 | 7.28 | 11.71 | 32.94 | 20.64 | 18.30 | 133 | | abseil (C++) | 36.55 | 24.20 | 38.48 | 40.86 | 35.46 | 40.09 | 134 | | netlib (C) | 47.19 | 14.12 | 48.85 | 52.28 | 33.70 | 48.79 | 135 | | strtod (C) | 176.13 | 21.48 | 165.43 | 187.98 | 132.19 | 190.63 | 136 | 137 | #### AMD Rome 138 | 139 | AMD Rome, Linux, Rust 1.49. 140 | 141 | | | `canada` | `mesh` | `uniform` | `iidi` | `iei` | `rec32` | 142 | | ---------------- | -------- | -------- | --------- | ------ | ------ | ------- | 143 | | fast-float | 25.90 | 12.12 | 20.54 | 47.01 | 29.23 | 32.36 | 144 | | lexical | 63.18 | 22.13 | 54.78 | 81.23 | 55.06 | 79.14 | 145 | | from_str | 190.06 | 26.10 | 102.44 | 239.87 | 119.04 | 211.73 | 146 | | fast_float (C++) | 21.29 | 10.47 | 18.31 | 42.33 | 24.56 | 29.76 | 147 | | abseil (C++) | 44.54 | 34.13 | 47.38 | 52.64 | 43.77 | 53.03 | 148 | | netlib (C) | 69.43 | 23.31 | 79.98 | 72.17 | 35.81 | 86.91 | 149 | | strtod (C) | 123.37 | 65.68 | 101.58 | 118.36 | 118.61 | 123.72 | 150 | 151 | #### Parsers 152 | 153 | - `fast-float` - this very crate 154 | - `lexical` – `lexical_core`, v0.7 (non-lossy; same performance as lossy) 155 | - `from_str` – Rust standard library, `FromStr` trait 156 | - `fast_float (C++)` – original C++ implementation of 'fast-float' method 157 | - `abseil (C++)` – Abseil C++ Common Libraries 158 | - `netlib (C++)` – C++ Network Library 159 | - `strtod (C)` – C standard library 160 | 161 | #### Datasets 162 | 163 | - `canada` – numbers in `canada.txt` file 164 | - `mesh` – numbers in `mesh.txt` file 165 | - `uniform` – uniform random numbers from 0 to 1 166 | - `iidi` – random numbers of format `%d%d.%d` 167 | - `iei` – random numbers of format `%de%d` 168 | - `rec32` – reciprocals of random 32-bit integers 169 | 170 | #### Notes 171 | 172 | - The two test files referred above can be found in 173 | [this](https://github.com/lemire/simple_fastfloat_benchmark) repository. 174 | - The Rust part of the table (along with a few other benchmarks) can be generated via 175 | the benchmark tool that can be found under `extras/simple-bench` of this repo. 176 | - The C/C++ part of the table (along with a few other benchmarks and parsers) can be 177 | generated via a C++ utility that can be found in 178 | [this](https://github.com/lemire/simple_fastfloat_benchmark) repository. 179 | 180 |
181 | 182 | #### References 183 | 184 | - Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021. 185 | 186 | #### License 187 | 188 | 189 | Licensed under either of Apache License, Version 190 | 2.0 or MIT license at your option. 191 | 192 | 193 |
194 | 195 | 196 | Unless you explicitly state otherwise, any contribution intentionally submitted 197 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 198 | be dual licensed as above, without any additional terms or conditions. 199 | 200 | -------------------------------------------------------------------------------- /extras/data-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast-float-data-tests" 3 | version = "0.1.0" 4 | authors = ["Ivan Smirnov "] 5 | edition = "2018" 6 | readme = "README.md" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [dependencies] 11 | fast-float = { path = "../.." } 12 | -------------------------------------------------------------------------------- /extras/data-tests/README.md: -------------------------------------------------------------------------------- 1 | This crate allows running the test based on files with test cases stored in the 2 | standardized format (credit to Daniel Lemire and Nigel Tao for the test cases). 3 | The test data is sourced from [this](https://github.com/lemire/fast_float_supplemental_tests) 4 | repository which is used for the original fast_float C++ library tests. 5 | 6 | Test data files can be found under `ext/data`. 7 | 8 | To run the tests: 9 | 10 | ```sh 11 | cargo run --release 12 | ``` 13 | -------------------------------------------------------------------------------- /extras/data-tests/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{read_dir, File}; 2 | use std::io::{BufRead, BufReader}; 3 | use std::mem::transmute; 4 | use std::path::{Path, PathBuf}; 5 | 6 | #[derive(Clone, Debug)] 7 | struct TestCase { 8 | float32: f32, 9 | float64: f64, 10 | string: String, 11 | } 12 | 13 | impl TestCase { 14 | pub fn parse(string: String) -> Self { 15 | let float32 = unsafe { transmute(u32::from_str_radix(&string[5..13], 16).unwrap()) }; 16 | let float64 = unsafe { transmute(u64::from_str_radix(&string[14..30], 16).unwrap()) }; 17 | let string = string[31..].to_string(); 18 | Self { 19 | float32, 20 | float64, 21 | string, 22 | } 23 | } 24 | 25 | fn execute_one(&self, expected: F) { 26 | let r = F::parse_float_partial(&self.string); 27 | if !r.is_ok() { 28 | dbg!(self); 29 | eprintln!("Failed to parse as f32: {:?}", self.string); 30 | } 31 | let (value, len) = r.unwrap(); 32 | if len != self.string.len() || value != expected { 33 | if len != self.string.len() { 34 | eprintln!( 35 | "Expected empty string remainder, got: {:?}", 36 | self.string.len() - len 37 | ); 38 | } 39 | if value != expected { 40 | eprintln!("Expected output {}, got {}", expected, value); 41 | } 42 | panic!("Test case failed: {:?}", self); 43 | } 44 | } 45 | 46 | pub fn execute(&self) { 47 | self.execute_one::(self.float32); 48 | self.execute_one::(self.float64); 49 | } 50 | } 51 | 52 | fn parse_test_file(filename: impl AsRef) -> impl Iterator { 53 | let file = File::open(filename).unwrap(); 54 | BufReader::new(file) 55 | .lines() 56 | .map(Result::unwrap) 57 | .map(TestCase::parse) 58 | } 59 | 60 | fn run_test_cases(filename: impl AsRef) -> usize { 61 | parse_test_file(filename).map(|test| test.execute()).count() 62 | } 63 | 64 | fn test_file_paths() -> Vec { 65 | let test_data_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ext/data"); 66 | let mut paths = read_dir(&test_data_dir) 67 | .unwrap() 68 | .map(|d| d.unwrap().path()) 69 | .filter(|p| { 70 | p.is_file() 71 | && p.extension().map(|e| e.to_str()) == Some(Some("txt")) 72 | && p.file_name().map(|f| f.to_str()) != Some(Some("CMakeLists.txt")) 73 | }) 74 | .collect::>(); 75 | paths.sort(); 76 | paths 77 | } 78 | 79 | fn main() { 80 | for path in &test_file_paths() { 81 | eprint!("Running test cases in {:?}... ", path.file_name().unwrap()); 82 | let count = run_test_cases(path); 83 | eprintln!("{} tests passed.", count); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /extras/simple-bench/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast-float-simple-bench" 3 | version = "0.1.0" 4 | authors = ["Ivan Smirnov "] 5 | edition = "2018" 6 | readme = "README.md" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [dependencies] 11 | fast-float = { path = "../.." } 12 | structopt = "0.3" 13 | anyhow = "1.0" 14 | lexical = "5.2" 15 | lexical-core = "0.7" 16 | fastrand = "1.4" 17 | -------------------------------------------------------------------------------- /extras/simple-bench/README.md: -------------------------------------------------------------------------------- 1 | This crate provides a utility for benchmarking the `fast-float` crate against 2 | `lexical_core` and standard library's `FromStr`. 3 | 4 | To run a file-based test: 5 | 6 | ```sh 7 | cargo run --release -- file ext/canada.txt 8 | ``` 9 | 10 | There's two files used in benchmarking of the original fast_float C++ library 11 | (canada.txt and mesh.txt), they are sourced from 12 | [this](https://github.com/lemire/simple_fastfloat_benchmark) repository. These 13 | files can be found under `ext/data`. 14 | 15 | To run a randomized test: 16 | 17 | ```sh 18 | cargo run --release -- random uniform 19 | ``` 20 | 21 | For more details and options (choosing a different random generator, storing 22 | randomized inputs to a file, changing the number of runs, or switching between 23 | 32-bit and 64-bit floats), refer to help: 24 | 25 | ``` 26 | cargo run --release -- --help 27 | ``` 28 | -------------------------------------------------------------------------------- /extras/simple-bench/src/main.rs: -------------------------------------------------------------------------------- 1 | mod random; 2 | 3 | use std::fs; 4 | use std::iter; 5 | use std::path::{Path, PathBuf}; 6 | use std::str::FromStr; 7 | use std::time::Instant; 8 | 9 | use fastrand::Rng; 10 | use lexical::FromLexical; 11 | use structopt::StructOpt; 12 | 13 | use fast_float::FastFloat; 14 | 15 | use random::RandomGen; 16 | 17 | #[derive(Debug, StructOpt)] 18 | #[structopt( 19 | name = "fast-float-simple-bench", 20 | about = "fast-float benchmark utility", 21 | no_version 22 | )] 23 | struct Opt { 24 | /// Parse numbers as float32 (default is float64) 25 | #[structopt(short, long = "32")] 26 | float32: bool, 27 | /// How many times to repeat parsing 28 | #[structopt(short, default_value = "1000")] 29 | repeat: usize, 30 | /// Only run fast-float benches 31 | #[structopt(short)] 32 | only_fast_float: bool, 33 | #[structopt(subcommand)] 34 | command: Cmd, 35 | } 36 | 37 | #[derive(Debug, StructOpt)] 38 | enum Cmd { 39 | /// Read the floats from file 40 | File { 41 | /// Input file (one number per line) 42 | #[structopt(parse(from_os_str))] 43 | filename: PathBuf, 44 | }, 45 | /// Generate random floats in (0, 1] 46 | Random { 47 | /// Random generator to be used 48 | #[structopt( 49 | default_value = "uniform", 50 | parse(try_from_str), 51 | possible_values = RandomGen::variants() 52 | )] 53 | gen: RandomGen, 54 | /// Number of random floats generated 55 | #[structopt(short = "n", default_value = "50000")] 56 | count: usize, 57 | /// Random generator seed 58 | #[structopt(short, default_value = "0")] 59 | seed: u64, 60 | /// Also save the generated inputs to file 61 | #[structopt(short = "f", parse(from_os_str))] 62 | filename: Option, 63 | }, 64 | /// Run all benchmarks for fast-float only 65 | All { 66 | /// Number of random floats generated 67 | #[structopt(short = "n", default_value = "50000")] 68 | count: usize, 69 | /// Random generator seed 70 | #[structopt(short, default_value = "0")] 71 | seed: u64, 72 | }, 73 | } 74 | 75 | #[derive(Debug, Clone)] 76 | struct BenchResult { 77 | pub name: String, 78 | pub times: Vec, 79 | pub count: usize, 80 | pub bytes: usize, 81 | } 82 | 83 | fn black_box(dummy: T) -> T { 84 | unsafe { 85 | let ret = core::ptr::read_volatile(&dummy); 86 | core::mem::forget(dummy); 87 | ret 88 | } 89 | } 90 | 91 | fn run_bench T>( 92 | inputs: &[String], 93 | repeat: usize, 94 | func: F, 95 | ) -> Vec { 96 | const WARMUP: usize = 1000; 97 | let mut times = Vec::with_capacity(repeat + WARMUP); 98 | for _ in 0..repeat + WARMUP { 99 | let t0 = Instant::now(); 100 | for input in inputs { 101 | black_box(func(input.as_str())); 102 | } 103 | times.push(t0.elapsed().as_nanos() as _); 104 | } 105 | times.split_at(WARMUP).1.into() 106 | } 107 | 108 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 109 | enum Method { 110 | FastFloat, 111 | Lexical, 112 | FromStr, 113 | } 114 | 115 | fn type_str(float32: bool) -> &'static str { 116 | if float32 { 117 | "f32" 118 | } else { 119 | "f64" 120 | } 121 | } 122 | 123 | impl Method { 124 | pub fn name(&self) -> &'static str { 125 | match self { 126 | Self::FastFloat => "fast-float", 127 | Self::Lexical => "lexical", 128 | Self::FromStr => "from_str", 129 | } 130 | } 131 | 132 | fn run_as( 133 | &self, 134 | input: &Input, 135 | repeat: usize, 136 | name: &str, 137 | ) -> BenchResult { 138 | let data = &input.data; 139 | let times = match self { 140 | Self::FastFloat => run_bench(data, repeat, |s: &str| { 141 | fast_float::parse_partial::(s).unwrap_or_default().0 142 | }), 143 | Self::Lexical => run_bench(data, repeat, |s: &str| { 144 | lexical_core::parse_partial::(s.as_bytes()) 145 | .unwrap_or_default() 146 | .0 147 | }), 148 | Self::FromStr => run_bench(data, repeat, |s: &str| s.parse::().unwrap_or_default()), 149 | }; 150 | 151 | BenchResult { 152 | times, 153 | name: name.into(), 154 | count: input.count(), 155 | bytes: input.bytes(), 156 | } 157 | } 158 | 159 | pub fn run(&self, input: &Input, repeat: usize, name: &str, float32: bool) -> BenchResult { 160 | if float32 { 161 | self.run_as::(input, repeat, name) 162 | } else { 163 | self.run_as::(input, repeat, name) 164 | } 165 | } 166 | 167 | pub fn all() -> &'static [Self] { 168 | &[Method::FastFloat, Method::Lexical, Method::FromStr] 169 | } 170 | } 171 | 172 | fn print_report(results: &[BenchResult], title: &str) { 173 | let width = 81; 174 | println!("{:= f64, 193 | ) { 194 | let repeat = results[0].times.len(); 195 | let columns = &[ 196 | ("min", 0), 197 | ("5%", repeat / 20), 198 | ("25%", repeat / 4), 199 | ("median", repeat / 2), 200 | ("75%", (3 * repeat) / 4), 201 | ("95%", (19 * repeat) / 20), 202 | ("max", repeat - 1), 203 | ]; 204 | let w = 9; 205 | let h = width - 7 * w; 206 | 207 | println!("|{:width$}|", "", width = width + 2); 208 | print!("| {:w$}", name, w = w); 211 | } 212 | println!(" |"); 213 | println!("|{:->(); 222 | metrics.sort_by(|a, b| a.partial_cmp(b).unwrap()); 223 | for &(_, idx) in columns { 224 | print!("{:>w$.2}", metrics[idx], w = w); 225 | } 226 | println!(" |"); 227 | } 228 | } 229 | 230 | struct Input { 231 | pub data: Vec, 232 | pub name: String, 233 | } 234 | 235 | impl Input { 236 | pub fn from_file(filename: impl AsRef) -> Self { 237 | let filename = filename.as_ref(); 238 | let data = fs::read_to_string(&filename) 239 | .unwrap() 240 | .trim() 241 | .lines() 242 | .map(String::from) 243 | .collect(); 244 | let name = filename.file_name().unwrap().to_str().unwrap().into(); 245 | Self { data, name } 246 | } 247 | 248 | pub fn from_random(gen: RandomGen, count: usize, seed: u64) -> Self { 249 | let mut rng = Rng::with_seed(seed); 250 | let data = iter::repeat_with(|| gen.gen(&mut rng)) 251 | .take(count) 252 | .collect(); 253 | let name = format!("{}", gen); 254 | Self { data, name } 255 | } 256 | 257 | pub fn count(&self) -> usize { 258 | self.data.len() 259 | } 260 | 261 | pub fn bytes(&self) -> usize { 262 | self.data.iter().map(|s| s.len()).sum() 263 | } 264 | 265 | pub fn title(&self, float32: bool) -> String { 266 | format!( 267 | "{} ({}, {:.2} MB, {})", 268 | self.name, 269 | self.count(), 270 | self.bytes() as f64 / 1024. / 1024., 271 | type_str(float32), 272 | ) 273 | } 274 | } 275 | 276 | fn main() { 277 | let opt: Opt = StructOpt::from_args(); 278 | 279 | let methods = if !opt.only_fast_float && !matches!(&opt.command, &Cmd::All {..}) { 280 | Method::all().into() 281 | } else { 282 | vec![Method::FastFloat] 283 | }; 284 | 285 | let inputs = match opt.command { 286 | Cmd::File { filename } => vec![Input::from_file(filename)], 287 | Cmd::Random { 288 | gen, 289 | count, 290 | seed, 291 | filename, 292 | } => { 293 | let input = Input::from_random(gen, count, seed); 294 | if let Some(filename) = filename { 295 | fs::write(filename, input.data.join("\n")).unwrap(); 296 | } 297 | vec![input] 298 | } 299 | Cmd::All { count, seed } => { 300 | let mut inputs = vec![]; 301 | let data_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ext/data"); 302 | inputs.push(Input::from_file(data_dir.join("mesh.txt"))); 303 | inputs.push(Input::from_file(data_dir.join("canada.txt"))); 304 | for &gen in RandomGen::all() { 305 | inputs.push(Input::from_random(gen, count, seed)) 306 | } 307 | inputs 308 | } 309 | }; 310 | 311 | let mut results = vec![]; 312 | for input in &inputs { 313 | for method in &methods { 314 | let name = if inputs.len() == 1 { 315 | method.name() 316 | } else { 317 | &input.name 318 | }; 319 | results.push(method.run(input, opt.repeat.max(1), name, opt.float32)); 320 | } 321 | } 322 | 323 | let title = if inputs.len() == 1 { 324 | inputs[0].title(opt.float32) 325 | } else { 326 | format!("fast-float (all, {})", type_str(opt.float32)) 327 | }; 328 | print_report(&results, &title); 329 | } 330 | -------------------------------------------------------------------------------- /extras/simple-bench/src/random.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | use std::str::FromStr; 3 | 4 | use anyhow::{bail, Error, Result}; 5 | use fastrand::Rng; 6 | 7 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 8 | pub enum RandomGen { 9 | Uniform, 10 | OneOverRand32, 11 | SimpleUniform32, 12 | SimpleInt32, 13 | IntEInt, 14 | SimpleInt64, 15 | BigIntDotInt, 16 | BigInts, 17 | } 18 | 19 | impl Display for RandomGen { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | match self { 22 | Self::Uniform => write!(f, "uniform"), 23 | Self::OneOverRand32 => write!(f, "one_over_rand32"), 24 | Self::SimpleUniform32 => write!(f, "simple_uniform32"), 25 | Self::SimpleInt32 => write!(f, "simple_int32"), 26 | Self::IntEInt => write!(f, "int_e_int"), 27 | Self::SimpleInt64 => write!(f, "simple_int64"), 28 | Self::BigIntDotInt => write!(f, "bigint_int_dot_int"), 29 | Self::BigInts => write!(f, "big_ints"), 30 | } 31 | } 32 | } 33 | 34 | impl FromStr for RandomGen { 35 | type Err = Error; 36 | 37 | fn from_str(s: &str) -> Result { 38 | Ok(match s { 39 | "uniform" => Self::Uniform, 40 | "one_over_rand32" => Self::OneOverRand32, 41 | "simple_uniform32" => Self::SimpleUniform32, 42 | "simple_int32" => Self::SimpleInt32, 43 | "int_e_int" => Self::IntEInt, 44 | "simple_int64" => Self::SimpleInt64, 45 | "bigint_int_dot_int" => Self::BigIntDotInt, 46 | "big_ints" => Self::BigInts, 47 | _ => bail!("Invalid random generator: {:?}", s), 48 | }) 49 | } 50 | } 51 | 52 | impl RandomGen { 53 | pub fn variants() -> &'static [&'static str] { 54 | &[ 55 | "uniform", 56 | "one_over_rand32", 57 | "simple_uniform32", 58 | "simple_int32", 59 | "int_e_int", 60 | "simple_int64", 61 | "bigint_int_dot_int", 62 | "big_ints", 63 | ] 64 | } 65 | 66 | pub fn all() -> &'static [Self] { 67 | &[ 68 | Self::Uniform, 69 | Self::OneOverRand32, 70 | Self::SimpleUniform32, 71 | Self::SimpleInt32, 72 | Self::IntEInt, 73 | Self::SimpleInt64, 74 | Self::BigIntDotInt, 75 | Self::BigInts, 76 | ] 77 | } 78 | 79 | pub fn gen(&self, rng: &mut Rng) -> String { 80 | match self { 81 | Self::Uniform 82 | | Self::OneOverRand32 83 | | Self::SimpleUniform32 84 | | Self::SimpleInt32 85 | | Self::SimpleInt64 => lexical::to_string(match self { 86 | Self::Uniform => rng.f64(), 87 | Self::OneOverRand32 => 1. / rng.u32(1..) as f64, 88 | Self::SimpleUniform32 => rng.u32(..) as f64 / u32::MAX as f64, 89 | Self::SimpleInt32 => rng.u32(..) as f64, 90 | Self::SimpleInt64 => rng.u64(..) as f64, 91 | _ => unreachable!(), 92 | }), 93 | Self::IntEInt => format!("{}e{}", rng.u32(..), rng.u32(..99)), 94 | Self::BigInts => format!("{}{}{}", rng.u64(..), rng.u64(..), rng.u64(..)), 95 | Self::BigIntDotInt => format!("{}.{}", rng.u32(..), rng.u32(..)), 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast-float-fuzz" 3 | version = "0.1.0" 4 | authors = ["Ivan Smirnov "] 5 | edition = "2018" 6 | publish = false 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [workspace] 12 | members = ["."] 13 | 14 | [dependencies] 15 | fast-float = { path = ".." } 16 | libfuzzer-sys = "0.3" 17 | ryu = "1.0" 18 | 19 | [[bin]] 20 | name = "fast_float" 21 | path = "fuzz_targets/fast_float.rs" 22 | test = false 23 | doc = false 24 | 25 | [[bin]] 26 | name = "roundtrip_f64" 27 | path = "fuzz_targets/roundtrip_f64.rs" 28 | test = false 29 | doc = false 30 | -------------------------------------------------------------------------------- /fuzz/float.dict: -------------------------------------------------------------------------------- 1 | "0" 2 | "1" 3 | "2" 4 | "3" 5 | "4" 6 | "5" 7 | "6" 8 | "7" 9 | "8" 10 | "9" 11 | "." 12 | "-" 13 | "+" 14 | "e" 15 | "E" 16 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fast_float.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fn black_box(dummy: T) -> T { 6 | unsafe { 7 | let ret = core::ptr::read_volatile(&dummy); 8 | core::mem::forget(dummy); 9 | ret 10 | } 11 | } 12 | 13 | fuzz_target!(|data: &[u8]| { 14 | let _ = black_box(::fast_float::parse::(data)); 15 | let _ = black_box(::fast_float::parse::(data)); 16 | }); 17 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/roundtrip_f64.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | // We only test the roundtrip of f64 with a fuzzer because f32 search space 5 | // is small enough that we can test it exhaustively 6 | 7 | fn check_roundtrip(float: f64, string: impl AsRef) { 8 | let result = ::fast_float::parse::(string.as_ref()).unwrap(); 9 | if float.is_nan() { 10 | assert!(result.is_nan()); 11 | } else { 12 | assert_eq!(float, result); 13 | } 14 | } 15 | 16 | fuzz_target!(|float: f64| { 17 | // we use both ryu and stdlib since ryu prefers scientific notation while stdlib 18 | // never uses it at all; hence more code paths will be executed 19 | let mut buf = ryu::Buffer::new(); 20 | check_roundtrip(float, buf.format(float)); 21 | check_roundtrip(float, float.to_string()); 22 | }); 23 | -------------------------------------------------------------------------------- /src/binary.rs: -------------------------------------------------------------------------------- 1 | use crate::common::AdjustedMantissa; 2 | use crate::float::Float; 3 | use crate::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE}; 4 | 5 | #[inline] 6 | pub fn compute_float(q: i64, mut w: u64) -> AdjustedMantissa { 7 | let am_zero = AdjustedMantissa::zero_pow2(0); 8 | let am_inf = AdjustedMantissa::zero_pow2(F::INFINITE_POWER); 9 | let am_error = AdjustedMantissa::zero_pow2(-1); 10 | 11 | if w == 0 || q < F::SMALLEST_POWER_OF_TEN as i64 { 12 | return am_zero; 13 | } else if q > F::LARGEST_POWER_OF_TEN as i64 { 14 | return am_inf; 15 | } 16 | let lz = w.leading_zeros(); 17 | w <<= lz; 18 | let (lo, hi) = compute_product_approx(q, w, F::MANTISSA_EXPLICIT_BITS + 3); 19 | if lo == 0xFFFF_FFFF_FFFF_FFFF { 20 | let inside_safe_exponent = (q >= -27) && (q <= 55); 21 | if !inside_safe_exponent { 22 | return am_error; 23 | } 24 | } 25 | let upperbit = (hi >> 63) as i32; 26 | let mut mantissa = hi >> (upperbit + 64 - F::MANTISSA_EXPLICIT_BITS as i32 - 3); 27 | let mut power2 = power(q as i32) + upperbit - lz as i32 - F::MINIMUM_EXPONENT; 28 | if power2 <= 0 { 29 | if -power2 + 1 >= 64 { 30 | return am_zero; 31 | } 32 | mantissa >>= -power2 + 1; 33 | mantissa += mantissa & 1; 34 | mantissa >>= 1; 35 | power2 = (mantissa >= (1_u64 << F::MANTISSA_EXPLICIT_BITS)) as i32; 36 | return AdjustedMantissa { mantissa, power2 }; 37 | } 38 | if lo <= 1 39 | && q >= F::MIN_EXPONENT_ROUND_TO_EVEN as i64 40 | && q <= F::MAX_EXPONENT_ROUND_TO_EVEN as i64 41 | && mantissa & 3 == 1 42 | && (mantissa << (upperbit + 64 - F::MANTISSA_EXPLICIT_BITS as i32 - 3)) == hi 43 | { 44 | mantissa &= !1_u64; 45 | } 46 | mantissa += mantissa & 1; 47 | mantissa >>= 1; 48 | if mantissa >= (2_u64 << F::MANTISSA_EXPLICIT_BITS) { 49 | mantissa = 1_u64 << F::MANTISSA_EXPLICIT_BITS; 50 | power2 += 1; 51 | } 52 | mantissa &= !(1_u64 << F::MANTISSA_EXPLICIT_BITS); 53 | if power2 >= F::INFINITE_POWER { 54 | return am_inf; 55 | } 56 | AdjustedMantissa { mantissa, power2 } 57 | } 58 | 59 | #[inline] 60 | fn power(q: i32) -> i32 { 61 | (q.wrapping_mul(152_170 + 65536) >> 16) + 63 62 | } 63 | 64 | #[inline] 65 | fn full_multiplication(a: u64, b: u64) -> (u64, u64) { 66 | let r = (a as u128) * (b as u128); 67 | (r as u64, (r >> 64) as u64) 68 | } 69 | 70 | // This will compute or rather approximate w * 5**q and return a pair of 64-bit words 71 | // approximating the result, with the "high" part corresponding to the most significant 72 | // bits and the low part corresponding to the least significant bits. 73 | #[inline] 74 | fn compute_product_approx(q: i64, w: u64, precision: usize) -> (u64, u64) { 75 | debug_assert!(q >= SMALLEST_POWER_OF_FIVE as i64); 76 | debug_assert!(q <= LARGEST_POWER_OF_FIVE as i64); 77 | debug_assert!(precision <= 64); 78 | 79 | let mask = if precision < 64 { 80 | 0xFFFF_FFFF_FFFF_FFFF_u64 >> precision 81 | } else { 82 | 0xFFFF_FFFF_FFFF_FFFF_u64 83 | }; 84 | let index = (q - SMALLEST_POWER_OF_FIVE as i64) as usize; 85 | let (lo5, hi5) = unsafe { *POWER_OF_FIVE_128.get_unchecked(index) }; 86 | let (mut first_lo, mut first_hi) = full_multiplication(w, lo5); 87 | if first_hi & mask == mask { 88 | let (_, second_hi) = full_multiplication(w, hi5); 89 | first_lo = first_lo.wrapping_add(second_hi); 90 | if second_hi > first_lo { 91 | first_hi += 1; 92 | } 93 | } 94 | (first_lo, first_hi) 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use super::*; 100 | 101 | #[test] 102 | fn test_full_multiplication() { 103 | fn check(a: u64, b: u64, lo: u64, hi: u64) { 104 | assert_eq!(full_multiplication(a, b), (lo, hi)); 105 | assert_eq!(full_multiplication(b, a), (lo, hi)); 106 | } 107 | check(1 << 0, 1 << 0, 1, 0); 108 | check(1 << 0, 1 << 63, 1 << 63, 0); 109 | check(1 << 1, 1 << 63, 0, 1); 110 | check(1 << 63, 1 << 0, 1 << 63, 0); 111 | check(1 << 63, 1 << 1, 0, 1); 112 | check(1 << 63, 1 << 2, 0, 2); 113 | check(1 << 63, 1 << 63, 0, 1 << 62); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | use core::ptr; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 5 | pub struct AsciiStr<'a> { 6 | ptr: *const u8, 7 | end: *const u8, 8 | _marker: PhantomData<&'a [u8]>, 9 | } 10 | 11 | impl<'a> AsciiStr<'a> { 12 | #[inline] 13 | pub fn new(s: &'a [u8]) -> Self { 14 | Self { 15 | ptr: s.as_ptr(), 16 | end: unsafe { s.as_ptr().add(s.len()) }, 17 | _marker: PhantomData::default(), 18 | } 19 | } 20 | 21 | #[inline] 22 | pub fn step_by(&mut self, n: usize) -> &mut Self { 23 | unsafe { self.ptr = self.ptr.add(n) }; 24 | self 25 | } 26 | 27 | #[inline] 28 | pub fn step(&mut self) -> &mut Self { 29 | self.step_by(1) 30 | } 31 | 32 | #[inline] 33 | pub fn is_empty(&self) -> bool { 34 | self.ptr == self.end 35 | } 36 | 37 | #[inline] 38 | pub fn first(&self) -> u8 { 39 | unsafe { *self.ptr } 40 | } 41 | 42 | #[inline] 43 | pub fn first_is(&self, c: u8) -> bool { 44 | self.first() == c 45 | } 46 | 47 | #[inline] 48 | pub fn first_either(&self, c1: u8, c2: u8) -> bool { 49 | let c = self.first(); 50 | c == c1 || c == c2 51 | } 52 | 53 | #[inline] 54 | pub fn check_first(&self, c: u8) -> bool { 55 | !self.is_empty() && self.first() == c 56 | } 57 | 58 | #[inline] 59 | pub fn check_first_either(&self, c1: u8, c2: u8) -> bool { 60 | !self.is_empty() && (self.first() == c1 || self.first() == c2) 61 | } 62 | 63 | #[inline] 64 | pub fn check_first_digit(&self) -> bool { 65 | !self.is_empty() && self.first().is_ascii_digit() 66 | } 67 | 68 | #[inline] 69 | pub fn parse_digits(&mut self, mut func: impl FnMut(u8)) { 70 | while !self.is_empty() && self.first().is_ascii_digit() { 71 | func(self.first() - b'0'); 72 | self.step(); 73 | } 74 | } 75 | 76 | #[inline] 77 | pub fn check_len(&self, n: usize) -> bool { 78 | let len = self.end as usize - self.ptr as usize; 79 | n <= len 80 | } 81 | 82 | #[inline] 83 | pub fn try_read_u64(&self) -> Option { 84 | if self.check_len(8) { 85 | Some(self.read_u64()) 86 | } else { 87 | None 88 | } 89 | } 90 | 91 | #[inline] 92 | pub fn read_u64(&self) -> u64 { 93 | debug_assert!(self.check_len(8)); 94 | let src = self.ptr as *const u64; 95 | u64::from_le(unsafe { ptr::read_unaligned(src) }) 96 | } 97 | 98 | #[inline] 99 | pub fn offset_from(&self, other: &Self) -> isize { 100 | isize::wrapping_sub(self.ptr as _, other.ptr as _) // assuming the same end 101 | } 102 | } 103 | 104 | // Most of these are inherently unsafe; we assume we know what we're calling and when. 105 | pub trait ByteSlice: AsRef<[u8]> + AsMut<[u8]> { 106 | #[inline] 107 | fn get_at(&self, i: usize) -> u8 { 108 | unsafe { *self.as_ref().get_unchecked(i) } 109 | } 110 | 111 | #[inline] 112 | fn get_first(&self) -> u8 { 113 | debug_assert!(!self.as_ref().is_empty()); 114 | self.get_at(0) 115 | } 116 | 117 | #[inline] 118 | fn check_first(&self, c: u8) -> bool { 119 | !self.as_ref().is_empty() && self.get_first() == c 120 | } 121 | 122 | #[inline] 123 | fn check_first2(&self, c1: u8, c2: u8) -> bool { 124 | !self.as_ref().is_empty() && (self.get_first() == c1 || self.get_first() == c2) 125 | } 126 | 127 | #[inline] 128 | fn eq_ignore_case(&self, u: &[u8]) -> bool { 129 | debug_assert!(self.as_ref().len() >= u.len()); 130 | let d = (0..u.len()).fold(0, |d, i| d | self.get_at(i) ^ u.get_at(i)); 131 | d == 0 || d == 32 132 | } 133 | 134 | #[inline] 135 | fn advance(&self, n: usize) -> &[u8] { 136 | &self.as_ref()[n..] 137 | } 138 | 139 | #[inline] 140 | fn skip_chars(&self, c: u8) -> &[u8] { 141 | let mut s = self.as_ref(); 142 | while s.check_first(c) { 143 | s = s.advance(1); 144 | } 145 | s 146 | } 147 | 148 | #[inline] 149 | fn skip_chars2(&self, c1: u8, c2: u8) -> &[u8] { 150 | let mut s = self.as_ref(); 151 | while !s.is_empty() && (s.get_first() == c1 || s.get_first() == c2) { 152 | s = s.advance(1); 153 | } 154 | s 155 | } 156 | 157 | #[inline] 158 | fn read_u64(&self) -> u64 { 159 | debug_assert!(self.as_ref().len() >= 8); 160 | let src = self.as_ref().as_ptr() as *const u64; 161 | u64::from_le(unsafe { ptr::read_unaligned(src) }) 162 | } 163 | 164 | #[inline] 165 | fn write_u64(&mut self, value: u64) { 166 | debug_assert!(self.as_ref().len() >= 8); 167 | let dst = self.as_mut().as_mut_ptr() as *mut u64; 168 | unsafe { ptr::write_unaligned(dst, u64::to_le(value)) }; 169 | } 170 | } 171 | 172 | impl ByteSlice for [u8] {} 173 | 174 | #[inline] 175 | pub fn is_8digits(v: u64) -> bool { 176 | let a = v.wrapping_add(0x4646_4646_4646_4646); 177 | let b = v.wrapping_sub(0x3030_3030_3030_3030); 178 | (a | b) & 0x8080_8080_8080_8080 == 0 179 | } 180 | 181 | #[inline] 182 | pub fn parse_digits(s: &mut &[u8], mut f: impl FnMut(u8)) { 183 | while !s.is_empty() { 184 | let c = s.get_first().wrapping_sub(b'0'); 185 | if c < 10 { 186 | f(c); 187 | *s = s.advance(1); 188 | } else { 189 | break; 190 | } 191 | } 192 | } 193 | 194 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] 195 | pub struct AdjustedMantissa { 196 | pub mantissa: u64, 197 | pub power2: i32, 198 | } 199 | 200 | impl AdjustedMantissa { 201 | #[inline] 202 | pub const fn zero_pow2(power2: i32) -> Self { 203 | Self { 204 | mantissa: 0, 205 | power2, 206 | } 207 | } 208 | } 209 | 210 | #[cfg(test)] 211 | mod tests { 212 | use super::*; 213 | 214 | #[test] 215 | fn test_read_write_u64() { 216 | let bytes = b"01234567"; 217 | let string = AsciiStr::new(bytes); 218 | let int = string.read_u64(); 219 | assert_eq!(int, 0x3736353433323130); 220 | 221 | let int = bytes.read_u64(); 222 | assert_eq!(int, 0x3736353433323130); 223 | 224 | let mut slc = [0u8; 8]; 225 | slc.write_u64(0x3736353433323130); 226 | assert_eq!(&slc, bytes); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/decimal.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Debug}; 2 | 3 | use crate::common::{is_8digits, parse_digits, ByteSlice}; 4 | 5 | #[derive(Clone)] 6 | pub struct Decimal { 7 | pub num_digits: usize, 8 | pub decimal_point: i32, 9 | pub negative: bool, 10 | pub truncated: bool, 11 | pub digits: [u8; Self::MAX_DIGITS], 12 | } 13 | 14 | impl Debug for Decimal { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | f.debug_struct("Decimal") 17 | .field("num_digits", &self.num_digits) 18 | .field("decimal_point", &self.decimal_point) 19 | .field("negative", &self.negative) 20 | .field("truncated", &self.truncated) 21 | .field("digits", &(&self.digits[..self.num_digits])) 22 | .finish() 23 | } 24 | } 25 | 26 | impl PartialEq for Decimal { 27 | fn eq(&self, other: &Self) -> bool { 28 | self.num_digits == other.num_digits 29 | && self.decimal_point == other.decimal_point 30 | && self.negative == other.negative 31 | && self.truncated == other.truncated 32 | && self.digits[..] == other.digits[..] 33 | } 34 | } 35 | 36 | impl Eq for Decimal {} 37 | 38 | impl Default for Decimal { 39 | fn default() -> Self { 40 | Self { 41 | num_digits: 0, 42 | decimal_point: 0, 43 | negative: false, 44 | truncated: false, 45 | digits: [0; Self::MAX_DIGITS], 46 | } 47 | } 48 | } 49 | 50 | impl Decimal { 51 | pub const MAX_DIGITS: usize = 768; 52 | pub const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; 53 | pub const DECIMAL_POINT_RANGE: i32 = 2047; 54 | 55 | #[inline] 56 | pub fn try_add_digit(&mut self, digit: u8) { 57 | if self.num_digits < Self::MAX_DIGITS { 58 | self.digits[self.num_digits] = digit; 59 | } 60 | self.num_digits += 1; 61 | } 62 | 63 | #[inline] 64 | pub fn trim(&mut self) { 65 | while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 { 66 | self.num_digits -= 1; 67 | } 68 | } 69 | 70 | #[inline] 71 | pub fn round(&self) -> u64 { 72 | if self.num_digits == 0 || self.decimal_point < 0 { 73 | return 0; 74 | } else if self.decimal_point > 18 { 75 | return 0xFFFF_FFFF_FFFF_FFFF_u64; 76 | } 77 | let dp = self.decimal_point as usize; 78 | let mut n = 0_u64; 79 | for i in 0..dp { 80 | n *= 10; 81 | if i < self.num_digits { 82 | n += self.digits[i] as u64; 83 | } 84 | } 85 | let mut round_up = false; 86 | if dp < self.num_digits { 87 | round_up = self.digits[dp] >= 5; 88 | if self.digits[dp] == 5 && dp + 1 == self.num_digits { 89 | round_up = self.truncated || ((dp != 0) && (1 & self.digits[dp - 1] != 0)) 90 | } 91 | } 92 | if round_up { 93 | n += 1; 94 | } 95 | n 96 | } 97 | 98 | #[inline] 99 | pub fn left_shift(&mut self, shift: usize) { 100 | if self.num_digits == 0 { 101 | return; 102 | } 103 | let num_new_digits = number_of_digits_decimal_left_shift(self, shift); 104 | let mut read_index = self.num_digits; 105 | let mut write_index = self.num_digits + num_new_digits; 106 | let mut n = 0_u64; 107 | while read_index != 0 { 108 | read_index -= 1; 109 | write_index -= 1; 110 | n += (self.digits[read_index] as u64) << shift; 111 | let quotient = n / 10; 112 | let remainder = n - (10 * quotient); 113 | if write_index < Self::MAX_DIGITS { 114 | self.digits[write_index] = remainder as u8; 115 | } else if remainder > 0 { 116 | self.truncated = true; 117 | } 118 | n = quotient; 119 | } 120 | while n > 0 { 121 | write_index -= 1; 122 | let quotient = n / 10; 123 | let remainder = n - (10 * quotient); 124 | if write_index < Self::MAX_DIGITS { 125 | self.digits[write_index] = remainder as u8; 126 | } else if remainder > 0 { 127 | self.truncated = true; 128 | } 129 | n = quotient; 130 | } 131 | self.num_digits += num_new_digits; 132 | if self.num_digits > Self::MAX_DIGITS { 133 | self.num_digits = Self::MAX_DIGITS; 134 | } 135 | self.decimal_point += num_new_digits as i32; 136 | self.trim(); 137 | } 138 | 139 | #[inline] 140 | pub fn right_shift(&mut self, shift: usize) { 141 | let mut read_index = 0; 142 | let mut write_index = 0; 143 | let mut n = 0_u64; 144 | while (n >> shift) == 0 { 145 | if read_index < self.num_digits { 146 | n = (10 * n) + self.digits[read_index] as u64; 147 | read_index += 1; 148 | } else if n == 0 { 149 | return; 150 | } else { 151 | while (n >> shift) == 0 { 152 | n *= 10; 153 | read_index += 1; 154 | } 155 | break; 156 | } 157 | } 158 | self.decimal_point -= read_index as i32 - 1; 159 | if self.decimal_point < -Self::DECIMAL_POINT_RANGE { 160 | self.num_digits = 0; 161 | self.decimal_point = 0; 162 | self.negative = false; 163 | self.truncated = false; 164 | return; 165 | } 166 | let mask = (1_u64 << shift) - 1; 167 | while read_index < self.num_digits { 168 | let new_digit = (n >> shift) as u8; 169 | n = (10 * (n & mask)) + self.digits[read_index] as u64; 170 | read_index += 1; 171 | self.digits[write_index] = new_digit; 172 | write_index += 1; 173 | } 174 | while n > 0 { 175 | let new_digit = (n >> shift) as u8; 176 | n = 10 * (n & mask); 177 | if write_index < Self::MAX_DIGITS { 178 | self.digits[write_index] = new_digit; 179 | write_index += 1; 180 | } else if new_digit > 0 { 181 | self.truncated = true; 182 | } 183 | } 184 | self.num_digits = write_index; 185 | self.trim(); 186 | } 187 | } 188 | 189 | #[inline] 190 | pub fn parse_decimal(mut s: &[u8]) -> Decimal { 191 | // can't fail since it follows a call to parse_number 192 | let mut d = Decimal::default(); 193 | let start = s; 194 | let c = s.get_first(); 195 | d.negative = c == b'-'; 196 | if c == b'-' || c == b'+' { 197 | s = s.advance(1); 198 | } 199 | s = s.skip_chars(b'0'); 200 | parse_digits(&mut s, |digit| d.try_add_digit(digit)); 201 | if s.check_first(b'.') { 202 | s = s.advance(1); 203 | let first = s; 204 | if d.num_digits == 0 { 205 | s = s.skip_chars(b'0'); 206 | } 207 | while s.len() >= 8 && d.num_digits + 8 < Decimal::MAX_DIGITS { 208 | let v = s.read_u64(); 209 | if !is_8digits(v) { 210 | break; 211 | } 212 | d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030); 213 | d.num_digits += 8; 214 | s = s.advance(8); 215 | } 216 | parse_digits(&mut s, |digit| d.try_add_digit(digit)); 217 | d.decimal_point = s.len() as i32 - first.len() as i32; 218 | } 219 | if d.num_digits != 0 { 220 | // Ignore the trailing zeros if there are any 221 | let mut n_trailing_zeros = 0; 222 | for &c in start[..(start.len() - s.len())].iter().rev() { 223 | if c == b'0' { 224 | n_trailing_zeros += 1; 225 | } else if c != b'.' { 226 | break; 227 | } 228 | } 229 | d.decimal_point += n_trailing_zeros as i32; 230 | d.num_digits -= n_trailing_zeros; 231 | d.decimal_point += d.num_digits as i32; 232 | if d.num_digits > Decimal::MAX_DIGITS { 233 | d.truncated = true; 234 | d.num_digits = Decimal::MAX_DIGITS; 235 | } 236 | } 237 | if s.check_first2(b'e', b'E') { 238 | s = s.advance(1); 239 | let mut neg_exp = false; 240 | if s.check_first(b'-') { 241 | neg_exp = true; 242 | s = s.advance(1); 243 | } else if s.check_first(b'+') { 244 | s = s.advance(1); 245 | } 246 | let mut exp_num = 0_i32; 247 | parse_digits(&mut s, |digit| { 248 | if exp_num < 0x10000 { 249 | exp_num = 10 * exp_num + digit as i32; 250 | } 251 | }); 252 | d.decimal_point += if neg_exp { -exp_num } else { exp_num }; 253 | } 254 | for i in d.num_digits..Decimal::MAX_DIGITS_WITHOUT_OVERFLOW { 255 | d.digits[i] = 0; 256 | } 257 | d 258 | } 259 | 260 | #[inline] 261 | fn number_of_digits_decimal_left_shift(d: &Decimal, mut shift: usize) -> usize { 262 | const TABLE: [u16; 65] = [ 263 | 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, 0x181D, 0x2024, 264 | 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, 0x3073, 0x3080, 0x388E, 0x389C, 265 | 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 266 | 0x5180, 0x5998, 0x59B0, 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 267 | 0x72AA, 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, 0x8C02, 268 | 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, 0x051C, 0x051C, 269 | ]; 270 | const TABLE_POW5: [u8; 0x051C] = [ 271 | 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9, 0, 6, 2, 5, 1, 272 | 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 273 | 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 274 | 5, 1, 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, 275 | 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1, 276 | 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 277 | 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 278 | 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, 279 | 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, 280 | 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, 281 | 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, 282 | 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, 283 | 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, 284 | 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 285 | 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 286 | 1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, 287 | 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, 1, 2, 5, 3, 288 | 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 289 | 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 290 | 7, 0, 1, 7, 7, 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5, 291 | 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3, 7, 3, 6, 7, 5, 292 | 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 293 | 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 294 | 8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, 295 | 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0, 296 | 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 297 | 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 298 | 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, 299 | 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, 300 | 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, 301 | 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 302 | 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, 303 | 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1, 304 | 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 305 | 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 306 | 8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, 307 | 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, 3, 8, 7, 7, 7, 8, 308 | 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 309 | 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 310 | 5, 5, 6, 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1, 311 | 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2, 312 | 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 313 | 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 314 | 2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, 315 | ]; 316 | 317 | shift &= 63; 318 | let x_a = TABLE[shift]; 319 | let x_b = TABLE[shift + 1]; 320 | let num_new_digits = (x_a >> 11) as _; 321 | let pow5_a = (0x7FF & x_a) as usize; 322 | let pow5_b = (0x7FF & x_b) as usize; 323 | let pow5 = &TABLE_POW5[pow5_a..]; 324 | for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) { 325 | if i >= d.num_digits { 326 | return num_new_digits - 1; 327 | } else if d.digits[i] == p5 { 328 | continue; 329 | } else if d.digits[i] < p5 { 330 | return num_new_digits - 1; 331 | } else { 332 | return num_new_digits; 333 | } 334 | } 335 | num_new_digits 336 | } 337 | -------------------------------------------------------------------------------- /src/float.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{Debug, Display}; 2 | use core::ops::{Add, Div, Mul, Neg}; 3 | 4 | mod private { 5 | pub trait Sealed {} 6 | } 7 | 8 | #[doc(hidden)] 9 | pub trait Float: 10 | Sized 11 | + private::Sealed 12 | + Div 13 | + Neg 14 | + Mul 15 | + Add 16 | + PartialEq 17 | + PartialOrd 18 | + Default 19 | + Clone 20 | + Copy 21 | + Debug 22 | + Display 23 | { 24 | const INFINITY: Self; 25 | const NEG_INFINITY: Self; 26 | const NAN: Self; 27 | const NEG_NAN: Self; 28 | 29 | const MANTISSA_EXPLICIT_BITS: usize; 30 | const MIN_EXPONENT_ROUND_TO_EVEN: i32; 31 | const MAX_EXPONENT_ROUND_TO_EVEN: i32; 32 | const MIN_EXPONENT_FAST_PATH: i64; 33 | const MAX_EXPONENT_FAST_PATH: i64; 34 | const MAX_EXPONENT_DISGUISED_FAST_PATH: i64; 35 | const MINIMUM_EXPONENT: i32; 36 | const INFINITE_POWER: i32; 37 | const SIGN_INDEX: usize; 38 | const SMALLEST_POWER_OF_TEN: i32; 39 | const LARGEST_POWER_OF_TEN: i32; 40 | 41 | const MAX_MANTISSA_FAST_PATH: u64 = 2_u64 << Self::MANTISSA_EXPLICIT_BITS; 42 | 43 | fn from_u64(v: u64) -> Self; 44 | fn from_u64_bits(v: u64) -> Self; 45 | fn pow10_fast_path(exponent: usize) -> Self; 46 | } 47 | 48 | impl private::Sealed for f32 {} 49 | 50 | impl Float for f32 { 51 | const INFINITY: Self = core::f32::INFINITY; 52 | const NEG_INFINITY: Self = core::f32::NEG_INFINITY; 53 | const NAN: Self = core::f32::NAN; 54 | const NEG_NAN: Self = -core::f32::NAN; 55 | 56 | const MANTISSA_EXPLICIT_BITS: usize = 23; 57 | const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17; 58 | const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10; 59 | const MIN_EXPONENT_FAST_PATH: i64 = -10; // assuming FLT_EVAL_METHOD = 0 60 | const MAX_EXPONENT_FAST_PATH: i64 = 10; 61 | const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = 17; 62 | const MINIMUM_EXPONENT: i32 = -127; 63 | const INFINITE_POWER: i32 = 0xFF; 64 | const SIGN_INDEX: usize = 31; 65 | const SMALLEST_POWER_OF_TEN: i32 = -65; 66 | const LARGEST_POWER_OF_TEN: i32 = 38; 67 | 68 | #[inline] 69 | fn from_u64(v: u64) -> Self { 70 | v as _ 71 | } 72 | 73 | #[inline] 74 | fn from_u64_bits(v: u64) -> Self { 75 | f32::from_bits((v & 0xFFFFFFFF) as u32) 76 | } 77 | 78 | #[inline] 79 | fn pow10_fast_path(exponent: usize) -> Self { 80 | #[allow(clippy::use_self)] 81 | const TABLE: [f32; 16] = [ 82 | 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0., 83 | ]; 84 | TABLE[exponent & 15] 85 | } 86 | } 87 | 88 | impl private::Sealed for f64 {} 89 | 90 | impl Float for f64 { 91 | const INFINITY: Self = core::f64::INFINITY; 92 | const NEG_INFINITY: Self = core::f64::NEG_INFINITY; 93 | const NAN: Self = core::f64::NAN; 94 | const NEG_NAN: Self = -core::f64::NAN; 95 | 96 | const MANTISSA_EXPLICIT_BITS: usize = 52; 97 | const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -4; 98 | const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23; 99 | const MIN_EXPONENT_FAST_PATH: i64 = -22; // assuming FLT_EVAL_METHOD = 0 100 | const MAX_EXPONENT_FAST_PATH: i64 = 22; 101 | const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = 37; 102 | const MINIMUM_EXPONENT: i32 = -1023; 103 | const INFINITE_POWER: i32 = 0x7FF; 104 | const SIGN_INDEX: usize = 63; 105 | const SMALLEST_POWER_OF_TEN: i32 = -342; 106 | const LARGEST_POWER_OF_TEN: i32 = 308; 107 | 108 | #[inline] 109 | fn from_u64(v: u64) -> Self { 110 | v as _ 111 | } 112 | 113 | #[inline] 114 | fn from_u64_bits(v: u64) -> Self { 115 | f64::from_bits(v) 116 | } 117 | 118 | #[inline] 119 | fn pow10_fast_path(exponent: usize) -> Self { 120 | #[allow(clippy::use_self)] 121 | const TABLE: [f64; 32] = [ 122 | 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 123 | 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0., 124 | ]; 125 | TABLE[exponent & 31] 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides a super-fast decimal number parser from strings into floats. 2 | //! 3 | //! ## Usage 4 | //! 5 | //! There's two top-level functions provided: [`parse`](crate::parse()) and 6 | //! [`parse_partial`](crate::parse_partial()), both taking 7 | //! either a string or a bytes slice and parsing the input into either `f32` or `f64`: 8 | //! 9 | //! - [`parse`](crate::parse()) treats the whole string as a decimal number and returns an 10 | //! error if there are invalid characters or if the string is empty. 11 | //! - [`parse_partial`](crate::parse_partial()) tries to find the longest substring at the 12 | //! beginning of the given input string that can be parsed as a decimal number and, 13 | //! in the case of success, returns the parsed value along the number of characters processed; 14 | //! an error is returned if the string doesn't start with a decimal number or if it is empty. 15 | //! This function is most useful as a building block when constructing more complex parsers, 16 | //! or when parsing streams of data. 17 | //! 18 | //! ## Examples 19 | //! 20 | //! ```rust 21 | //! // Parse the entire string as a decimal number. 22 | //! let s = "1.23e-02"; 23 | //! let x: f32 = fast_float::parse(s).unwrap(); 24 | //! assert_eq!(x, 0.0123); 25 | //! 26 | //! // Parse as many characters as possible as a decimal number. 27 | //! let s = "1.23e-02foo"; 28 | //! let (x, n) = fast_float::parse_partial::(s).unwrap(); 29 | //! assert_eq!(x, 0.0123); 30 | //! assert_eq!(n, 8); 31 | //! assert_eq!(&s[n..], "foo"); 32 | //! ``` 33 | 34 | #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] 35 | #![allow( 36 | clippy::cast_possible_truncation, 37 | clippy::cast_possible_wrap, 38 | clippy::cast_sign_loss, 39 | clippy::cast_lossless, 40 | clippy::cast_precision_loss, 41 | clippy::missing_const_for_fn, 42 | clippy::use_self, 43 | clippy::module_name_repetitions, 44 | clippy::cargo_common_metadata 45 | )] 46 | 47 | use core::fmt::{self, Display}; 48 | 49 | mod binary; 50 | mod common; 51 | mod decimal; 52 | mod float; 53 | mod number; 54 | mod parse; 55 | mod simple; 56 | mod table; 57 | 58 | /// Opaque error type for fast-float parsing functions. 59 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 60 | pub struct Error; 61 | 62 | impl Display for Error { 63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 64 | write!(f, "error while parsing a float") 65 | } 66 | } 67 | 68 | #[cfg(feature = "std")] 69 | impl std::error::Error for Error { 70 | fn description(&self) -> &str { 71 | "error while parsing a float" 72 | } 73 | } 74 | 75 | /// Result type alias for fast-float parsing functions. 76 | pub type Result = core::result::Result; 77 | 78 | /// Trait for numerical float types that can be parsed from string. 79 | pub trait FastFloat: float::Float { 80 | /// Parse a decimal number from string into float (full). 81 | /// 82 | /// # Errors 83 | /// 84 | /// Will return an error either if the string is not a valid decimal number. 85 | /// or if any characters are left remaining unparsed. 86 | #[inline] 87 | fn parse_float>(s: S) -> Result { 88 | let s = s.as_ref(); 89 | match Self::parse_float_partial(s) { 90 | Ok((v, n)) if n == s.len() => Ok(v), 91 | _ => Err(Error), 92 | } 93 | } 94 | 95 | /// Parse a decimal number from string into float (partial). 96 | /// 97 | /// This method parses as many characters as possible and returns the resulting number along 98 | /// with the number of digits processed (in case of success, this number is always positive). 99 | /// 100 | /// # Errors 101 | /// 102 | /// Will return an error either if the string doesn't start with a valid decimal number 103 | /// – that is, if no zero digits were processed. 104 | #[inline] 105 | fn parse_float_partial>(s: S) -> Result<(Self, usize)> { 106 | parse::parse_float(s.as_ref()).ok_or(Error) 107 | } 108 | } 109 | 110 | impl FastFloat for f32 {} 111 | impl FastFloat for f64 {} 112 | 113 | /// Parse a decimal number from string into float (full). 114 | /// 115 | /// # Errors 116 | /// 117 | /// Will return an error either if the string is not a valid decimal number 118 | /// or if any characters are left remaining unparsed. 119 | #[inline] 120 | pub fn parse>(s: S) -> Result { 121 | T::parse_float(s) 122 | } 123 | 124 | /// Parse a decimal number from string into float (partial). 125 | /// 126 | /// This function parses as many characters as possible and returns the resulting number along 127 | /// with the number of digits processed (in case of success, this number is always positive). 128 | /// 129 | /// # Errors 130 | /// 131 | /// Will return an error either if the string doesn't start with a valid decimal number 132 | /// – that is, if no zero digits were processed. 133 | #[inline] 134 | pub fn parse_partial>(s: S) -> Result<(T, usize)> { 135 | T::parse_float_partial(s) 136 | } 137 | -------------------------------------------------------------------------------- /src/number.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{is_8digits, AsciiStr, ByteSlice}; 2 | use crate::float::Float; 3 | 4 | const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000; 5 | 6 | pub const INT_POW10: [u64; 16] = [ 7 | 1, 8 | 10, 9 | 100, 10 | 1000, 11 | 10000, 12 | 100000, 13 | 1000000, 14 | 10000000, 15 | 100000000, 16 | 1000000000, 17 | 10000000000, 18 | 100000000000, 19 | 1000000000000, 20 | 10000000000000, 21 | 100000000000000, 22 | 1000000000000000, 23 | ]; 24 | 25 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 26 | pub struct Number { 27 | pub exponent: i64, 28 | pub mantissa: u64, 29 | pub negative: bool, 30 | pub many_digits: bool, 31 | } 32 | 33 | impl Number { 34 | #[inline] 35 | fn is_fast_path(&self) -> bool { 36 | F::MIN_EXPONENT_FAST_PATH <= self.exponent 37 | && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH 38 | && self.mantissa <= F::MAX_MANTISSA_FAST_PATH 39 | && !self.many_digits 40 | } 41 | 42 | #[inline] 43 | pub fn try_fast_path(&self) -> Option { 44 | if self.is_fast_path::() { 45 | let mut value = if self.exponent <= F::MAX_EXPONENT_FAST_PATH { 46 | // normal fast path 47 | let value = F::from_u64(self.mantissa); 48 | if self.exponent < 0 { 49 | value / F::pow10_fast_path((-self.exponent) as _) 50 | } else { 51 | value * F::pow10_fast_path(self.exponent as _) 52 | } 53 | } else { 54 | // disguised fast path 55 | let shift = self.exponent - F::MAX_EXPONENT_FAST_PATH; 56 | let mantissa = self.mantissa.checked_mul(INT_POW10[shift as usize])?; 57 | if mantissa > F::MAX_MANTISSA_FAST_PATH { 58 | return None; 59 | } 60 | F::from_u64(mantissa) * F::pow10_fast_path(F::MAX_EXPONENT_FAST_PATH as _) 61 | }; 62 | if self.negative { 63 | value = -value; 64 | } 65 | Some(value) 66 | } else { 67 | None 68 | } 69 | } 70 | } 71 | 72 | #[inline] 73 | fn parse_8digits(mut v: u64) -> u64 { 74 | const MASK: u64 = 0x0000_00FF_0000_00FF; 75 | const MUL1: u64 = 0x000F_4240_0000_0064; 76 | const MUL2: u64 = 0x0000_2710_0000_0001; 77 | v -= 0x3030_3030_3030_3030; 78 | v = (v * 10) + (v >> 8); // will not overflow, fits in 63 bits 79 | let v1 = (v & MASK).wrapping_mul(MUL1); 80 | let v2 = ((v >> 16) & MASK).wrapping_mul(MUL2); 81 | ((v1.wrapping_add(v2) >> 32) as u32) as u64 82 | } 83 | 84 | #[inline] 85 | fn try_parse_digits(s: &mut AsciiStr<'_>, x: &mut u64) { 86 | s.parse_digits(|digit| { 87 | *x = x.wrapping_mul(10).wrapping_add(digit as _); // overflows to be handled later 88 | }); 89 | } 90 | 91 | #[inline] 92 | fn try_parse_19digits(s: &mut AsciiStr<'_>, x: &mut u64) { 93 | while *x < MIN_19DIGIT_INT && !s.is_empty() && s.first().is_ascii_digit() { 94 | let digit = s.first() - b'0'; 95 | *x = (*x * 10) + digit as u64; // no overflows here 96 | s.step(); 97 | } 98 | } 99 | 100 | #[inline] 101 | fn try_parse_8digits(s: &mut AsciiStr<'_>, x: &mut u64) { 102 | // may cause overflows, to be handled later 103 | if let Some(v) = s.try_read_u64() { 104 | if is_8digits(v) { 105 | *x = x 106 | .wrapping_mul(1_0000_0000) 107 | .wrapping_add(parse_8digits(v)); 108 | s.step_by(8); 109 | if let Some(v) = s.try_read_u64() { 110 | if is_8digits(v) { 111 | *x = x 112 | .wrapping_mul(1_0000_0000) 113 | .wrapping_add(parse_8digits(v)); 114 | s.step_by(8); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | 121 | #[inline] 122 | fn parse_scientific(s: &mut AsciiStr<'_>) -> i64 { 123 | // the first character is 'e'/'E' and scientific mode is enabled 124 | let start = *s; 125 | s.step(); 126 | let mut exp_num = 0_i64; 127 | let mut neg_exp = false; 128 | if !s.is_empty() && s.first_either(b'-', b'+') { 129 | neg_exp = s.first_is(b'-'); 130 | s.step(); 131 | } 132 | if s.check_first_digit() { 133 | s.parse_digits(|digit| { 134 | if exp_num < 0x10000 { 135 | exp_num = 10 * exp_num + digit as i64; // no overflows here 136 | } 137 | }); 138 | if neg_exp { 139 | -exp_num 140 | } else { 141 | exp_num 142 | } 143 | } else { 144 | *s = start; // ignore 'e' and return back 145 | 0 146 | } 147 | } 148 | 149 | #[inline] 150 | pub fn parse_number(s: &[u8]) -> Option<(Number, usize)> { 151 | debug_assert!(!s.is_empty()); 152 | 153 | let mut s = AsciiStr::new(s); 154 | let start = s; 155 | 156 | // handle optional +/- sign 157 | let mut negative = false; 158 | if s.first() == b'-' { 159 | negative = true; 160 | if s.step().is_empty() { 161 | return None; 162 | } 163 | } else if s.first() == b'+' && s.step().is_empty() { 164 | return None; 165 | } 166 | debug_assert!(!s.is_empty()); 167 | 168 | // parse initial digits before dot 169 | let mut mantissa = 0_u64; 170 | let digits_start = s; 171 | try_parse_digits(&mut s, &mut mantissa); 172 | let mut n_digits = s.offset_from(&digits_start); 173 | 174 | // handle dot with the following digits 175 | let mut n_after_dot = 0; 176 | let mut exponent = 0_i64; 177 | let int_end = s; 178 | if s.check_first(b'.') { 179 | s.step(); 180 | let before = s; 181 | try_parse_8digits(&mut s, &mut mantissa); 182 | try_parse_digits(&mut s, &mut mantissa); 183 | n_after_dot = s.offset_from(&before); 184 | exponent = -n_after_dot as i64; 185 | } 186 | 187 | n_digits += n_after_dot; 188 | if n_digits == 0 { 189 | return None; 190 | } 191 | 192 | // handle scientific format 193 | let mut exp_number = 0_i64; 194 | if s.check_first_either(b'e', b'E') { 195 | exp_number = parse_scientific(&mut s); 196 | exponent += exp_number; 197 | } 198 | 199 | let len = s.offset_from(&start) as _; 200 | 201 | // handle uncommon case with many digits 202 | if n_digits <= 19 { 203 | return Some(( 204 | Number { 205 | exponent, 206 | mantissa, 207 | negative, 208 | many_digits: false, 209 | }, 210 | len, 211 | )); 212 | } 213 | 214 | n_digits -= 19; 215 | let mut many_digits = false; 216 | let mut p = digits_start; 217 | while p.check_first_either(b'0', b'.') { 218 | n_digits -= p.first().saturating_sub(b'0' - 1) as isize; // '0' = b'.' + 2 219 | p.step(); 220 | } 221 | if n_digits > 0 { 222 | // at this point we have more than 19 significant digits, let's try again 223 | many_digits = true; 224 | mantissa = 0; 225 | let mut s = digits_start; 226 | try_parse_19digits(&mut s, &mut mantissa); 227 | exponent = if mantissa >= MIN_19DIGIT_INT { 228 | int_end.offset_from(&s) // big int 229 | } else { 230 | s.step(); // fractional component, skip the '.' 231 | let before = s; 232 | try_parse_19digits(&mut s, &mut mantissa); 233 | -s.offset_from(&before) 234 | } as i64; 235 | exponent += exp_number; // add back the explicit part 236 | } 237 | 238 | Some(( 239 | Number { 240 | exponent, 241 | mantissa, 242 | negative, 243 | many_digits, 244 | }, 245 | len, 246 | )) 247 | } 248 | 249 | #[inline] 250 | pub fn parse_inf_nan(s: &[u8]) -> Option<(F, usize)> { 251 | fn parse_inf_rest(s: &[u8]) -> usize { 252 | if s.len() >= 8 && s[3..].eq_ignore_case(b"inity") { 253 | 8 254 | } else { 255 | 3 256 | } 257 | } 258 | if s.len() >= 3 { 259 | if s.eq_ignore_case(b"nan") { 260 | return Some((F::NAN, 3)); 261 | } else if s.eq_ignore_case(b"inf") { 262 | return Some((F::INFINITY, parse_inf_rest(s))); 263 | } else if s.len() >= 4 { 264 | if s.get_first() == b'+' { 265 | let s = s.advance(1); 266 | if s.eq_ignore_case(b"nan") { 267 | return Some((F::NAN, 4)); 268 | } else if s.eq_ignore_case(b"inf") { 269 | return Some((F::INFINITY, 1 + parse_inf_rest(s))); 270 | } 271 | } else if s.get_first() == b'-' { 272 | let s = s.advance(1); 273 | if s.eq_ignore_case(b"nan") { 274 | return Some((F::NEG_NAN, 4)); 275 | } else if s.eq_ignore_case(b"inf") { 276 | return Some((F::NEG_INFINITY, 1 + parse_inf_rest(s))); 277 | } 278 | } 279 | } 280 | } 281 | None 282 | } 283 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use crate::binary::compute_float; 2 | use crate::float::Float; 3 | use crate::number::{parse_inf_nan, parse_number}; 4 | use crate::simple::parse_long_mantissa; 5 | 6 | #[inline] 7 | pub fn parse_float(s: &[u8]) -> Option<(F, usize)> { 8 | if s.is_empty() { 9 | return None; 10 | } 11 | 12 | let (num, rest) = match parse_number(s) { 13 | Some(r) => r, 14 | None => return parse_inf_nan(s), 15 | }; 16 | if let Some(value) = num.try_fast_path::() { 17 | return Some((value, rest)); 18 | } 19 | 20 | let mut am = compute_float::(num.exponent, num.mantissa); 21 | if num.many_digits && am != compute_float::(num.exponent, num.mantissa + 1) { 22 | am.power2 = -1; 23 | } 24 | if am.power2 < 0 { 25 | am = parse_long_mantissa::(s); 26 | } 27 | 28 | let mut word = am.mantissa; 29 | word |= (am.power2 as u64) << F::MANTISSA_EXPLICIT_BITS; 30 | if num.negative { 31 | word |= 1_u64 << F::SIGN_INDEX; 32 | } 33 | Some((F::from_u64_bits(word), rest)) 34 | } 35 | -------------------------------------------------------------------------------- /src/simple.rs: -------------------------------------------------------------------------------- 1 | use crate::common::AdjustedMantissa; 2 | use crate::decimal::{parse_decimal, Decimal}; 3 | use crate::float::Float; 4 | 5 | #[inline] 6 | pub fn parse_long_mantissa(s: &[u8]) -> AdjustedMantissa { 7 | const MAX_SHIFT: usize = 60; 8 | const NUM_POWERS: usize = 19; 9 | const POWERS: [u8; 19] = [ 10 | 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, 33, 36, 39, 43, 46, 49, 53, 56, 59, 11 | ]; 12 | 13 | let get_shift = |n| { 14 | if n < NUM_POWERS { 15 | POWERS[n] as usize 16 | } else { 17 | MAX_SHIFT 18 | } 19 | }; 20 | 21 | let am_zero = AdjustedMantissa::zero_pow2(0); 22 | let am_inf = AdjustedMantissa::zero_pow2(F::INFINITE_POWER); 23 | 24 | let mut d = parse_decimal(s); 25 | 26 | if d.num_digits == 0 || d.decimal_point < -324 { 27 | return am_zero; 28 | } else if d.decimal_point >= 310 { 29 | return am_inf; 30 | } 31 | let mut exp2 = 0_i32; 32 | while d.decimal_point > 0 { 33 | let n = d.decimal_point as usize; 34 | let shift = get_shift(n); 35 | d.right_shift(shift); 36 | if d.decimal_point < -Decimal::DECIMAL_POINT_RANGE { 37 | return am_zero; 38 | } 39 | exp2 += shift as i32; 40 | } 41 | while d.decimal_point <= 0 { 42 | let shift = if d.decimal_point == 0 { 43 | match d.digits[0] { 44 | digit if digit >= 5 => break, 45 | 0 | 1 => 2, 46 | _ => 1, 47 | } 48 | } else { 49 | get_shift((-d.decimal_point) as _) 50 | }; 51 | d.left_shift(shift); 52 | if d.decimal_point > Decimal::DECIMAL_POINT_RANGE { 53 | return am_inf; 54 | } 55 | exp2 -= shift as i32; 56 | } 57 | exp2 -= 1; 58 | while (F::MINIMUM_EXPONENT + 1) > exp2 { 59 | let mut n = ((F::MINIMUM_EXPONENT + 1) - exp2) as usize; 60 | if n > MAX_SHIFT { 61 | n = MAX_SHIFT; 62 | } 63 | d.right_shift(n); 64 | exp2 += n as i32; 65 | } 66 | if (exp2 - F::MINIMUM_EXPONENT) >= F::INFINITE_POWER { 67 | return am_inf; 68 | } 69 | d.left_shift(F::MANTISSA_EXPLICIT_BITS + 1); 70 | let mut mantissa = d.round(); 71 | if mantissa >= (1_u64 << (F::MANTISSA_EXPLICIT_BITS + 1)) { 72 | d.right_shift(1); 73 | exp2 += 1; 74 | mantissa = d.round(); 75 | if (exp2 - F::MINIMUM_EXPONENT) >= F::INFINITE_POWER { 76 | return am_inf; 77 | } 78 | } 79 | let mut power2 = exp2 - F::MINIMUM_EXPONENT; 80 | if mantissa < (1_u64 << F::MANTISSA_EXPLICIT_BITS) { 81 | power2 -= 1; 82 | } 83 | mantissa &= (1_u64 << F::MANTISSA_EXPLICIT_BITS) - 1; 84 | AdjustedMantissa { mantissa, power2 } 85 | } 86 | -------------------------------------------------------------------------------- /src/table.rs: -------------------------------------------------------------------------------- 1 | pub const SMALLEST_POWER_OF_FIVE: i32 = -342; 2 | pub const LARGEST_POWER_OF_FIVE: i32 = 308; 3 | pub const N_POWERS_OF_FIVE: usize = (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize; 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | use super::*; 8 | 9 | use num_bigint::BigUint; 10 | 11 | fn compute_pow5_128(q: i32) -> (u64, u64) { 12 | let mut c = if q < 0 { 13 | let pow5 = BigUint::from(5_u8).pow((-q) as u32); 14 | let mut z = 0_u16; 15 | while (BigUint::from(1_u8) << z) < pow5 { 16 | z += 1; 17 | } 18 | let b = if q < -27 { 2 * z + 128 } else { z + 127 }; 19 | (BigUint::from(1_u8) << b) / pow5 + BigUint::from(1_u8) 20 | } else { 21 | BigUint::from(5_u8).pow(q as u32) 22 | }; 23 | while c < (BigUint::from(1_u8) << 127) { 24 | c <<= 1; 25 | } 26 | while c >= (BigUint::from(1_u8) << 128) { 27 | c >>= 1; 28 | } 29 | let mut digits = c.to_u32_digits(); 30 | while digits.len() < 4 { 31 | digits.push(0); 32 | } 33 | assert_eq!(digits.len(), 4); 34 | let lo = (digits[0] as u64) + (digits[1] as u64 * (1_u64 << 32)); 35 | let hi = (digits[2] as u64) + (digits[3] as u64 * (1_u64 << 32)); 36 | (hi, lo) 37 | } 38 | 39 | #[test] 40 | fn test_pow5_table() { 41 | for q in SMALLEST_POWER_OF_FIVE..=LARGEST_POWER_OF_FIVE { 42 | let (hi, lo) = compute_pow5_128(q); 43 | let expected = POWER_OF_FIVE_128[(q - SMALLEST_POWER_OF_FIVE) as usize]; 44 | assert_eq!(hi, expected.0); 45 | assert_eq!(lo, expected.1); 46 | } 47 | } 48 | } 49 | 50 | #[allow(clippy::unreadable_literal)] 51 | pub const POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [ 52 | (0xeef453d6923bd65a, 0x113faa2906a13b3f), 53 | (0x9558b4661b6565f8, 0x4ac7ca59a424c507), 54 | (0xbaaee17fa23ebf76, 0x5d79bcf00d2df649), 55 | (0xe95a99df8ace6f53, 0xf4d82c2c107973dc), 56 | (0x91d8a02bb6c10594, 0x79071b9b8a4be869), 57 | (0xb64ec836a47146f9, 0x9748e2826cdee284), 58 | (0xe3e27a444d8d98b7, 0xfd1b1b2308169b25), 59 | (0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7), 60 | (0xb208ef855c969f4f, 0xbdbd2d335e51a935), 61 | (0xde8b2b66b3bc4723, 0xad2c788035e61382), 62 | (0x8b16fb203055ac76, 0x4c3bcb5021afcc31), 63 | (0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d), 64 | (0xd953e8624b85dd78, 0xd71d6dad34a2af0d), 65 | (0x87d4713d6f33aa6b, 0x8672648c40e5ad68), 66 | (0xa9c98d8ccb009506, 0x680efdaf511f18c2), 67 | (0xd43bf0effdc0ba48, 0x212bd1b2566def2), 68 | (0x84a57695fe98746d, 0x14bb630f7604b57), 69 | (0xa5ced43b7e3e9188, 0x419ea3bd35385e2d), 70 | (0xcf42894a5dce35ea, 0x52064cac828675b9), 71 | (0x818995ce7aa0e1b2, 0x7343efebd1940993), 72 | (0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8), 73 | (0xca66fa129f9b60a6, 0xd41a26e077774ef6), 74 | (0xfd00b897478238d0, 0x8920b098955522b4), 75 | (0x9e20735e8cb16382, 0x55b46e5f5d5535b0), 76 | (0xc5a890362fddbc62, 0xeb2189f734aa831d), 77 | (0xf712b443bbd52b7b, 0xa5e9ec7501d523e4), 78 | (0x9a6bb0aa55653b2d, 0x47b233c92125366e), 79 | (0xc1069cd4eabe89f8, 0x999ec0bb696e840a), 80 | (0xf148440a256e2c76, 0xc00670ea43ca250d), 81 | (0x96cd2a865764dbca, 0x380406926a5e5728), 82 | (0xbc807527ed3e12bc, 0xc605083704f5ecf2), 83 | (0xeba09271e88d976b, 0xf7864a44c633682e), 84 | (0x93445b8731587ea3, 0x7ab3ee6afbe0211d), 85 | (0xb8157268fdae9e4c, 0x5960ea05bad82964), 86 | (0xe61acf033d1a45df, 0x6fb92487298e33bd), 87 | (0x8fd0c16206306bab, 0xa5d3b6d479f8e056), 88 | (0xb3c4f1ba87bc8696, 0x8f48a4899877186c), 89 | (0xe0b62e2929aba83c, 0x331acdabfe94de87), 90 | (0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14), 91 | (0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9), 92 | (0xdb71e91432b1a24a, 0xc9e82cd9f69d6150), 93 | (0x892731ac9faf056e, 0xbe311c083a225cd2), 94 | (0xab70fe17c79ac6ca, 0x6dbd630a48aaf406), 95 | (0xd64d3d9db981787d, 0x92cbbccdad5b108), 96 | (0x85f0468293f0eb4e, 0x25bbf56008c58ea5), 97 | (0xa76c582338ed2621, 0xaf2af2b80af6f24e), 98 | (0xd1476e2c07286faa, 0x1af5af660db4aee1), 99 | (0x82cca4db847945ca, 0x50d98d9fc890ed4d), 100 | (0xa37fce126597973c, 0xe50ff107bab528a0), 101 | (0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8), 102 | (0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a), 103 | (0x9faacf3df73609b1, 0x77b191618c54e9ac), 104 | (0xc795830d75038c1d, 0xd59df5b9ef6a2417), 105 | (0xf97ae3d0d2446f25, 0x4b0573286b44ad1d), 106 | (0x9becce62836ac577, 0x4ee367f9430aec32), 107 | (0xc2e801fb244576d5, 0x229c41f793cda73f), 108 | (0xf3a20279ed56d48a, 0x6b43527578c1110f), 109 | (0x9845418c345644d6, 0x830a13896b78aaa9), 110 | (0xbe5691ef416bd60c, 0x23cc986bc656d553), 111 | (0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8), 112 | (0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9), 113 | (0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53), 114 | (0xe858ad248f5c22c9, 0xd1b3400f8f9cff68), 115 | (0x91376c36d99995be, 0x23100809b9c21fa1), 116 | (0xb58547448ffffb2d, 0xabd40a0c2832a78a), 117 | (0xe2e69915b3fff9f9, 0x16c90c8f323f516c), 118 | (0x8dd01fad907ffc3b, 0xae3da7d97f6792e3), 119 | (0xb1442798f49ffb4a, 0x99cd11cfdf41779c), 120 | (0xdd95317f31c7fa1d, 0x40405643d711d583), 121 | (0x8a7d3eef7f1cfc52, 0x482835ea666b2572), 122 | (0xad1c8eab5ee43b66, 0xda3243650005eecf), 123 | (0xd863b256369d4a40, 0x90bed43e40076a82), 124 | (0x873e4f75e2224e68, 0x5a7744a6e804a291), 125 | (0xa90de3535aaae202, 0x711515d0a205cb36), 126 | (0xd3515c2831559a83, 0xd5a5b44ca873e03), 127 | (0x8412d9991ed58091, 0xe858790afe9486c2), 128 | (0xa5178fff668ae0b6, 0x626e974dbe39a872), 129 | (0xce5d73ff402d98e3, 0xfb0a3d212dc8128f), 130 | (0x80fa687f881c7f8e, 0x7ce66634bc9d0b99), 131 | (0xa139029f6a239f72, 0x1c1fffc1ebc44e80), 132 | (0xc987434744ac874e, 0xa327ffb266b56220), 133 | (0xfbe9141915d7a922, 0x4bf1ff9f0062baa8), 134 | (0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9), 135 | (0xc4ce17b399107c22, 0xcb550fb4384d21d3), 136 | (0xf6019da07f549b2b, 0x7e2a53a146606a48), 137 | (0x99c102844f94e0fb, 0x2eda7444cbfc426d), 138 | (0xc0314325637a1939, 0xfa911155fefb5308), 139 | (0xf03d93eebc589f88, 0x793555ab7eba27ca), 140 | (0x96267c7535b763b5, 0x4bc1558b2f3458de), 141 | (0xbbb01b9283253ca2, 0x9eb1aaedfb016f16), 142 | (0xea9c227723ee8bcb, 0x465e15a979c1cadc), 143 | (0x92a1958a7675175f, 0xbfacd89ec191ec9), 144 | (0xb749faed14125d36, 0xcef980ec671f667b), 145 | (0xe51c79a85916f484, 0x82b7e12780e7401a), 146 | (0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810), 147 | (0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15), 148 | (0xdfbdcece67006ac9, 0x67a791e093e1d49a), 149 | (0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0), 150 | (0xaecc49914078536d, 0x58fae9f773886e18), 151 | (0xda7f5bf590966848, 0xaf39a475506a899e), 152 | (0x888f99797a5e012d, 0x6d8406c952429603), 153 | (0xaab37fd7d8f58178, 0xc8e5087ba6d33b83), 154 | (0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64), 155 | (0x855c3be0a17fcd26, 0x5cf2eea09a55067f), 156 | (0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e), 157 | (0xd0601d8efc57b08b, 0xf13b94daf124da26), 158 | (0x823c12795db6ce57, 0x76c53d08d6b70858), 159 | (0xa2cb1717b52481ed, 0x54768c4b0c64ca6e), 160 | (0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09), 161 | (0xfe5d54150b090b02, 0xd3f93b35435d7c4c), 162 | (0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf), 163 | (0xc6b8e9b0709f109a, 0x359ab6419ca1091b), 164 | (0xf867241c8cc6d4c0, 0xc30163d203c94b62), 165 | (0x9b407691d7fc44f8, 0x79e0de63425dcf1d), 166 | (0xc21094364dfb5636, 0x985915fc12f542e4), 167 | (0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d), 168 | (0x979cf3ca6cec5b5a, 0xa705992ceecf9c42), 169 | (0xbd8430bd08277231, 0x50c6ff782a838353), 170 | (0xece53cec4a314ebd, 0xa4f8bf5635246428), 171 | (0x940f4613ae5ed136, 0x871b7795e136be99), 172 | (0xb913179899f68584, 0x28e2557b59846e3f), 173 | (0xe757dd7ec07426e5, 0x331aeada2fe589cf), 174 | (0x9096ea6f3848984f, 0x3ff0d2c85def7621), 175 | (0xb4bca50b065abe63, 0xfed077a756b53a9), 176 | (0xe1ebce4dc7f16dfb, 0xd3e8495912c62894), 177 | (0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c), 178 | (0xb080392cc4349dec, 0xbd8d794d96aacfb3), 179 | (0xdca04777f541c567, 0xecf0d7a0fc5583a0), 180 | (0x89e42caaf9491b60, 0xf41686c49db57244), 181 | (0xac5d37d5b79b6239, 0x311c2875c522ced5), 182 | (0xd77485cb25823ac7, 0x7d633293366b828b), 183 | (0x86a8d39ef77164bc, 0xae5dff9c02033197), 184 | (0xa8530886b54dbdeb, 0xd9f57f830283fdfc), 185 | (0xd267caa862a12d66, 0xd072df63c324fd7b), 186 | (0x8380dea93da4bc60, 0x4247cb9e59f71e6d), 187 | (0xa46116538d0deb78, 0x52d9be85f074e608), 188 | (0xcd795be870516656, 0x67902e276c921f8b), 189 | (0x806bd9714632dff6, 0xba1cd8a3db53b6), 190 | (0xa086cfcd97bf97f3, 0x80e8a40eccd228a4), 191 | (0xc8a883c0fdaf7df0, 0x6122cd128006b2cd), 192 | (0xfad2a4b13d1b5d6c, 0x796b805720085f81), 193 | (0x9cc3a6eec6311a63, 0xcbe3303674053bb0), 194 | (0xc3f490aa77bd60fc, 0xbedbfc4411068a9c), 195 | (0xf4f1b4d515acb93b, 0xee92fb5515482d44), 196 | (0x991711052d8bf3c5, 0x751bdd152d4d1c4a), 197 | (0xbf5cd54678eef0b6, 0xd262d45a78a0635d), 198 | (0xef340a98172aace4, 0x86fb897116c87c34), 199 | (0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0), 200 | (0xbae0a846d2195712, 0x8974836059cca109), 201 | (0xe998d258869facd7, 0x2bd1a438703fc94b), 202 | (0x91ff83775423cc06, 0x7b6306a34627ddcf), 203 | (0xb67f6455292cbf08, 0x1a3bc84c17b1d542), 204 | (0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93), 205 | (0x8e938662882af53e, 0x547eb47b7282ee9c), 206 | (0xb23867fb2a35b28d, 0xe99e619a4f23aa43), 207 | (0xdec681f9f4c31f31, 0x6405fa00e2ec94d4), 208 | (0x8b3c113c38f9f37e, 0xde83bc408dd3dd04), 209 | (0xae0b158b4738705e, 0x9624ab50b148d445), 210 | (0xd98ddaee19068c76, 0x3badd624dd9b0957), 211 | (0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6), 212 | (0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c), 213 | (0xd47487cc8470652b, 0x7647c3200069671f), 214 | (0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073), 215 | (0xa5fb0a17c777cf09, 0xf468107100525890), 216 | (0xcf79cc9db955c2cc, 0x7182148d4066eeb4), 217 | (0x81ac1fe293d599bf, 0xc6f14cd848405530), 218 | (0xa21727db38cb002f, 0xb8ada00e5a506a7c), 219 | (0xca9cf1d206fdc03b, 0xa6d90811f0e4851c), 220 | (0xfd442e4688bd304a, 0x908f4a166d1da663), 221 | (0x9e4a9cec15763e2e, 0x9a598e4e043287fe), 222 | (0xc5dd44271ad3cdba, 0x40eff1e1853f29fd), 223 | (0xf7549530e188c128, 0xd12bee59e68ef47c), 224 | (0x9a94dd3e8cf578b9, 0x82bb74f8301958ce), 225 | (0xc13a148e3032d6e7, 0xe36a52363c1faf01), 226 | (0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1), 227 | (0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9), 228 | (0xbcb2b812db11a5de, 0x7415d448f6b6f0e7), 229 | (0xebdf661791d60f56, 0x111b495b3464ad21), 230 | (0x936b9fcebb25c995, 0xcab10dd900beec34), 231 | (0xb84687c269ef3bfb, 0x3d5d514f40eea742), 232 | (0xe65829b3046b0afa, 0xcb4a5a3112a5112), 233 | (0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab), 234 | (0xb3f4e093db73a093, 0x59ed216765690f56), 235 | (0xe0f218b8d25088b8, 0x306869c13ec3532c), 236 | (0x8c974f7383725573, 0x1e414218c73a13fb), 237 | (0xafbd2350644eeacf, 0xe5d1929ef90898fa), 238 | (0xdbac6c247d62a583, 0xdf45f746b74abf39), 239 | (0x894bc396ce5da772, 0x6b8bba8c328eb783), 240 | (0xab9eb47c81f5114f, 0x66ea92f3f326564), 241 | (0xd686619ba27255a2, 0xc80a537b0efefebd), 242 | (0x8613fd0145877585, 0xbd06742ce95f5f36), 243 | (0xa798fc4196e952e7, 0x2c48113823b73704), 244 | (0xd17f3b51fca3a7a0, 0xf75a15862ca504c5), 245 | (0x82ef85133de648c4, 0x9a984d73dbe722fb), 246 | (0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba), 247 | (0xcc963fee10b7d1b3, 0x318df905079926a8), 248 | (0xffbbcfe994e5c61f, 0xfdf17746497f7052), 249 | (0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633), 250 | (0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0), 251 | (0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0), 252 | (0x9c1661a651213e2d, 0x6bea10ca65c084e), 253 | (0xc31bfa0fe5698db8, 0x486e494fcff30a62), 254 | (0xf3e2f893dec3f126, 0x5a89dba3c3efccfa), 255 | (0x986ddb5c6b3a76b7, 0xf89629465a75e01c), 256 | (0xbe89523386091465, 0xf6bbb397f1135823), 257 | (0xee2ba6c0678b597f, 0x746aa07ded582e2c), 258 | (0x94db483840b717ef, 0xa8c2a44eb4571cdc), 259 | (0xba121a4650e4ddeb, 0x92f34d62616ce413), 260 | (0xe896a0d7e51e1566, 0x77b020baf9c81d17), 261 | (0x915e2486ef32cd60, 0xace1474dc1d122e), 262 | (0xb5b5ada8aaff80b8, 0xd819992132456ba), 263 | (0xe3231912d5bf60e6, 0x10e1fff697ed6c69), 264 | (0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1), 265 | (0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2), 266 | (0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde), 267 | (0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b), 268 | (0xad4ab7112eb3929d, 0x86c16c98d2c953c6), 269 | (0xd89d64d57a607744, 0xe871c7bf077ba8b7), 270 | (0x87625f056c7c4a8b, 0x11471cd764ad4972), 271 | (0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf), 272 | (0xd389b47879823479, 0x4aff1d108d4ec2c3), 273 | (0x843610cb4bf160cb, 0xcedf722a585139ba), 274 | (0xa54394fe1eedb8fe, 0xc2974eb4ee658828), 275 | (0xce947a3da6a9273e, 0x733d226229feea32), 276 | (0x811ccc668829b887, 0x806357d5a3f525f), 277 | (0xa163ff802a3426a8, 0xca07c2dcb0cf26f7), 278 | (0xc9bcff6034c13052, 0xfc89b393dd02f0b5), 279 | (0xfc2c3f3841f17c67, 0xbbac2078d443ace2), 280 | (0x9d9ba7832936edc0, 0xd54b944b84aa4c0d), 281 | (0xc5029163f384a931, 0xa9e795e65d4df11), 282 | (0xf64335bcf065d37d, 0x4d4617b5ff4a16d5), 283 | (0x99ea0196163fa42e, 0x504bced1bf8e4e45), 284 | (0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6), 285 | (0xf07da27a82c37088, 0x5d767327bb4e5a4c), 286 | (0x964e858c91ba2655, 0x3a6a07f8d510f86f), 287 | (0xbbe226efb628afea, 0x890489f70a55368b), 288 | (0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e), 289 | (0x92c8ae6b464fc96f, 0x3b0b8bc90012929d), 290 | (0xb77ada0617e3bbcb, 0x9ce6ebb40173744), 291 | (0xe55990879ddcaabd, 0xcc420a6a101d0515), 292 | (0x8f57fa54c2a9eab6, 0x9fa946824a12232d), 293 | (0xb32df8e9f3546564, 0x47939822dc96abf9), 294 | (0xdff9772470297ebd, 0x59787e2b93bc56f7), 295 | (0x8bfbea76c619ef36, 0x57eb4edb3c55b65a), 296 | (0xaefae51477a06b03, 0xede622920b6b23f1), 297 | (0xdab99e59958885c4, 0xe95fab368e45eced), 298 | (0x88b402f7fd75539b, 0x11dbcb0218ebb414), 299 | (0xaae103b5fcd2a881, 0xd652bdc29f26a119), 300 | (0xd59944a37c0752a2, 0x4be76d3346f0495f), 301 | (0x857fcae62d8493a5, 0x6f70a4400c562ddb), 302 | (0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952), 303 | (0xd097ad07a71f26b2, 0x7e2000a41346a7a7), 304 | (0x825ecc24c873782f, 0x8ed400668c0c28c8), 305 | (0xa2f67f2dfa90563b, 0x728900802f0f32fa), 306 | (0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9), 307 | (0xfea126b7d78186bc, 0xe2f610c84987bfa8), 308 | (0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9), 309 | (0xc6ede63fa05d3143, 0x91503d1c79720dbb), 310 | (0xf8a95fcf88747d94, 0x75a44c6397ce912a), 311 | (0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba), 312 | (0xc24452da229b021b, 0xfbe85badce996168), 313 | (0xf2d56790ab41c2a2, 0xfae27299423fb9c3), 314 | (0x97c560ba6b0919a5, 0xdccd879fc967d41a), 315 | (0xbdb6b8e905cb600f, 0x5400e987bbc1c920), 316 | (0xed246723473e3813, 0x290123e9aab23b68), 317 | (0x9436c0760c86e30b, 0xf9a0b6720aaf6521), 318 | (0xb94470938fa89bce, 0xf808e40e8d5b3e69), 319 | (0xe7958cb87392c2c2, 0xb60b1d1230b20e04), 320 | (0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2), 321 | (0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3), 322 | (0xe2280b6c20dd5232, 0x25c6da63c38de1b0), 323 | (0x8d590723948a535f, 0x579c487e5a38ad0e), 324 | (0xb0af48ec79ace837, 0x2d835a9df0c6d851), 325 | (0xdcdb1b2798182244, 0xf8e431456cf88e65), 326 | (0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff), 327 | (0xac8b2d36eed2dac5, 0xe272467e3d222f3f), 328 | (0xd7adf884aa879177, 0x5b0ed81dcc6abb0f), 329 | (0x86ccbb52ea94baea, 0x98e947129fc2b4e9), 330 | (0xa87fea27a539e9a5, 0x3f2398d747b36224), 331 | (0xd29fe4b18e88640e, 0x8eec7f0d19a03aad), 332 | (0x83a3eeeef9153e89, 0x1953cf68300424ac), 333 | (0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7), 334 | (0xcdb02555653131b6, 0x3792f412cb06794d), 335 | (0x808e17555f3ebf11, 0xe2bbd88bbee40bd0), 336 | (0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4), 337 | (0xc8de047564d20a8b, 0xf245825a5a445275), 338 | (0xfb158592be068d2e, 0xeed6e2f0f0d56712), 339 | (0x9ced737bb6c4183d, 0x55464dd69685606b), 340 | (0xc428d05aa4751e4c, 0xaa97e14c3c26b886), 341 | (0xf53304714d9265df, 0xd53dd99f4b3066a8), 342 | (0x993fe2c6d07b7fab, 0xe546a8038efe4029), 343 | (0xbf8fdb78849a5f96, 0xde98520472bdd033), 344 | (0xef73d256a5c0f77c, 0x963e66858f6d4440), 345 | (0x95a8637627989aad, 0xdde7001379a44aa8), 346 | (0xbb127c53b17ec159, 0x5560c018580d5d52), 347 | (0xe9d71b689dde71af, 0xaab8f01e6e10b4a6), 348 | (0x9226712162ab070d, 0xcab3961304ca70e8), 349 | (0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22), 350 | (0xe45c10c42a2b3b05, 0x8cb89a7db77c506a), 351 | (0x8eb98a7a9a5b04e3, 0x77f3608e92adb242), 352 | (0xb267ed1940f1c61c, 0x55f038b237591ed3), 353 | (0xdf01e85f912e37a3, 0x6b6c46dec52f6688), 354 | (0x8b61313bbabce2c6, 0x2323ac4b3b3da015), 355 | (0xae397d8aa96c1b77, 0xabec975e0a0d081a), 356 | (0xd9c7dced53c72255, 0x96e7bd358c904a21), 357 | (0x881cea14545c7575, 0x7e50d64177da2e54), 358 | (0xaa242499697392d2, 0xdde50bd1d5d0b9e9), 359 | (0xd4ad2dbfc3d07787, 0x955e4ec64b44e864), 360 | (0x84ec3c97da624ab4, 0xbd5af13bef0b113e), 361 | (0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e), 362 | (0xcfb11ead453994ba, 0x67de18eda5814af2), 363 | (0x81ceb32c4b43fcf4, 0x80eacf948770ced7), 364 | (0xa2425ff75e14fc31, 0xa1258379a94d028d), 365 | (0xcad2f7f5359a3b3e, 0x96ee45813a04330), 366 | (0xfd87b5f28300ca0d, 0x8bca9d6e188853fc), 367 | (0x9e74d1b791e07e48, 0x775ea264cf55347e), 368 | (0xc612062576589dda, 0x95364afe032a819e), 369 | (0xf79687aed3eec551, 0x3a83ddbd83f52205), 370 | (0x9abe14cd44753b52, 0xc4926a9672793543), 371 | (0xc16d9a0095928a27, 0x75b7053c0f178294), 372 | (0xf1c90080baf72cb1, 0x5324c68b12dd6339), 373 | (0x971da05074da7bee, 0xd3f6fc16ebca5e04), 374 | (0xbce5086492111aea, 0x88f4bb1ca6bcf585), 375 | (0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6), 376 | (0x9392ee8e921d5d07, 0x3aff322e62439fd0), 377 | (0xb877aa3236a4b449, 0x9befeb9fad487c3), 378 | (0xe69594bec44de15b, 0x4c2ebe687989a9b4), 379 | (0x901d7cf73ab0acd9, 0xf9d37014bf60a11), 380 | (0xb424dc35095cd80f, 0x538484c19ef38c95), 381 | (0xe12e13424bb40e13, 0x2865a5f206b06fba), 382 | (0x8cbccc096f5088cb, 0xf93f87b7442e45d4), 383 | (0xafebff0bcb24aafe, 0xf78f69a51539d749), 384 | (0xdbe6fecebdedd5be, 0xb573440e5a884d1c), 385 | (0x89705f4136b4a597, 0x31680a88f8953031), 386 | (0xabcc77118461cefc, 0xfdc20d2b36ba7c3e), 387 | (0xd6bf94d5e57a42bc, 0x3d32907604691b4d), 388 | (0x8637bd05af6c69b5, 0xa63f9a49c2c1b110), 389 | (0xa7c5ac471b478423, 0xfcf80dc33721d54), 390 | (0xd1b71758e219652b, 0xd3c36113404ea4a9), 391 | (0x83126e978d4fdf3b, 0x645a1cac083126ea), 392 | (0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4), 393 | (0xcccccccccccccccc, 0xcccccccccccccccd), 394 | (0x8000000000000000, 0x0), 395 | (0xa000000000000000, 0x0), 396 | (0xc800000000000000, 0x0), 397 | (0xfa00000000000000, 0x0), 398 | (0x9c40000000000000, 0x0), 399 | (0xc350000000000000, 0x0), 400 | (0xf424000000000000, 0x0), 401 | (0x9896800000000000, 0x0), 402 | (0xbebc200000000000, 0x0), 403 | (0xee6b280000000000, 0x0), 404 | (0x9502f90000000000, 0x0), 405 | (0xba43b74000000000, 0x0), 406 | (0xe8d4a51000000000, 0x0), 407 | (0x9184e72a00000000, 0x0), 408 | (0xb5e620f480000000, 0x0), 409 | (0xe35fa931a0000000, 0x0), 410 | (0x8e1bc9bf04000000, 0x0), 411 | (0xb1a2bc2ec5000000, 0x0), 412 | (0xde0b6b3a76400000, 0x0), 413 | (0x8ac7230489e80000, 0x0), 414 | (0xad78ebc5ac620000, 0x0), 415 | (0xd8d726b7177a8000, 0x0), 416 | (0x878678326eac9000, 0x0), 417 | (0xa968163f0a57b400, 0x0), 418 | (0xd3c21bcecceda100, 0x0), 419 | (0x84595161401484a0, 0x0), 420 | (0xa56fa5b99019a5c8, 0x0), 421 | (0xcecb8f27f4200f3a, 0x0), 422 | (0x813f3978f8940984, 0x4000000000000000), 423 | (0xa18f07d736b90be5, 0x5000000000000000), 424 | (0xc9f2c9cd04674ede, 0xa400000000000000), 425 | (0xfc6f7c4045812296, 0x4d00000000000000), 426 | (0x9dc5ada82b70b59d, 0xf020000000000000), 427 | (0xc5371912364ce305, 0x6c28000000000000), 428 | (0xf684df56c3e01bc6, 0xc732000000000000), 429 | (0x9a130b963a6c115c, 0x3c7f400000000000), 430 | (0xc097ce7bc90715b3, 0x4b9f100000000000), 431 | (0xf0bdc21abb48db20, 0x1e86d40000000000), 432 | (0x96769950b50d88f4, 0x1314448000000000), 433 | (0xbc143fa4e250eb31, 0x17d955a000000000), 434 | (0xeb194f8e1ae525fd, 0x5dcfab0800000000), 435 | (0x92efd1b8d0cf37be, 0x5aa1cae500000000), 436 | (0xb7abc627050305ad, 0xf14a3d9e40000000), 437 | (0xe596b7b0c643c719, 0x6d9ccd05d0000000), 438 | (0x8f7e32ce7bea5c6f, 0xe4820023a2000000), 439 | (0xb35dbf821ae4f38b, 0xdda2802c8a800000), 440 | (0xe0352f62a19e306e, 0xd50b2037ad200000), 441 | (0x8c213d9da502de45, 0x4526f422cc340000), 442 | (0xaf298d050e4395d6, 0x9670b12b7f410000), 443 | (0xdaf3f04651d47b4c, 0x3c0cdd765f114000), 444 | (0x88d8762bf324cd0f, 0xa5880a69fb6ac800), 445 | (0xab0e93b6efee0053, 0x8eea0d047a457a00), 446 | (0xd5d238a4abe98068, 0x72a4904598d6d880), 447 | (0x85a36366eb71f041, 0x47a6da2b7f864750), 448 | (0xa70c3c40a64e6c51, 0x999090b65f67d924), 449 | (0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d), 450 | (0x82818f1281ed449f, 0xbff8f10e7a8921a4), 451 | (0xa321f2d7226895c7, 0xaff72d52192b6a0d), 452 | (0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490), 453 | (0xfee50b7025c36a08, 0x2f236d04753d5b4), 454 | (0x9f4f2726179a2245, 0x1d762422c946590), 455 | (0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5), 456 | (0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2), 457 | (0x9b934c3b330c8577, 0x63cc55f49f88eb2f), 458 | (0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb), 459 | (0xf316271c7fc3908a, 0x8bef464e3945ef7a), 460 | (0x97edd871cfda3a56, 0x97758bf0e3cbb5ac), 461 | (0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317), 462 | (0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd), 463 | (0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a), 464 | (0xb975d6b6ee39e436, 0xb3e2fd538e122b44), 465 | (0xe7d34c64a9c85d44, 0x60dbbca87196b616), 466 | (0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd), 467 | (0xb51d13aea4a488dd, 0x6babab6398bdbe41), 468 | (0xe264589a4dcdab14, 0xc696963c7eed2dd1), 469 | (0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2), 470 | (0xb0de65388cc8ada8, 0x3b25a55f43294bcb), 471 | (0xdd15fe86affad912, 0x49ef0eb713f39ebe), 472 | (0x8a2dbf142dfcc7ab, 0x6e3569326c784337), 473 | (0xacb92ed9397bf996, 0x49c2c37f07965404), 474 | (0xd7e77a8f87daf7fb, 0xdc33745ec97be906), 475 | (0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3), 476 | (0xa8acd7c0222311bc, 0xc40832ea0d68ce0c), 477 | (0xd2d80db02aabd62b, 0xf50a3fa490c30190), 478 | (0x83c7088e1aab65db, 0x792667c6da79e0fa), 479 | (0xa4b8cab1a1563f52, 0x577001b891185938), 480 | (0xcde6fd5e09abcf26, 0xed4c0226b55e6f86), 481 | (0x80b05e5ac60b6178, 0x544f8158315b05b4), 482 | (0xa0dc75f1778e39d6, 0x696361ae3db1c721), 483 | (0xc913936dd571c84c, 0x3bc3a19cd1e38e9), 484 | (0xfb5878494ace3a5f, 0x4ab48a04065c723), 485 | (0x9d174b2dcec0e47b, 0x62eb0d64283f9c76), 486 | (0xc45d1df942711d9a, 0x3ba5d0bd324f8394), 487 | (0xf5746577930d6500, 0xca8f44ec7ee36479), 488 | (0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb), 489 | (0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e), 490 | (0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e), 491 | (0x95d04aee3b80ece5, 0xbba1f1d158724a12), 492 | (0xbb445da9ca61281f, 0x2a8a6e45ae8edc97), 493 | (0xea1575143cf97226, 0xf52d09d71a3293bd), 494 | (0x924d692ca61be758, 0x593c2626705f9c56), 495 | (0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c), 496 | (0xe498f455c38b997a, 0xb6dfb9c0f956447), 497 | (0x8edf98b59a373fec, 0x4724bd4189bd5eac), 498 | (0xb2977ee300c50fe7, 0x58edec91ec2cb657), 499 | (0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed), 500 | (0x8b865b215899f46c, 0xbd79e0d20082ee74), 501 | (0xae67f1e9aec07187, 0xecd8590680a3aa11), 502 | (0xda01ee641a708de9, 0xe80e6f4820cc9495), 503 | (0x884134fe908658b2, 0x3109058d147fdcdd), 504 | (0xaa51823e34a7eede, 0xbd4b46f0599fd415), 505 | (0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a), 506 | (0x850fadc09923329e, 0x3e2cf6bc604ddb0), 507 | (0xa6539930bf6bff45, 0x84db8346b786151c), 508 | (0xcfe87f7cef46ff16, 0xe612641865679a63), 509 | (0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e), 510 | (0xa26da3999aef7749, 0xe3be5e330f38f09d), 511 | (0xcb090c8001ab551c, 0x5cadf5bfd3072cc5), 512 | (0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6), 513 | (0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa), 514 | (0xc646d63501a1511d, 0xb281e1fd541501b8), 515 | (0xf7d88bc24209a565, 0x1f225a7ca91a4226), 516 | (0x9ae757596946075f, 0x3375788de9b06958), 517 | (0xc1a12d2fc3978937, 0x52d6b1641c83ae), 518 | (0xf209787bb47d6b84, 0xc0678c5dbd23a49a), 519 | (0x9745eb4d50ce6332, 0xf840b7ba963646e0), 520 | (0xbd176620a501fbff, 0xb650e5a93bc3d898), 521 | (0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe), 522 | (0x93ba47c980e98cdf, 0xc66f336c36b10137), 523 | (0xb8a8d9bbe123f017, 0xb80b0047445d4184), 524 | (0xe6d3102ad96cec1d, 0xa60dc059157491e5), 525 | (0x9043ea1ac7e41392, 0x87c89837ad68db2f), 526 | (0xb454e4a179dd1877, 0x29babe4598c311fb), 527 | (0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a), 528 | (0x8ce2529e2734bb1d, 0x1899e4a65f58660c), 529 | (0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f), 530 | (0xdc21a1171d42645d, 0x76707543f4fa1f73), 531 | (0x899504ae72497eba, 0x6a06494a791c53a8), 532 | (0xabfa45da0edbde69, 0x487db9d17636892), 533 | (0xd6f8d7509292d603, 0x45a9d2845d3c42b6), 534 | (0x865b86925b9bc5c2, 0xb8a2392ba45a9b2), 535 | (0xa7f26836f282b732, 0x8e6cac7768d7141e), 536 | (0xd1ef0244af2364ff, 0x3207d795430cd926), 537 | (0x8335616aed761f1f, 0x7f44e6bd49e807b8), 538 | (0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6), 539 | (0xcd036837130890a1, 0x36dba887c37a8c0f), 540 | (0x802221226be55a64, 0xc2494954da2c9789), 541 | (0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c), 542 | (0xc83553c5c8965d3d, 0x6f92829494e5acc7), 543 | (0xfa42a8b73abbf48c, 0xcb772339ba1f17f9), 544 | (0x9c69a97284b578d7, 0xff2a760414536efb), 545 | (0xc38413cf25e2d70d, 0xfef5138519684aba), 546 | (0xf46518c2ef5b8cd1, 0x7eb258665fc25d69), 547 | (0x98bf2f79d5993802, 0xef2f773ffbd97a61), 548 | (0xbeeefb584aff8603, 0xaafb550ffacfd8fa), 549 | (0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38), 550 | (0x952ab45cfa97a0b2, 0xdd945a747bf26183), 551 | (0xba756174393d88df, 0x94f971119aeef9e4), 552 | (0xe912b9d1478ceb17, 0x7a37cd5601aab85d), 553 | (0x91abb422ccb812ee, 0xac62e055c10ab33a), 554 | (0xb616a12b7fe617aa, 0x577b986b314d6009), 555 | (0xe39c49765fdf9d94, 0xed5a7e85fda0b80b), 556 | (0x8e41ade9fbebc27d, 0x14588f13be847307), 557 | (0xb1d219647ae6b31c, 0x596eb2d8ae258fc8), 558 | (0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb), 559 | (0x8aec23d680043bee, 0x25de7bb9480d5854), 560 | (0xada72ccc20054ae9, 0xaf561aa79a10ae6a), 561 | (0xd910f7ff28069da4, 0x1b2ba1518094da04), 562 | (0x87aa9aff79042286, 0x90fb44d2f05d0842), 563 | (0xa99541bf57452b28, 0x353a1607ac744a53), 564 | (0xd3fa922f2d1675f2, 0x42889b8997915ce8), 565 | (0x847c9b5d7c2e09b7, 0x69956135febada11), 566 | (0xa59bc234db398c25, 0x43fab9837e699095), 567 | (0xcf02b2c21207ef2e, 0x94f967e45e03f4bb), 568 | (0x8161afb94b44f57d, 0x1d1be0eebac278f5), 569 | (0xa1ba1ba79e1632dc, 0x6462d92a69731732), 570 | (0xca28a291859bbf93, 0x7d7b8f7503cfdcfe), 571 | (0xfcb2cb35e702af78, 0x5cda735244c3d43e), 572 | (0x9defbf01b061adab, 0x3a0888136afa64a7), 573 | (0xc56baec21c7a1916, 0x88aaa1845b8fdd0), 574 | (0xf6c69a72a3989f5b, 0x8aad549e57273d45), 575 | (0x9a3c2087a63f6399, 0x36ac54e2f678864b), 576 | (0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd), 577 | (0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5), 578 | (0x969eb7c47859e743, 0x9f644ae5a4b1b325), 579 | (0xbc4665b596706114, 0x873d5d9f0dde1fee), 580 | (0xeb57ff22fc0c7959, 0xa90cb506d155a7ea), 581 | (0x9316ff75dd87cbd8, 0x9a7f12442d588f2), 582 | (0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f), 583 | (0xe5d3ef282a242e81, 0x8f1668c8a86da5fa), 584 | (0x8fa475791a569d10, 0xf96e017d694487bc), 585 | (0xb38d92d760ec4455, 0x37c981dcc395a9ac), 586 | (0xe070f78d3927556a, 0x85bbe253f47b1417), 587 | (0x8c469ab843b89562, 0x93956d7478ccec8e), 588 | (0xaf58416654a6babb, 0x387ac8d1970027b2), 589 | (0xdb2e51bfe9d0696a, 0x6997b05fcc0319e), 590 | (0x88fcf317f22241e2, 0x441fece3bdf81f03), 591 | (0xab3c2fddeeaad25a, 0xd527e81cad7626c3), 592 | (0xd60b3bd56a5586f1, 0x8a71e223d8d3b074), 593 | (0x85c7056562757456, 0xf6872d5667844e49), 594 | (0xa738c6bebb12d16c, 0xb428f8ac016561db), 595 | (0xd106f86e69d785c7, 0xe13336d701beba52), 596 | (0x82a45b450226b39c, 0xecc0024661173473), 597 | (0xa34d721642b06084, 0x27f002d7f95d0190), 598 | (0xcc20ce9bd35c78a5, 0x31ec038df7b441f4), 599 | (0xff290242c83396ce, 0x7e67047175a15271), 600 | (0x9f79a169bd203e41, 0xf0062c6e984d386), 601 | (0xc75809c42c684dd1, 0x52c07b78a3e60868), 602 | (0xf92e0c3537826145, 0xa7709a56ccdf8a82), 603 | (0x9bbcc7a142b17ccb, 0x88a66076400bb691), 604 | (0xc2abf989935ddbfe, 0x6acff893d00ea435), 605 | (0xf356f7ebf83552fe, 0x583f6b8c4124d43), 606 | (0x98165af37b2153de, 0xc3727a337a8b704a), 607 | (0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c), 608 | (0xeda2ee1c7064130c, 0x1162def06f79df73), 609 | (0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8), 610 | (0xb9a74a0637ce2ee1, 0x6d953e2bd7173692), 611 | (0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437), 612 | (0x910ab1d4db9914a0, 0x1d9c9892400a22a2), 613 | (0xb54d5e4a127f59c8, 0x2503beb6d00cab4b), 614 | (0xe2a0b5dc971f303a, 0x2e44ae64840fd61d), 615 | (0x8da471a9de737e24, 0x5ceaecfed289e5d2), 616 | (0xb10d8e1456105dad, 0x7425a83e872c5f47), 617 | (0xdd50f1996b947518, 0xd12f124e28f77719), 618 | (0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f), 619 | (0xace73cbfdc0bfb7b, 0x636cc64d1001550b), 620 | (0xd8210befd30efa5a, 0x3c47f7e05401aa4e), 621 | (0x8714a775e3e95c78, 0x65acfaec34810a71), 622 | (0xa8d9d1535ce3b396, 0x7f1839a741a14d0d), 623 | (0xd31045a8341ca07c, 0x1ede48111209a050), 624 | (0x83ea2b892091e44d, 0x934aed0aab460432), 625 | (0xa4e4b66b68b65d60, 0xf81da84d5617853f), 626 | (0xce1de40642e3f4b9, 0x36251260ab9d668e), 627 | (0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019), 628 | (0xa1075a24e4421730, 0xb24cf65b8612f81f), 629 | (0xc94930ae1d529cfc, 0xdee033f26797b627), 630 | (0xfb9b7cd9a4a7443c, 0x169840ef017da3b1), 631 | (0x9d412e0806e88aa5, 0x8e1f289560ee864e), 632 | (0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2), 633 | (0xf5b5d7ec8acb58a2, 0xae10af696774b1db), 634 | (0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29), 635 | (0xbff610b0cc6edd3f, 0x17fd090a58d32af3), 636 | (0xeff394dcff8a948e, 0xddfc4b4cef07f5b0), 637 | (0x95f83d0a1fb69cd9, 0x4abdaf101564f98e), 638 | (0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1), 639 | (0xea53df5fd18d5513, 0x84c86189216dc5ed), 640 | (0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4), 641 | (0xb7118682dbb66a77, 0x3fbc8c33221dc2a1), 642 | (0xe4d5e82392a40515, 0xfabaf3feaa5334a), 643 | (0x8f05b1163ba6832d, 0x29cb4d87f2a7400e), 644 | (0xb2c71d5bca9023f8, 0x743e20e9ef511012), 645 | (0xdf78e4b2bd342cf6, 0x914da9246b255416), 646 | (0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e), 647 | (0xae9672aba3d0c320, 0xa184ac2473b529b1), 648 | (0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e), 649 | (0x8865899617fb1871, 0x7e2fa67c7a658892), 650 | (0xaa7eebfb9df9de8d, 0xddbb901b98feeab7), 651 | (0xd51ea6fa85785631, 0x552a74227f3ea565), 652 | (0x8533285c936b35de, 0xd53a88958f87275f), 653 | (0xa67ff273b8460356, 0x8a892abaf368f137), 654 | (0xd01fef10a657842c, 0x2d2b7569b0432d85), 655 | (0x8213f56a67f6b29b, 0x9c3b29620e29fc73), 656 | (0xa298f2c501f45f42, 0x8349f3ba91b47b8f), 657 | (0xcb3f2f7642717713, 0x241c70a936219a73), 658 | (0xfe0efb53d30dd4d7, 0xed238cd383aa0110), 659 | (0x9ec95d1463e8a506, 0xf4363804324a40aa), 660 | (0xc67bb4597ce2ce48, 0xb143c6053edcd0d5), 661 | (0xf81aa16fdc1b81da, 0xdd94b7868e94050a), 662 | (0x9b10a4e5e9913128, 0xca7cf2b4191c8326), 663 | (0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0), 664 | (0xf24a01a73cf2dccf, 0xbc633b39673c8cec), 665 | (0x976e41088617ca01, 0xd5be0503e085d813), 666 | (0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18), 667 | (0xec9c459d51852ba2, 0xddf8e7d60ed1219e), 668 | (0x93e1ab8252f33b45, 0xcabb90e5c942b503), 669 | (0xb8da1662e7b00a17, 0x3d6a751f3b936243), 670 | (0xe7109bfba19c0c9d, 0xcc512670a783ad4), 671 | (0x906a617d450187e2, 0x27fb2b80668b24c5), 672 | (0xb484f9dc9641e9da, 0xb1f9f660802dedf6), 673 | (0xe1a63853bbd26451, 0x5e7873f8a0396973), 674 | (0x8d07e33455637eb2, 0xdb0b487b6423e1e8), 675 | (0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62), 676 | (0xdc5c5301c56b75f7, 0x7641a140cc7810fb), 677 | (0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d), 678 | (0xac2820d9623bf429, 0x546345fa9fbdcd44), 679 | (0xd732290fbacaf133, 0xa97c177947ad4095), 680 | (0x867f59a9d4bed6c0, 0x49ed8eabcccc485d), 681 | (0xa81f301449ee8c70, 0x5c68f256bfff5a74), 682 | (0xd226fc195c6a2f8c, 0x73832eec6fff3111), 683 | (0x83585d8fd9c25db7, 0xc831fd53c5ff7eab), 684 | (0xa42e74f3d032f525, 0xba3e7ca8b77f5e55), 685 | (0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb), 686 | (0x80444b5e7aa7cf85, 0x7980d163cf5b81b3), 687 | (0xa0555e361951c366, 0xd7e105bcc332621f), 688 | (0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7), 689 | (0xfa856334878fc150, 0xb14f98f6f0feb951), 690 | (0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3), 691 | (0xc3b8358109e84f07, 0xa862f80ec4700c8), 692 | (0xf4a642e14c6262c8, 0xcd27bb612758c0fa), 693 | (0x98e7e9cccfbd7dbd, 0x8038d51cb897789c), 694 | (0xbf21e44003acdd2c, 0xe0470a63e6bd56c3), 695 | (0xeeea5d5004981478, 0x1858ccfce06cac74), 696 | (0x95527a5202df0ccb, 0xf37801e0c43ebc8), 697 | (0xbaa718e68396cffd, 0xd30560258f54e6ba), 698 | (0xe950df20247c83fd, 0x47c6b82ef32a2069), 699 | (0x91d28b7416cdd27e, 0x4cdc331d57fa5441), 700 | (0xb6472e511c81471d, 0xe0133fe4adf8e952), 701 | (0xe3d8f9e563a198e5, 0x58180fddd97723a6), 702 | (0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648), 703 | ]; 704 | -------------------------------------------------------------------------------- /tests/test_api.rs: -------------------------------------------------------------------------------- 1 | use fast_float::{parse, parse_partial, FastFloat}; 2 | 3 | macro_rules! check_ok { 4 | ($s:expr, $x:expr) => { 5 | let s = $s; 6 | check_ok!(s, $x, f32); 7 | check_ok!(s.as_bytes(), $x, f32); 8 | check_ok!(s, $x, f64); 9 | check_ok!(s.as_bytes(), $x, f64); 10 | }; 11 | ($s:expr, $x:expr, $ty:ty) => { 12 | assert_eq!(<$ty>::parse_float($s).unwrap(), $x); 13 | assert_eq!(<$ty>::parse_float_partial($s).unwrap(), ($x, $s.len())); 14 | assert_eq!(parse::<$ty, _>($s).unwrap(), $x); 15 | assert_eq!(parse_partial::<$ty, _>($s).unwrap(), ($x, $s.len())); 16 | }; 17 | } 18 | 19 | macro_rules! check_ok_partial { 20 | ($s:expr, $x:expr, $n:expr) => { 21 | let s = $s; 22 | check_ok_partial!(s, $x, $n, f32); 23 | check_ok_partial!(s.as_bytes(), $x, $n, f32); 24 | check_ok_partial!(s, $x, $n, f64); 25 | check_ok_partial!(s.as_bytes(), $x, $n, f64); 26 | }; 27 | ($s:expr, $x:expr, $n:expr, $ty:ty) => { 28 | assert!(<$ty>::parse_float($s).is_err()); 29 | assert_eq!(<$ty>::parse_float_partial($s).unwrap(), ($x, $n)); 30 | assert!(parse::<$ty, _>($s).is_err()); 31 | assert_eq!(parse_partial::<$ty, _>($s).unwrap(), ($x, $n)); 32 | }; 33 | } 34 | 35 | macro_rules! check_err { 36 | ($s:expr) => { 37 | let s = $s; 38 | check_err!(s, f32); 39 | check_err!(s.as_bytes(), f32); 40 | check_err!(s, f64); 41 | check_err!(s.as_bytes(), f64); 42 | }; 43 | ($s:expr, $ty:ty) => { 44 | assert!(<$ty>::parse_float($s).is_err()); 45 | assert!(<$ty>::parse_float_partial($s).is_err()); 46 | assert!(parse::<$ty, _>($s).is_err()); 47 | assert!(parse_partial::<$ty, _>($s).is_err()); 48 | }; 49 | } 50 | 51 | #[test] 52 | fn test_api() { 53 | check_ok!("1.23", 1.23); 54 | check_ok!("0.", 0.); 55 | check_ok!("-0", 0.); 56 | check_ok!("+00", 0.); 57 | check_ok!("-0001e-02", -0.01); 58 | check_ok!("345", 345.); 59 | 60 | check_ok_partial!("1a", 1., 1); 61 | check_ok_partial!("-2e-1x", -0.2, 5); 62 | check_ok_partial!("2e2.", 200., 3); 63 | check_ok_partial!("2ea", 2., 1); 64 | 65 | check_err!(""); 66 | check_err!(" "); 67 | check_err!("."); 68 | check_err!(".e1"); 69 | check_err!("+"); 70 | check_err!("-"); 71 | check_err!("x"); 72 | check_err!("a123"); 73 | } 74 | -------------------------------------------------------------------------------- /tests/test_basic.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use hexf_parse::{parse_hexf32, parse_hexf64}; 4 | 5 | fn hexf32(s: &str) -> f32 { 6 | parse_hexf32(s, false).unwrap() 7 | } 8 | 9 | fn hexf64(s: &str) -> f64 { 10 | parse_hexf64(s, false).unwrap() 11 | } 12 | 13 | macro_rules! check { 14 | ($ty:ident, $s:expr) => {{ 15 | check!($ty, stringify!($s), $s) 16 | }}; 17 | ($ty:ident, $s:expr, inf) => {{ 18 | check!($ty, $s, core::$ty::INFINITY) 19 | }}; 20 | ($ty:ident, $s:expr, neg_inf) => {{ 21 | check!($ty, $s, core::$ty::NEG_INFINITY) 22 | }}; 23 | ($ty:ident, $s:expr, $e:expr) => {{ 24 | let string = String::from($s); 25 | let s = string.as_bytes(); 26 | let expected: $ty = $e; 27 | let result = fast_float::parse::<$ty, _>(s).unwrap(); 28 | assert_eq!(result, expected); 29 | let lex = lexical_core::parse::<$ty>(s).unwrap(); 30 | assert_eq!(result, lex); 31 | let std = <$ty>::from_str(string.as_str()); 32 | if let Ok(std) = std { 33 | // stdlib can't parse all weird floats 34 | if std.is_finite() && result.is_finite() { 35 | // some weird edge cases stdlib parses as inf, e.g. 0e999999999999999 36 | assert_eq!(result, std); 37 | } 38 | } 39 | }}; 40 | } 41 | macro_rules! check_lex { 42 | ($ty:ident, $s:expr) => {{ 43 | let v = lexical_core::parse::<$ty>($s.as_bytes()).unwrap(); 44 | check!($ty, $s, v); 45 | }}; 46 | } 47 | macro_rules! check_f32 { 48 | ($($tt:tt)+) => { 49 | check!(f32, $($tt)+) 50 | } 51 | } 52 | macro_rules! check_f64 { 53 | ($($tt:tt)+) => { 54 | check!(f64, $($tt)+) 55 | } 56 | } 57 | macro_rules! check_f32_lex { 58 | ($s:expr) => { 59 | check_lex!(f32, $s) 60 | }; 61 | } 62 | macro_rules! check_f64_lex { 63 | ($s:expr) => { 64 | check_lex!(f64, $s) 65 | }; 66 | } 67 | macro_rules! check_f32_inf { 68 | ($s:expr) => { 69 | check!(f32, $s, inf) 70 | }; 71 | } 72 | macro_rules! check_f32_neg_inf { 73 | ($s:expr) => { 74 | check!(f32, $s, neg_inf) 75 | }; 76 | } 77 | macro_rules! check_f64_inf { 78 | ($s:expr) => { 79 | check!(f64, $s, inf) 80 | }; 81 | } 82 | macro_rules! check_f64_neg_inf { 83 | ($s:expr) => { 84 | check!(f64, $s, neg_inf) 85 | }; 86 | } 87 | 88 | fn append_zeros(s: impl AsRef, n: usize) -> String { 89 | let mut s = String::from(s.as_ref()); 90 | for _ in 0..n { 91 | s.push('0'); 92 | } 93 | s 94 | } 95 | 96 | #[test] 97 | fn test_f64_inf() { 98 | check_f64_inf!("INF"); 99 | check_f64_inf!("INFINITY"); 100 | check_f64_inf!("infinity"); 101 | check_f64_inf!("inf"); 102 | check_f64_inf!("1234456789012345678901234567890e9999999999999999999999999999"); 103 | check_f64_inf!("1.832312213213213232132132143451234453123412321321312e308"); 104 | check_f64_inf!("2e30000000000000000"); 105 | check_f64_inf!("2e3000"); 106 | check_f64_inf!("1.8e308"); 107 | check_f64_inf!("1.9e308"); 108 | 109 | check_f64_neg_inf!("-INF"); 110 | check_f64_neg_inf!("-INFINITY"); 111 | check_f64_neg_inf!("-infinity"); 112 | check_f64_neg_inf!("-inf"); 113 | check_f64_neg_inf!("-2139879401095466344511101915470454744.9813888656856943E+272"); 114 | } 115 | 116 | #[test] 117 | fn test_f64_long() { 118 | check_f64!( 119 | "\ 120 | 9355950000000000000.000000000000000000000000000000000018446744073709551616000001\ 121 | 84467440737095516161844674407370955161407370955161618446744073709551616000184467\ 122 | 44073709551616600000184467440737095516161844674407370955161407370955161618446744\ 123 | 07370955161600018446744073709551616018446744073709556744516161844674407370955161\ 124 | 40737095516161844674407370955161600018446744073709551616018446744073709551611616\ 125 | 00018446744073709500184467440737095516160018446744073709551616001844674407370955\ 126 | 11681644674407370955161600018440737095516160184467440737095516161844674407370955\ 127 | 16160001844674407536910751601611616000184467440737095001844674407370955161600184\ 128 | 46744073709551616001844674407370955161618446744073709551616000184495516161844674\ 129 | 4073709551616000184467440753691075160018446744073709", 130 | hexf64("0x1.03ae05e8fca1cp+63") 131 | ); 132 | check_f64!( 133 | "\ 134 | 2.225073858507202124188701479202220329072405282794390378143031338374351073192441\ 135 | 94686754406432563881851382188218502438069999947733013005649884107791928741341929\ 136 | 29720097048195199306799329096904278406473168204156592672863293363047467012331685\ 137 | 29834221527445172608358596545663192828352447877877998943107797838336991592885945\ 138 | 55213714181128458251145584319223079897504395086859412457230891738946169368372321\ 139 | 19137365897797772328669884035639025104444303545739673370658398105542045669382465\ 140 | 84137476071559811765738776267476659123871999319040063173347090030127901881752034\ 141 | 47190250028061277777916798391090578584006464715943810511489154282775041174682194\ 142 | 13395246668250343130618158782937900420539237507208336669324158000275839111885418\ 143 | 8641513168478436313080237596295773983001708984375e-308", 144 | hexf64("0x1.0000000000002p-1022") 145 | ); 146 | check_f64_inf!( 147 | "\ 148 | 14384566631413902735261182076422355811832278452463312311626366537903681520913941\ 149 | 96930365828634687637948157940776599182791387527135353034738357134110310609455693\ 150 | 90082419354977279201654318268051974058035436546798544018359870131225762454556233\ 151 | 13970183299286131961255902741877200739148180625308303165331580986249841188892982\ 152 | 81371812288789537310599037529113415438738954894752124724983067241108764488346454\ 153 | 37669901867307840475112141480493722424080599312381693232622368309077056159757045\ 154 | 77939329858261626042558845291341263962822021265262533893834218067279545885255961\ 155 | 14379801269094096329805054803089299736996870951258573010877404407451953846698609\ 156 | 19821392688269207855703322826525930548119852605981316446918758669325733577952202\ 157 | 04076454986842633399219052275566166981299674128912822316855046606712779271982900\ 158 | 09824680186319750978665734576683784255802269708917361719466043175201158849097881\ 159 | 37047711185017157986905601606166617302905958843377601564443970505037755427769614\ 160 | 39282780934537928038462527159660167332226464423828921239400524413468224297215938\ 161 | 84378212558701004356924243030059517489346646577724622498919752597382095222500311\ 162 | 12418182351225107135618176937657765139002829779615620881537508915912839494571051\ 163 | 58613344862671017974971111259092725051947928708896171797587034426080161433432621\ 164 | 59998149700606597792535574457560429226974273443630323818747730771316763398572110\ 165 | 87495998192373246307688452867739265415001026982223940199342748237651323138921235\ 166 | 35835735663769155726509168665536123661873789595549835667127670933729060301889762\ 167 | 20169058025354973622211666504549316958271880975697143546564469806791358707318873\ 168 | 07570838334500409015197406832583817753126695417740666139222980134999469594150993\ 169 | 5655355652985723782153570084089560139142231.738475042362596875449154552392299548\ 170 | 94713816208169416867534067784380761312978044932336375902701297246698737092181681\ 171 | 31626587547265451210905455072402670004565947865409496052607224619378706306348749\ 172 | 91729398208026467698131898691830012167897399682179601734569071423681e-733" 173 | ); 174 | check_f64_lex!( 175 | "\ 176 | 0.000000000000000000000000000000000000000000000000000000000000000000000000000000\ 177 | 00000000000000000000000000000000000000000000000000000000000000000000000000000000\ 178 | 00000000000000000000000000000000000000000000000000000000000000000000000000000000\ 179 | 00000000000000000000000000000000000000000000000000000000000000000000044501477170\ 180 | 14402272114819593418263951869639092703291296046852219449644444042153891033059047\ 181 | 81627017582829831782607924221374017287738918929105531441481564124348675997628212\ 182 | 65346585071045737627442980259622449029037796981144446145705102663115100318287949\ 183 | 52795966823603998647925096578034214163701381261333311989876551545144031526125381\ 184 | 32666529513060001849177663286607555958373922409899478075565940981010216121988146\ 185 | 05258742579179000071675999344145086087205681577915435923018910334964869420614052\ 186 | 18289243144579760516365090360651414037721744226256159024466852576737244643007551\ 187 | 33324500796506867194913776884780053099639677097589658441378944337966219939673169\ 188 | 36280457084866613206797017728916080020698679408551343728867675409720757232455434\ 189 | 770912461317493580281734466552734375" 190 | ); 191 | check_f64_lex!( 192 | "\ 193 | 0.000000000000000000000000000000000000000000000000000000000000000000000000000000\ 194 | 00000000000000000000000000000000000000000000000000000000000000000000000000000000\ 195 | 00000000000000000000000000000000000000000000000000000000000000000000000000000000\ 196 | 00000000000000000000000000000000000000000000000000000000000000000000022250738585\ 197 | 07200889024586876085859887650423112240959465493524802562440009228235695178775888\ 198 | 80375915526423097809504343120858773871583572918219930202943792242235598198275012\ 199 | 42041788969571311791082261043971979604000454897391938079198936081525613113376149\ 200 | 84204327175103362739154978273159414382813627511383860409424946494228631669542910\ 201 | 50802018159266421349966065178030950759130587198464239060686371020051087232827846\ 202 | 78843631944515866135041223479014792369585208321597621066375401613736583044193603\ 203 | 71477835530668283453563400507407304013560296804637591858316312422452159926254649\ 204 | 43008368518617194224176464551371354201322170313704965832101546540680353974179060\ 205 | 22589503023501937519773030945763173210852507299305089761582519159720757232455434\ 206 | 770912461317493580281734466552734375" 207 | ); 208 | } 209 | 210 | #[test] 211 | fn test_f64_general() { 212 | check_f64!("9007199254740993.0", hexf64("0x1.p+53")); 213 | check_f64!(append_zeros("9007199254740993.0", 1000), hexf64("0x1.p+53")); 214 | check_f64!("10000000000000000000", hexf64("0x1.158e460913dp+63")); 215 | check_f64!( 216 | "10000000000000000000000000000001000000000000", 217 | hexf64("0x1.cb2d6f618c879p+142") 218 | ); 219 | check_f64!( 220 | "10000000000000000000000000000000000000000001", 221 | hexf64("0x1.cb2d6f618c879p+142") 222 | ); 223 | check_f64!(1.1920928955078125e-07); 224 | check_f64!("-0", -0.0); 225 | check_f64!( 226 | "1.0000000000000006661338147750939242541790008544921875", 227 | 1.0000000000000007 228 | ); 229 | check_f64!( 230 | "1090544144181609348835077142190", 231 | hexf64("0x1.b8779f2474dfbp+99") 232 | ); 233 | check_f64!(2.2250738585072013e-308); 234 | check_f64!(-92666518056446206563E3); 235 | check_f64!(-92666518056446206563E3); 236 | check_f64!(-42823146028335318693e-128); 237 | check_f64!(90054602635948575728E72); 238 | check_f64_lex!( 239 | "\ 240 | 1.000000000000001885589208702234638701745660206917535153946435506630705583683732\ 241 | 21972569761144603605635692374830246134201063722058e-309" 242 | ); 243 | check_f64!("0e9999999999999999999999999999", 0.0); 244 | check_f64!(-2402844368454405395.2); 245 | check_f64!(2402844368454405395.2); 246 | check_f64!(7.0420557077594588669468784357561207962098443483187940792729600000e+59); 247 | check_f64!(7.0420557077594588669468784357561207962098443483187940792729600000e+59); 248 | check_f64!(-1.7339253062092163730578609458683877051596800000000000000000000000e+42); 249 | check_f64!(-2.0972622234386619214559824785284023792871122537545728000000000000e+52); 250 | check_f64!(-1.0001803374372191849407179462120053338028379051879898808320000000e+57); 251 | check_f64!(-1.8607245283054342363818436991534856973992070520151142825984000000e+58); 252 | check_f64!(-1.9189205311132686907264385602245237137907390376574976000000000000e+52); 253 | check_f64!(-2.8184483231688951563253238886553506793085187889855201280000000000e+54); 254 | check_f64!(-1.7664960224650106892054063261344555646357024359107788800000000000e+53); 255 | check_f64!(-2.1470977154320536489471030463761883783915110400000000000000000000e+45); 256 | check_f64!(-4.4900312744003159009338275160799498340862630046359789166919680000e+61); 257 | check_f64!("+1", 1.0); 258 | check_f64!( 259 | "1.797693134862315700000000000000001e308", 260 | 1.7976931348623157e308 261 | ); 262 | check_f64!("3e-324", hexf64("0x0.0000000000001p-1022")); 263 | check_f64!("1.00000006e+09", hexf64("0x1.dcd651ep+29")); 264 | check_f64!("4.9406564584124653e-324", hexf64("0x0.0000000000001p-1022")); 265 | check_f64!("4.9406564584124654e-324", hexf64("0x0.0000000000001p-1022")); 266 | check_f64!("2.2250738585072009e-308", hexf64("0x0.fffffffffffffp-1022")); 267 | check_f64!("2.2250738585072014e-308", hexf64("0x1.p-1022")); 268 | check_f64!("1.7976931348623157e308", hexf64("0x1.fffffffffffffp+1023")); 269 | check_f64!("1.7976931348623158e308", hexf64("0x1.fffffffffffffp+1023")); 270 | check_f64!(4503599627370496.5); 271 | check_f64!(4503599627475352.5); 272 | check_f64!(4503599627475353.5); 273 | check_f64!(2251799813685248.25); 274 | check_f64!(1125899906842624.125); 275 | check_f64!(1125899906842901.875); 276 | check_f64!(2251799813685803.75); 277 | check_f64!(4503599627370497.5); 278 | check_f64!(45035996.273704995); 279 | check_f64!(45035996.273704985); 280 | check_f64!(1.2345e30); 281 | } 282 | 283 | #[test] 284 | fn test_f32_inf() { 285 | check_f32_inf!("INF"); 286 | check_f32_inf!("INFINITY"); 287 | check_f32_inf!("infinity"); 288 | check_f32_inf!("inf"); 289 | check_f32_inf!("1234456789012345678901234567890e9999999999999999999999999999"); 290 | check_f32_inf!("2e3000"); 291 | check_f32_inf!("3.5028234666e38"); 292 | 293 | check_f32_neg_inf!("-INF"); 294 | check_f32_neg_inf!("-INFINITY"); 295 | check_f32_neg_inf!("-infinity"); 296 | check_f32_neg_inf!("-inf"); 297 | } 298 | 299 | #[test] 300 | fn test_f32_basic() { 301 | let f1 = "\ 302 | 1.175494140627517859246175898662808184331245864732796240031385942718174675986064\ 303 | 7699724722770042717456817626953125"; 304 | check_f32!(f1, hexf32("0x1.2ced3p+0")); 305 | check_f32!(format!("{}e-38", f1), hexf32("0x1.fffff8p-127")); 306 | check_f32!( 307 | format!("{}e-38", append_zeros(f1, 655)), 308 | hexf32("0x1.fffff8p-127") 309 | ); 310 | check_f32!( 311 | format!("{}e-38", append_zeros(f1, 656)), 312 | hexf32("0x1.fffff8p-127") 313 | ); 314 | check_f32!( 315 | format!("{}e-38", append_zeros(f1, 1000)), 316 | hexf32("0x1.fffff8p-127") 317 | ); 318 | check_f32!(1.00000006e+09); 319 | check_f32!(1.4012984643e-45); 320 | check_f32!(1.1754942107e-38); 321 | check_f32!(1.1754943508e-45); 322 | check_f32!("-0", -0.0); 323 | check_f32!("1090544144181609348835077142190", hexf32("0x1.b877ap+99")); 324 | check_f32!(1.1754943508e-38); 325 | check_f32!(30219.0830078125); 326 | check_f32!(16252921.5); 327 | check_f32!(5322519.25); 328 | check_f32!(3900245.875); 329 | check_f32!(1510988.3125); 330 | check_f32!(782262.28125); 331 | check_f32!(328381.484375); 332 | check_f32!(156782.0703125); 333 | check_f32!(85003.24609375); 334 | check_f32!(43827.048828125); 335 | check_f32!(17419.6494140625); 336 | check_f32!(15498.36376953125); 337 | check_f32!(6318.580322265625); 338 | check_f32!(2525.2840576171875); 339 | check_f32!(1370.9265747070312); 340 | check_f32!(936.3702087402344); 341 | check_f32!(411.88682556152344); 342 | check_f32!(206.50310516357422); 343 | check_f32!(124.16878890991211); 344 | check_f32!(50.811574935913086); 345 | check_f32!(17.486443519592285); 346 | check_f32!(13.91745138168335); 347 | check_f32!("7.5464513301849365", hexf32("0x1.e2f90ep+2")); 348 | check_f32!(2.687217116355896); 349 | check_f32!("1.1877630352973938", hexf32("0x1.30113ep+0")); 350 | check_f32!(0.7622503340244293); 351 | check_f32!("0.30531780421733856", hexf32("0x1.38a53ap-2")); 352 | check_f32!("0.21791061013936996", hexf32("0x1.be47eap-3")); 353 | check_f32!("0.09289376810193062", hexf32("0x1.7c7e2ep-4")); 354 | check_f32!(0.03706067614257336); 355 | check_f32!(0.028068351559340954); 356 | check_f32!("0.012114629615098238", hexf32("0x1.8cf8e2p-7")); 357 | check_f32!("0.004221370676532388", hexf32("0x1.14a6dap-8")); 358 | check_f32!(0.002153817447833717); 359 | check_f32!("0.0015924838953651488", hexf32("0x1.a175cap-10")); 360 | check_f32!(0.0008602388261351734); 361 | check_f32!("0.00036393293703440577", hexf32("0x1.7d9c82p-12")); 362 | check_f32!(0.00013746770127909258); 363 | check_f32!(16407.9462890625); 364 | check_f32!("1.1754947011469036e-38", hexf32("0x1.000006p-126")); 365 | check_f32!("7.0064923216240854e-46", hexf32("0x1.p-149")); 366 | check_f32!(8388614.5); 367 | check_f32!("0e9999999999999999999999999999", 0.); 368 | check_f32!( 369 | "4.7019774032891500318749461488889827112746622270883500860350068251e-38", 370 | 4.7019774032891500318749461488889827112746622270883500860350068251e-38 371 | ); 372 | check_f32_lex!( 373 | "\ 374 | 3.141592653589793238462643383279502884197169399375105820974944592307816406286208\ 375 | 9986280348253421170679" 376 | ); 377 | check_f32!( 378 | "2.3509887016445750159374730744444913556373311135441750430175034126e-38", 379 | 2.3509887016445750159374730744444913556373311135441750430175034126e-38 380 | ); 381 | check_f32!("+1", 1.); 382 | check_f32!("7.0060e-46", 0.); 383 | check_f32!("3.4028234664e38", hexf32("0x1.fffffep+127")); 384 | check_f32!("3.4028234665e38", hexf32("0x1.fffffep+127")); 385 | check_f32!("3.4028234666e38", hexf32("0x1.fffffep+127")); 386 | check_f32_lex!( 387 | "\ 388 | 0.000000000000000000000000000000000000011754943508222875079687365372222456778186\ 389 | 655567720875215087517062784172594547271728515625" 390 | ); 391 | check_f32_lex!( 392 | "\ 393 | 0.000000000000000000000000000000000000000000001401298464324817070923729583289916\ 394 | 13128026194187651577175706828388979108268586060148663818836212158203125" 395 | ); 396 | check_f32_lex!( 397 | "\ 398 | 0.000000000000000000000000000000000000023509885615147285834557659820715330266457\ 399 | 17985517980855365926236850006129930346077117064851336181163787841796875" 400 | ); 401 | check_f32_lex!( 402 | "\ 403 | 0.000000000000000000000000000000000000011754942106924410754870294448492873488270\ 404 | 52428745893333857174530571588870475618904265502351336181163787841796875" 405 | ); 406 | check_f32!(1.2345e15); 407 | } 408 | 409 | #[test] 410 | fn test_f64_pow10() { 411 | for i in -308..=308 { 412 | let s = format!("1e{}", i); 413 | let v = f64::from_str(&s).unwrap(); 414 | assert_eq!(fast_float::parse::(s).unwrap(), v); 415 | } 416 | } 417 | 418 | #[test] 419 | fn test_f32_pow10() { 420 | for i in -38..=38 { 421 | let s = format!("1e{}", i); 422 | let v = f32::from_str(&s).unwrap(); 423 | assert_eq!(fast_float::parse::(s).unwrap(), v); 424 | } 425 | } 426 | -------------------------------------------------------------------------------- /tests/test_exhaustive.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | #[ignore] 3 | fn test_f32_exhaustive_ryu() { 4 | let mut buf = ryu::Buffer::new(); 5 | for i in 0..0xFFFF_FFFF_u32 { 6 | let a: f32 = unsafe { core::mem::transmute(i) }; 7 | let s = buf.format(a); 8 | let b: f32 = fast_float::parse(s).unwrap(); 9 | assert!(a == b || (a.is_nan() && b.is_nan())); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/test_random.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | #[ignore] 3 | fn test_f64_random_from_u64() { 4 | const N_ITER: u64 = 1 << 32; 5 | 6 | let rng = fastrand::Rng::with_seed(0); 7 | let mut buf = ryu::Buffer::new(); 8 | for _ in 0..N_ITER { 9 | let i: u64 = rng.u64(0..0xFFFF_FFFF_FFFF_FFFF); 10 | let a: f64 = unsafe { core::mem::transmute(i) }; 11 | let s = buf.format(a); 12 | let b: f64 = fast_float::parse(s).unwrap(); 13 | assert!(a == b || (a.is_nan() && b.is_nan())); 14 | } 15 | } 16 | --------------------------------------------------------------------------------