Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "ddfc0f6bfcc0085ff982f567f41f24a5541a14ea"
},
"path_in_vcs": "lexical-parse-float"
}
@@ -0,0 +1,141 @@
# Code of Conduct
## When Something Happens
If you see a Code of Conduct violation, follow these steps:
1. Let the person know that what they did is not appropriate and ask them to stop and/or edit their message(s) or commits.
2. That person should immediately stop the behavior and correct the issue.
3. If this doesnt happen, or if you're uncomfortable speaking up, [contact the maintainers](#contacting-maintainers).
4. As soon as available, a maintainer will look into the issue, and take [further action (see below)](#further-enforcement), starting with a warning, then temporary block, then long-term repo or organization ban.
When reporting, please include any relevant details, links, screenshots, context, or other information that may be used to better understand and resolve the situation.
**The maintainer team will prioritize the well-being and comfort of the recipients of the violation over the comfort of the violator.** See [some examples below](#enforcement-examples).
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers of this project pledge to making participation in our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, technical preferences, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language.
- Being respectful of differing viewpoints and experiences.
- Gracefully accepting constructive feedback.
- Focusing on what is best for the community.
- Showing empathy and kindness towards other community members.
- Encouraging and raising up your peers in the project so you can all bask in hacks and glory.
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances, including when simulated online. The only exception to sexual topics is channels/spaces specifically for topics of sexual identity.
- Casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology.
- Making light of/making mocking comments about trigger warnings and content warnings.
- Trolling, insulting/derogatory comments, and personal or political attacks.
- Public or private harassment, deliberate intimidation, or threats.
- Publishing others' private information, such as a physical or electronic address, without explicit permission. This includes any sort of "outing" of any aspect of someone's identity without their consent.
- Publishing private screenshots or quotes of interactions in the context of this project without all quoted users' *explicit* consent.
- Publishing of private communication that doesn't have to do with reporting harrassment.
- Any of the above even when [presented as "ironic" or "joking"](https://en.wikipedia.org/wiki/Hipster_racism).
- Any attempt to present "reverse-ism" versions of the above as violations. Examples of reverse-isms are "reverse racism", "reverse sexism", "heterophobia", and "cisphobia".
- Unsolicited explanations under the assumption that someone doesn't already know it. Ask before you teach! Don't assume what people's knowledge gaps are.
- [Feigning or exaggerating surprise](https://www.recurse.com/manual#no-feigned-surprise) when someone admits to not knowing something.
- "[Well-actuallies](https://www.recurse.com/manual#no-well-actuallys)"
- Other conduct which could reasonably be considered inappropriate in a professional or community setting.
## Scope
This Code of Conduct applies both within spaces involving this project and in other spaces involving community members. This includes the repository, its Pull Requests and Issue tracker, private email communications in the context of the project, and any events where members of the project are participating, as well as adjacent communities and venues affecting the project's members.
Depending on the violation, the maintainers may decide that violations of this code of conduct that have happened outside of the scope of the community may deem an individual unwelcome, and take appropriate action to maintain the comfort and safety of its members.
### Other Community Standards
As a project on GitHub, this project is additionally covered by the [GitHub Community Guidelines](https://help.github.com/articles/github-community-guidelines/).
Enforcement of those guidelines after violations overlapping with the above are the responsibility of the entities, and enforcement may happen in any or all of the services/communities.
## Maintainer Enforcement Process
Once the maintainers get involved, they will follow a documented series of steps and do their best to preserve the well-being of project members. This section covers actual concrete steps.
### Contacting Maintainers
You may get in touch with the maintainer team through any of the following methods:
Through email:
ahuszagh@gmail.com (Alex Huszagh)
### Further Enforcement
If you've already followed the [initial enforcement steps](#maintainer-enforcement-process), these are the steps maintainers will take for further enforcement, as needed:
1. Repeat the request to stop.
2. If the person doubles down, they will have offending messages removed or edited by a maintainers given an official warning. The PR or Issue may be locked.
3. If the behavior continues or is repeated later, the person will be blocked from participating for 24 hours.
4. If the behavior continues or is repeated after the temporary block, a long-term (6-12mo) ban will be used.
On top of this, maintainers may remove any offending messages, images, contributions, etc, as they deem necessary.
Maintainers reserve full rights to skip any of these steps, at their discretion, if the violation is considered to be a serious and/or immediate threat to the health and well-being of members of the community. These include any threats, serious physical or verbal attacks, and other such behavior that would be completely unacceptable in any social setting that puts our members at risk.
Members expelled from events or venues with any sort of paid attendance will not be refunded.
### Who Watches the Watchers?
Maintainers and other leaders who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. These may include anything from removal from the maintainer team to a permanent ban from the community.
Additionally, as a project hosted on both GitHub and npm, [their own Codes of Conducts may be applied against maintainers of this project](#other-community-standards), externally of this project's procedures.
### Enforcement Examples
#### The Best Case
The vast majority of situations work out like this. This interaction is common, and generally positive.
> Alex: "Yeah I used X and it was really crazy!"
> Patt (not a maintainer): "Hey, could you not use that word? What about 'ridiculous' instead?"
> Alex: "oh sorry, sure." -> edits old comment to say "it was really confusing!"
#### The Maintainer Case
Sometimes, though, you need to get maintainers involved. Maintainers will do their best to resolve conflicts, but people who were harmed by something **will take priority**.
> Patt: "Honestly, sometimes I just really hate using $library and anyone who uses it probably sucks at their job."
> Alex: "Whoa there, could you dial it back a bit? There's a CoC thing about attacking folks' tech use like that."
> Patt: "I'm not attacking anyone, what's your problem?"
> Alex: "@maintainers hey uh. Can someone look at this issue? Patt is getting a bit aggro. I tried to nudge them about it, but nope."
> KeeperOfCommitBits: (on issue) "Hey Patt, maintainer here. Could you tone it down? This sort of attack is really not okay in this space."
> Patt: "Leave me alone I haven't said anything bad wtf is wrong with you."
> KeeperOfCommitBits: (deletes user's comment), "@patt I mean it. Please refer to the CoC over at (URL to this CoC) if you have questions, but you can consider this an actual warning. I'd appreciate it if you reworded your messages in this thread, since they made folks there uncomfortable. Let's try and be kind, yeah?"
> Patt: "@keeperofbits Okay sorry. I'm just frustrated and I'm kinda burnt out and I guess I got carried away. I'll DM Alex a note apologizing and edit my messages. Sorry for the trouble."
> KeeperOfCommitBits: "@patt Thanks for that. I hear you on the stress. Burnout sucks :/. Have a good one!"
#### The Nope Case
> PepeTheFrog🐸: "Hi, I am a literal actual nazi and I think white supremacists are quite fashionable."
> Patt: "NOOOOPE. OH NOPE NOPE."
> Alex: "JFC NO. NOPE. @keeperofbits NOPE NOPE LOOK HERE"
> KeeperOfCommitBits: "👀 Nope. NOPE NOPE NOPE. 🔥"
> PepeTheFrog🐸 has been banned from all organization or user repositories belonging to KeeperOfCommitBits.
## Attribution
This Code of Conduct was generated using [WeAllJS Code of Conduct Generator](https://npm.im/weallbehave), which is based on the [WeAllJS Code of Conduct](https://wealljs.org/code-of-conduct), which is itself based on
[Contributor Covenant](http://contributor-covenant.org), version 1.4, available at [http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4), and the LGBTQ in Technology Slack [Code of Conduct](http://lgbtq.technology/coc.html).
+69
View File
@@ -0,0 +1,69 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "float16"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eb1bdc9979a09caeacb8528039f7a957f8209234e2c21395dd4abd601cd739"
dependencies = [
"cfg-if",
"rustc_version",
]
[[package]]
name = "lexical-parse-float"
version = "1.0.6"
dependencies = [
"lexical-parse-integer",
"lexical-util",
]
[[package]]
name = "lexical-parse-integer"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34"
dependencies = [
"lexical-util",
]
[[package]]
name = "lexical-util"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17"
dependencies = [
"float16",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+181
View File
@@ -0,0 +1,181 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.60.0"
name = "lexical-parse-float"
version = "1.0.6"
authors = ["Alex Huszagh <ahuszagh@gmail.com>"]
build = false
exclude = [
"assets/*",
"docs/*",
"etc/*",
"cargo-timing*.html",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Efficient parsing of floats from strings."
readme = "README.md"
keywords = [
"parsing",
"lexical",
"no_std",
]
categories = [
"parsing",
"no-std",
]
license = "MIT/Apache-2.0"
repository = "https://github.com/Alexhuszagh/rust-lexical"
[package.metadata.docs.rs]
features = [
"radix",
"format",
"f16",
]
rustdoc-args = [
"--cfg",
"docsrs",
]
[features]
compact = [
"lexical-util/compact",
"lexical-parse-integer/compact",
]
default = ["std"]
f128 = ["lexical-util/f128"]
f16 = ["lexical-util/f16"]
format = [
"lexical-util/format",
"lexical-parse-integer/format",
]
lint = [
"lexical-util/lint",
"lexical-parse-integer/lint",
]
power-of-two = [
"lexical-util/power-of-two",
"lexical-parse-integer/power-of-two",
]
radix = [
"lexical-util/radix",
"lexical-parse-integer/radix",
"power-of-two",
]
std = [
"lexical-util/std",
"lexical-parse-integer/std",
]
[lib]
name = "lexical_parse_float"
path = "src/lib.rs"
[[test]]
name = "api_tests"
path = "tests/api_tests.rs"
[[test]]
name = "bellerophon"
path = "tests/bellerophon.rs"
[[test]]
name = "bellerophon_radix_tests"
path = "tests/bellerophon_radix_tests.rs"
[[test]]
name = "bellerophon_tests"
path = "tests/bellerophon_tests.rs"
[[test]]
name = "bigfloat_tests"
path = "tests/bigfloat_tests.rs"
[[test]]
name = "bigint_tests"
path = "tests/bigint_tests.rs"
[[test]]
name = "binary_tests"
path = "tests/binary_tests.rs"
[[test]]
name = "float_tests"
path = "tests/float_tests.rs"
[[test]]
name = "issue_96_tests"
path = "tests/issue_96_tests.rs"
[[test]]
name = "issue_98_tests"
path = "tests/issue_98_tests.rs"
[[test]]
name = "lemire_tests"
path = "tests/lemire_tests.rs"
[[test]]
name = "libm_tests"
path = "tests/libm_tests.rs"
[[test]]
name = "limits_tests"
path = "tests/limits_tests.rs"
[[test]]
name = "mask_tests"
path = "tests/mask_tests.rs"
[[test]]
name = "number_tests"
path = "tests/number_tests.rs"
[[test]]
name = "options_tests"
path = "tests/options_tests.rs"
[[test]]
name = "parse_tests"
path = "tests/parse_tests.rs"
[[test]]
name = "shared_tests"
path = "tests/shared_tests.rs"
[[test]]
name = "slow_tests"
path = "tests/slow_tests.rs"
[[test]]
name = "stackvec"
path = "tests/stackvec.rs"
[[test]]
name = "stackvec_tests"
path = "tests/stackvec_tests.rs"
[dependencies.lexical-parse-integer]
version = "1.0.6"
features = []
default-features = false
[dependencies.lexical-util]
version = "1.0.7"
features = ["parse-floats"]
default-features = false
+82
View File
@@ -0,0 +1,82 @@
[package]
authors = ["Alex Huszagh <ahuszagh@gmail.com>"]
autobenches = false
categories = ["parsing", "no-std"]
description = "Efficient parsing of floats from strings."
edition = "2021"
keywords = ["parsing", "lexical", "no_std"]
license = "MIT/Apache-2.0"
name = "lexical-parse-float"
readme = "README.md"
repository = "https://github.com/Alexhuszagh/rust-lexical"
version = "1.0.6"
rust-version = "1.60.0"
exclude = [
"assets/*",
"docs/*",
"etc/*",
"cargo-timing*.html"
]
[dependencies.lexical-util]
version = "1.0.7"
path = "../lexical-util"
default-features = false
features = ["parse-floats"]
[dependencies.lexical-parse-integer]
version = "1.0.6"
path = "../lexical-parse-integer"
default-features = false
features = []
[features]
default = ["std"]
# Use the standard library.
std = [
"lexical-util/std",
"lexical-parse-integer/std"
]
# Add support for parsing power-of-two float strings.
power-of-two = [
"lexical-util/power-of-two",
"lexical-parse-integer/power-of-two"
]
# Add support for parsing non-decimal float strings.
radix = [
"lexical-util/radix",
"lexical-parse-integer/radix",
"power-of-two"
]
# Add support for parsing custom float formats.
format = [
"lexical-util/format",
"lexical-parse-integer/format"
]
# Reduce code size at the cost of performance.
compact = [
"lexical-util/compact",
"lexical-parse-integer/compact"
]
# Enable support for 16-bit floats.
f16 = ["lexical-util/f16"]
# INTERNAL ONLY
# -------------
# Internal only features. These are not meant to be used directly.
# Enable the lint checks.
lint = [
"lexical-util/lint",
"lexical-parse-integer/lint"
]
# UNSUPPORTED
# -----------
# Currently unsupported features.
# Enable support for 128-bit floats. Unsupported and unlikely to ever be.
# https://github.com/Alexhuszagh/rust-lexical/issues/46
f128 = ["lexical-util/f128"]
[package.metadata.docs.rs]
features = ["radix", "format", "f16"]
rustdoc-args = ["--cfg", "docsrs"]
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+23
View File
@@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+337
View File
@@ -0,0 +1,337 @@
# Licensing
Lexical is dual licensed under the Apache 2.0 license as well as the MIT
license. See the LICENCE-MIT and the LICENCE-APACHE files for the licenses.
Other licensing terms may apply, as described in depth below for various features and functionality. All assume use of `lexical` or `lexical-core`.
## `write-floats, not(compact)`
`lexical-write-float/src/algorithm.rs` is a direct port of the reference C++ implementation of Dragonbox, found [here](https://github.com/jk-jeon/dragonbox/).
This code (used if the `write-floats` feature is enabled and the `compact` feature is disabled) is subject to a [Boost Software License](https://github.com/jk-jeon/dragonbox/blob/71993f55067a89f4b4e27591605e21521f5c61be/LICENSE-Boost) and a modified [Apache2 license](https://github.com/jk-jeon/dragonbox/blob/71993f55067a89f4b4e27591605e21521f5c61be/LICENSE-Apache2-LLVM), shown in the [Boost Software License](#boost-software-license) and [Apache2 With LLVM Exceptions](#apache2-with-llvm-exceptions) sections below.
## `write-floats, compact`
`lexical-write-float/src/compact.rs` is a direct port of a C++ implementation of the Grisu algorithm, found [here](https://github.com/night-shift/fpconv/).
This code (used if both the `write-floats` and `compact` features are enabled) is subject to a [MIT License](https://github.com/night-shift/fpconv/blob/dfeb7e938fb85fb5eca130b84f856705ced75012/license), shown in the [fpconv License](#fpconv-license) section below.
## `write-floats, radix`
`lexical-write-float/src/radix.rs` is adapted from the V8 implementation found [here](). This code (used if both the `parse-floats` and `radix` features are enabled) is subject to a [3-clause BSD license](https://github.com/v8/v8/blob/f80bfeaf0792652bfbc1f174d5a7b8ab8bc0cbbd/LICENSE.v8), shown in the [V8 License](#v8-license) section below.
## `parse-floats, compact`
`lexical-parse-float/src/bellerophon.rs` is loosely based off the Golang implementation,
found [here](https://github.com/golang/go/blob/b10849fbb97a2244c086991b4623ae9f32c212d0/src/strconv/extfloat.go). This code (used if both the `parse-floats` and `compact` features are enabled) is subject to a [3-clause BSD license](https://github.com/golang/go/blob/b10849fbb97a2244c086991b4623ae9f32c212d0/LICENSE), shown in the [Go License](#go-license) section below.
# License Terms
This contains complete copies of the licensing terms for the feature-dependent code described above.
## Go License
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## Boost Software License
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
## Apache2 With LLVM Exceptions
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### LLVM Exceptions to the Apache 2.0 License
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
## V8 License
Copyright 2014, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## fpconv License
The MIT License
Copyright (c) 2013 Andreas Samoljuk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+414
View File
@@ -0,0 +1,414 @@
# lexical
High-performance numeric conversion routines for use in a `no_std` environment. This does not depend on any standard library features, nor a system allocator. Comprehensive benchmarks can be found at [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks).
**Similar Projects**
If you want a minimal, performant float parser, recent versions of the Rust standard library should be [sufficient](https://github.com/rust-lang/rust/pull/86761). For high-performance integer formatters, look at [itoa](https://docs.rs/itoa/latest/itoa/). The [metrics](#metrics) section contains a detailed comparison of various crates and their performance in comparison to lexical. Lexical is the currently fastest Rust number formatter and parser, and is tested against:
- [itoa](https://crates.io/crates/itoa)
- [dtoa](https://crates.io/crates/dtoa)
- [ryu](https://crates.io/crates/ryu)
- Rust core library
**Table of Contents**
- [Getting Started](#getting-started)
- [Partial/Complete Parsers](#partialcomplete-parsers)
- [no_std](#no_std)
- [Features](#features)
- [Customization](#customization)
- [Number Format API](#number-format-api)
- [Options API](#options-api)
- [Documentation](#documentation)
- [Validation](#validation)
- [Metrics](#metrics)
- [Safety](#safety)
- [Platform Support](#platform-support)
- [Versioning and Version Support](#versioning-and-version-support)
- [Changelog](#changelog)
- [License](#license)
- [Contributing](#contributing)
## Getting Started
Add lexical to your `Cargo.toml`:
```toml
[dependencies]
lexical-core = "^1.0"
```
And get started using lexical:
```rust
// Number to string
use lexical_core::BUFFER_SIZE;
let mut buffer = [b'0'; BUFFER_SIZE];
lexical_core::write(3.0, &mut buffer); // "3.0", always has a fraction suffix,
lexical_core::write(3, &mut buffer); // "3"
// String to number.
let i: i32 = lexical_core::parse("3")?; // Ok(3), auto-type deduction.
let f: f32 = lexical_core::parse("3.5")?; // Ok(3.5)
let d: f64 = lexical_core::parse("3.5")?; // Ok(3.5), error checking parse.
let d: f64 = lexical_core::parse("3a")?; // Err(Error(_)), failed to parse.
```
In order to use lexical in generic code, the trait bounds `FromLexical` (for `parse`) and `ToLexical` (for `to_string`) are provided.
```rust
/// Multiply a value in a string by multiplier, and serialize to string.
fn mul_2<T>(value: &str, multiplier: T)
-> Result<String, lexical_core::Error>
where
T: lexical_core::ToLexical + lexical_core::FromLexical,
{
let value: T = lexical_core::parse(value.as_bytes())?;
let mut buffer = [b'0'; lexical_core::BUFFER_SIZE];
let bytes = lexical_core::write(value * multiplier, &mut buffer);
Ok(String::from_utf8(bytes).unwrap())
}
```
## Partial/Complete Parsers
Lexical has both partial and complete parsers: the complete parsers ensure the entire buffer is used while parsing, without ignoring trailing characters, while the partial parsers parse as many characters as possible, returning both the parsed value and the number of parsed digits. Upon encountering an error, lexical will return an error indicating both the error type and the index at which the error occurred inside the buffer.
**Complete Parsers**
```rust
// This will return Err(Error::InvalidDigit(3)), indicating
// the first invalid character occurred at the index 3 in the input
// string (the space character).
let x: i32 = lexical_core::parse(b"123 456")?;
```
**Partial Parsers**
```rust
// This will return Ok((123, 3)), indicating that 3 digits were successfully
// parsed, and that the returned value is `123`.
let (x, count): (i32, usize) = lexical_core::parse_partial(b"123 456")?;
```
## no_std
`lexical-core` does not depend on a standard library, nor a system allocator. To use `lexical-core` in a [`no_std`] environment, add the following to `Cargo.toml`:
[`no_std`]: <https://docs.rust-embedded.org/book/intro/no-std.html>
```toml
[dependencies.lexical-core]
version = "1.0.0"
default-features = false
# Can select only desired parsing/writing features.
features = ["write-integers", "write-floats", "parse-integers", "parse-floats"]
```
And get started using `lexical-core`:
```rust
// A constant for the maximum number of bytes a formatter will write.
use lexical_core::BUFFER_SIZE;
let mut buffer = [b'0'; BUFFER_SIZE];
// Number to string. The underlying buffer must be a slice of bytes.
let count = lexical_core::write(3.0, &mut buffer);
assert_eq!(buffer[..count], b"3.0");
let count = lexical_core::write(3i32, &mut buffer);
assert_eq!(buffer[..count], b"3");
// String to number. The input must be a slice of bytes.
let i: i32 = lexical_core::parse(b"3")?; // Ok(3), auto-type deduction.
let f: f32 = lexical_core::parse(b"3.5")?; // Ok(3.5)
let d: f64 = lexical_core::parse(b"3.5")?; // Ok(3.5), error checking parse.
let d: f64 = lexical_core::parse(b"3a")?; // Err(Error(_)), failed to parse.
```
## Features
Lexical feature-gates each numeric conversion routine, resulting in faster compile times if certain numeric conversions. These features can be enabled/disabled for both `lexical-core` (which does not require a system allocator) and `lexical`. By default, all conversions are enabled.
- **parse-floats**: &ensp; Enable string-to-float conversions.
- **parse-integers**: &ensp; Enable string-to-integer conversions.
- **write-floats**: &ensp; Enable float-to-string conversions.
- **write-integers**: &ensp; Enable integer-to-string conversions.
Lexical is highly customizable, and contains numerous other optional features:
- **std**: &ensp; Enable use of the Rust standard library (enabled by default).
- **power-of-two**: &ensp; Enable conversions to and from non-decimal strings.
<blockquote>With power_of_two enabled, the radixes <code>{2, 4, 8, 10, 16, and 32}</code> are valid, otherwise, only <code>10</code> is valid. This enables common conversions to/from hexadecimal integers/floats, without requiring large pre-computed tables for other radixes.</blockquote>
- **radix**: &ensp; Allow conversions to and from non-decimal strings.
<blockquote>With radix enabled, any radix from <code>2</code> to <code>36</code> (inclusive) is valid, otherwise, only <code>10</code> is valid.</blockquote>
- **format**: &ensp; Customize acceptable number formats for number parsing and writing.
<blockquote>With format enabled, the number format is dictated through bitflags and masks packed into a <code>u128</code>. These dictate the valid syntax of parsed and written numbers, including enabling digit separators, requiring integer or fraction digits, and toggling case-sensitive exponent characters.</blockquote>
- **compact**: &ensp; Optimize for binary size at the expense of performance.
<blockquote>This minimizes the use of pre-computed tables, producing significantly smaller binaries.</blockquote>
- **f16**: &ensp; Add support for numeric conversions to-and-from 16-bit floats.
<blockquote>Adds <code>f16</code>, a half-precision IEEE-754 floating-point type, and <code>bf16</code>, the Brain Float 16 type, and numeric conversions to-and-from these floats. Note that since these are storage formats, and therefore do not have native arithmetic operations, all conversions are done using an intermediate <code>f32</code>.</blockquote>
To ensure memory safety, we extensively fuzz the all numeric conversion routines. See the [Safety](#safety) section below for more information.
Lexical also places a heavy focus on code bloat: with algorithms both optimized for performance and size. By default, this focuses on performance, however, using the `compact` feature, you can also opt-in to reduced code size at the cost of performance. The compact algorithms minimize the use of pre-computed tables and other optimizations at a major cost to performance.
## Customization
Lexical is extensively customizable to support parsing numbers from a wide variety of programming languages, such as `1_2_3`. However, lexical takes the concept of "you don't pay for what you don't use" seriously: enabling the `format` feature does not affect the performance of parsing regular numbers: only those with digit separators.
> ⚠ **WARNING:** When changing the number of significant digits written, disabling the use of exponent notation, or changing exponent notation thresholds, `BUFFER_SIZE` may be insufficient to hold the resulting output. `WriteOptions::buffer_size_const` will provide a correct upper bound on the number of bytes written. If a buffer of insufficient length is provided, `lexical-core` will panic.
Every language has competing specifications for valid numerical input, meaning a number parser for Rust will incorrectly accept or reject input for different programming or data languages. For example:
```rust
// Valid in Rust strings.
// Not valid in JSON.
let f: f64 = lexical_core::parse(b"3.e7")?; // 3e7
// Let's only accept JSON floats.
const JSON: u128 = lexical_core::format::JSON;
const OPTIONS: ParseFloatOptions = ParseFloatOptions::new();
let f: f64 = lexical_core::parse_with_options::<_, JSON>(b"3.0e7", &OPTIONS)?; // 3e7
let f: f64 = lexical_core::parse_with_options::<_, JSON>(b"3.e7", &OPTIONS)?; // Errors!
```
Due the high variability in the syntax of numbers in different programming and data languages, we provide 2 different APIs to simplify converting numbers with different syntax requirements.
- Number Format API (feature-gated via `format` or `power-of-two`).
<blockquote>This is a packed struct contained flags to specify compile-time syntax rules for number parsing or writing. This includes features such as the radix of the numeric string, digit separators, case-sensitive exponent characters, optional base prefixes/suffixes, and more.</blockquote>
- Options API.
<blockquote>This contains run-time rules for parsing and writing numbers. This includes exponent break points, rounding modes, the exponent and decimal point characters, and the string representation of NaN and Infinity.</blockquote>
A limited subset of functionality is documented in examples below, however, the complete specification can be found in the API reference documentation ([parse-float](https://docs.rs/lexical-parse-float/latest/lexical_parse_float/struct.Options.html), [parse-integer](https://docs.rs/lexical-parse-integer/latest/lexical_parse_integer/struct.Options.html), and [write-float](https://docs.rs/lexical-write-float/latest/lexical_write_float/struct.Options.html)).
### Number Format API
The number format class provides numerous flags to specify number syntax when parsing or writing. When the `power-of-two` feature is enabled, additional flags are added:
- The radix for the significant digits (default `10`).
- The radix for the exponent base (default `10`).
- The radix for the exponent digits (default `10`).
When the `format` feature is enabled, numerous other syntax and digit separator flags are enabled, including:
- A digit separator character, to group digits for increased legibility.
- Whether leading, trailing, internal, and consecutive digit separators are allowed.
- Toggling required float components, such as digits before the decimal point.
- Toggling whether special floats are allowed or are case-sensitive.
Many pre-defined constants therefore exist to simplify common use-cases,
including:
- [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more.
- [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings, and many more.
[`JSON`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.JSON.html
[`XML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.XML.html
[`TOML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.TOML.html
[`YAML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.YAML.html
[`SQLite`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.SQLITE.html
[`Rust`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.RUST_LITERAL.html
[`Python`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.PYTHON_LITERAL.html
[`C#`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.CSHARP_LITERAL.html
[`FORTRAN`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.FORTRAN_LITERAL.html
[`COBOL`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.COBOL_LITERAL.html
An example of building a custom number format is as follows:
```rust
// this will panic if the format is invalid
const FORMAT: u128 = lexical_core::NumberFormatBuilder::new()
// Disable exponent notation.
.no_exponent_notation(true)
// Disable all special numbers, such as Nan and Inf.
.no_special(true)
.build_strict();
```
### Options API
The options API allows customizing number parsing and writing at run-time, such as specifying the maximum number of significant digits, exponent characters, and more.
An example of building a custom options struct is as follows:
```rust
use std::num;
const OPTIONS: lexical_core::WriteFloatOptions = lexical_core::WriteFloatOptions::builder()
// Only write up to 5 significant digits, IE, `1.23456` becomes `1.2345`.
.max_significant_digits(num::NonZeroUsize::new(5))
// Never write less than 5 significant digits, `1.1` becomes `1.1000`.
.min_significant_digits(num::NonZeroUsize::new(5))
// Trim the trailing `.0` from integral float strings.
.trim_floats(true)
// Use a European-style decimal point.
.decimal_point(b',')
// Panic if we try to write NaN as a string.
.nan_string(None)
// Write infinity as "Infinity".
.inf_string(Some(b"Infinity"))
.build_strict();
```
## Documentation
Lexical's API reference can be found on [docs.rs](https://docs.rs/lexical), as can [lexical-core's](lexical-core). Detailed descriptions of the algorithms used can be found here:
- [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md)
- [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md)
- [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md)
- [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md)
In addition, descriptions of how lexical handles [digit separators](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/DigitSeparators.md) and implements [big-integer arithmetic](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/BigInteger.md) are also documented.
## Validation
**Float-Parsing**
Float parsing is difficult to do correctly, and major bugs have been found in implementations from [libstdc++'s strtod](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) to [Python](https://bugs.python.org/issue7632). In order to validate the accuracy of the lexical, we employ the following external tests:
1. Hrvoje Abraham's [strtod](https://github.com/ahrvoje/numerics/tree/master/strtod) test cases.
2. Rust's [test-float-parse](https://github.com/rust-lang/rust/tree/64185f205dcbd8db255ad6674e43c63423f2369a/src/etc/test-float-parse) unittests.
3. Testbase's [stress tests](https://www.icir.org/vern/papers/testbase-report.pdf) for converting from decimal to binary.
4. Nigel Tao's [tests](https://github.com/nigeltao/parse-number-fxx-test-data) extracted from test suites for Freetype, Google's double-conversion library, IBM's IEEE-754R compliance test, as well as numerous other curated examples.
5. [Various](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) [difficult](https://www.exploringbinary.com/how-glibc-strtod-works/) [cases](https://www.exploringbinary.com/how-strtod-works-and-sometimes-doesnt/) reported on blogs.
Lexical is extensively used in production, the same float parsing algorithm has been adopted by Golang's and Rust's standard libraries, and is unlikely to have correctness issues.
## Metrics
Various benchmarks, binary sizes, and compile times are shown here. All the benchmarks can be found on [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks?tab=readme-ov-file#latest-results). All benchmarks used a black box to avoid optimizing out the result and leading to misleading metrics.
**Build Timings**
The compile-times when building with all numeric conversions enabled. For a more fine-tuned breakdown, see [build timings](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BuildTimings.md).
![Build Timings](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/timings_all_posix.svg)
**Binary Size**
The binary sizes of stripped binaries compiled at optimization level "2". For a more fine-tuned breakdown, see [binary sizes](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BinarySize.md).
![Parse Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_parse_stripped_opt2_posix.svg)
![Write Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_write_stripped_opt2_posix.svg)
### Benchmarks — Parse Integer
**Random**
A benchmark on randomly-generated integers uniformly distributed over the entire range.
![Uniform Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_random%20-%20parse%20int%20-%20core,lexical.png)
**Simple**
A benchmark on randomly-generated integers from 1-1000.
![Simple Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_simple%20-%20parse%20int%20-%20core,lexical.png)
### Benchmarks — Parse Float
**Real-World Datasets**
A benchmark on parsing floats from various real-world data sets, including Canada, Mesh, and astronomical data (earth).
![Canada](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/canada%20-%20parse%20float%20-%20core,lexical.png)
![Earth](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/earth%20-%20parse%20float%20-%20core,lexical.png)
![Mesh](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/mesh%20-%20parse%20float%20-%20core,lexical.png)
**Random**
A benchmark on randomly-generated integers uniformly distributed over the entire range.
![Random Big Integer](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_big_ints%20-%20parse%20float%20-%20core,lexical.png)
**Simple**
A benchmark on randomly-generated integers from 1-1000.
![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_simple_int64%20-%20parse%20float%20-%20core,lexical.png)
### Benchmarks — Write Integer
**Random**
A benchmark on randomly-generated integers uniformly distributed over the entire range.
![Random Uniform](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_chain_random%20-%20write%20int%20-%20fmt,itoa,lexical.png)
**Simple**
![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_simple%20-%20write%20int%20-%20fmt,itoa,lexical.png)
**Large**
![Random Large](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_large%20-%20write%20int%20-%20fmt,itoa,lexical.png)
### Benchmarks — Write Float
**Big Integer**
A benchmarks for values with a large integers.
![Big Integers](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_big_ints%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png)
**Simple 64-Bit Inteers**
![Simple Int64](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_simple_int64%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png)
**Random**
![Random](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png)
## Safety
Due to the use of memory unsafe code in the library, we extensively fuzz our float writers and parsers. The fuzz harnesses may be found under [fuzz](https://github.com/Alexhuszagh/rust-lexical/tree/main/fuzz), and are run continuously. So far, we've parsed and written over 72 billion floats.
## Platform Support
lexical-core is tested on a wide variety of platforms, including big and small-endian systems, to ensure portable code. Supported architectures include:
- x86_64 Linux, Windows, macOS, Android, iOS, FreeBSD, and NetBSD.
- x86 Linux, macOS, Android, iOS, and FreeBSD.
- aarch64 (ARM8v8-A) Linux, Android, and iOS.
- armv7 (ARMv7-A) Linux, Android, and iOS.
- arm (ARMv6) Linux, and Android.
- powerpc (PowerPC) Linux.
- powerpc64 (PPC64) Linux.
- powerpc64le (PPC64LE) Linux.
- s390x (IBM Z) Linux.
lexical-core should also work on a wide variety of other architectures and ISAs. If you have any issue compiling lexical-core on any architecture, please file a bug report.
## Versioning and Version Support
**Version Support**
The currently supported versions are:
- v1.0.x
Due to security considerations, all other versions are not supported and security advisories exist for them.
**Rustc Compatibility**
- v1.0.x supports 1.63+, including stable, beta, and nightly.
Please report any errors compiling a supported `lexical` version on a compatible Rustc version.
**Versioning**
`lexical` uses [semantic versioning](https://semver.org/). Removing support for Rustc versions newer than the latest stable Debian or Ubuntu version is considered an incompatible API change, requiring a major version change.
## Changelog
All changes are documented in [CHANGELOG](https://github.com/Alexhuszagh/rust-lexical/blob/main/CHANGELOG).
## License
Lexical is dual licensed under the Apache 2.0 license as well as the MIT license. See the [LICENSE.md](LICENSE.md) file for full license details.
## Contributing
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in `lexical` by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. Contributing to the repository means abiding by the [code of conduct](https://github.com/Alexhuszagh/rust-lexical/blob/main/CODE_OF_CONDUCT.md).
For the process on how to contribute to `lexical`, see the [development](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/Development.md) quick-start guide.
+79
View File
@@ -0,0 +1,79 @@
////! Implements the algorithm in terms of the lexical API.
#![doc(hidden)]
#[cfg(feature = "f16")]
use lexical_util::bf16::bf16;
use lexical_util::error::Error;
#[cfg(feature = "f16")]
use lexical_util::f16::f16;
use lexical_util::format::{is_valid_options_punctuation, NumberFormat, STANDARD};
use lexical_util::{from_lexical, from_lexical_with_options};
use crate::options::Options;
use crate::parse::ParseFloat;
// API
const DEFAULT_OPTIONS: Options = Options::new();
/// Implement `FromLexical` for numeric type.
///
/// Need to inline these, otherwise code generation is sub-optimal.
/// For some reason, it can't determine some of the const evaluations
/// can actually be evaluated at compile-time, which causes major branching
/// issues.
macro_rules! float_from_lexical {
($($t:ident)*) => ($(
impl FromLexical for $t {
#[cfg_attr(not(feature = "compact"), inline)]
fn from_lexical(bytes: &[u8]) -> lexical_util::result::Result<Self>
{
Self::parse_complete::<STANDARD>(bytes, &DEFAULT_OPTIONS)
}
#[cfg_attr(not(feature = "compact"), inline)]
fn from_lexical_partial(
bytes: &[u8],
) -> lexical_util::result::Result<(Self, usize)>
{
Self::parse_partial::<STANDARD>(bytes, &DEFAULT_OPTIONS)
}
}
impl FromLexicalWithOptions for $t {
type Options = Options;
#[cfg_attr(not(feature = "compact"), inline)]
fn from_lexical_with_options<const FORMAT: u128>(
bytes: &[u8],
options: &Self::Options,
) -> lexical_util::result::Result<Self>
{
let format = NumberFormat::<{ FORMAT }> {};
if !format.is_valid() {
return Err(format.error());
} else if !is_valid_options_punctuation(FORMAT, options.exponent(), options.decimal_point()) {
return Err(Error::InvalidPunctuation);
}
Self::parse_complete::<FORMAT>(bytes, options)
}
#[cfg_attr(not(feature = "compact"), inline)]
fn from_lexical_partial_with_options<const FORMAT: u128>(
bytes: &[u8],
options: &Self::Options,
) -> lexical_util::result::Result<(Self, usize)>
{
Self::parse_partial::<FORMAT>(bytes, options)
}
}
)*)
}
from_lexical!("lexical_parse_float", 1.234, f64, 5);
from_lexical_with_options!("lexical_parse_float", 1.234, f64, 5, Options);
float_from_lexical! { f32 f64 }
#[cfg(feature = "f16")]
float_from_lexical! { bf16 f16 }
@@ -0,0 +1,406 @@
//! An implementation of Clinger's Bellerophon algorithm.
//!
//! This is a moderate path algorithm that uses an extended-precision
//! float, represented in 80 bits, by calculating the bits of slop
//! and determining if those bits could prevent unambiguous rounding.
//!
//! This algorithm requires less static storage than the Lemire algorithm,
//! and has decent performance, and is therefore used when non-decimal,
//! non-power-of-two strings need to be parsed. Clinger's algorithm
//! is described in depth in "How to Read Floating Point Numbers Accurately.",
//! available online [here](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.45.4152&rep=rep1&type=pdf).
//!
//! This implementation is loosely based off the Golang implementation,
//! found [here](https://github.com/golang/go/blob/b10849fbb97a2244c086991b4623ae9f32c212d0/src/strconv/extfloat.go).
//! This code is therefore subject to a 3-clause BSD license.
#![cfg(any(feature = "compact", feature = "radix"))]
#![doc(hidden)]
use lexical_util::format::NumberFormat;
use crate::float::{ExtendedFloat80, RawFloat};
use crate::mask::{lower_n_halfway, lower_n_mask};
use crate::number::Number;
use crate::shared;
use crate::table::bellerophon_powers;
// ALGORITHM
// ---------
/// Core implementation of the Bellerophon algorithm.
///
/// Create an extended-precision float, scale it to the proper radix power,
/// calculate the bits of slop, and return the representation. The value
/// will always be guaranteed to be within 1 bit, rounded-down, of the real
/// value. If a negative exponent is returned, this represents we were
/// unable to unambiguously round the significant digits.
///
/// This has been modified to return a biased, rather than unbiased exponent.
pub fn bellerophon<F: RawFloat, const FORMAT: u128>(num: &Number, lossy: bool) -> ExtendedFloat80 {
let format = NumberFormat::<{ FORMAT }> {};
debug_assert!(
!matches!(format.radix(), 2 | 4 | 8 | 16 | 32),
"performance slow for non-pow2 cases"
);
debug_assert!(
format.mantissa_radix() == format.exponent_base(),
"only works if digits and exponent have same base"
);
let fp_zero = ExtendedFloat80 {
mant: 0,
exp: 0,
};
let fp_inf = ExtendedFloat80 {
mant: 0,
exp: F::INFINITE_POWER,
};
// Early short-circuit, in case of literal 0 or infinity.
// This allows us to avoid narrow casts causing numeric overflow,
// and is a quick check for any radix.
if num.mantissa == 0 || num.exponent <= -0x1000 {
return fp_zero;
} else if num.exponent >= 0x1000 {
return fp_inf;
}
// Calculate our indexes for our extended-precision multiplication.
let powers = bellerophon_powers(format.radix());
// This narrowing cast is safe, since exponent must be in a valid range.
let exponent = num.exponent as i32 + powers.bias;
let small_index = exponent % powers.step;
let large_index = exponent / powers.step;
if exponent < 0 {
// Guaranteed underflow (assign 0).
return fp_zero;
}
if large_index as usize >= powers.large.len() {
// Overflow (assign infinity)
return fp_inf;
}
// Within the valid exponent range, multiply by the large and small
// exponents and return the resulting value.
// Track errors to as a factor of unit in last-precision.
let mut errors: u32 = 0;
if num.many_digits {
errors += error_halfscale();
}
// Multiply by the small power.
// Check if we can directly multiply by an integer, if not,
// use extended-precision multiplication.
let mut fp = ExtendedFloat80 {
mant: num.mantissa,
exp: 0,
};
match fp.mant.overflowing_mul(powers.get_small_int(small_index as usize)) {
// Overflow, multiplication unsuccessful, go slow path.
(_, true) => {
normalize(&mut fp);
fp = mul(&fp, &powers.get_small(small_index as usize));
errors += error_halfscale();
},
// No overflow, multiplication successful.
(mant, false) => {
fp.mant = mant;
normalize(&mut fp);
},
}
// Multiply by the large power.
fp = mul(&fp, &powers.get_large(large_index as usize));
if errors > 0 {
errors += 1;
}
errors += error_halfscale();
// Normalize the floating point (and the errors).
let shift = normalize(&mut fp);
errors <<= shift;
fp.exp += F::EXPONENT_BIAS;
// Check for literal overflow, even with halfway cases.
if -fp.exp + 1 > 65 {
return fp_zero;
}
// Too many errors accumulated, return an error.
if !lossy && !error_is_accurate::<F>(errors, &fp) {
// Bias the exponent so we know it's invalid.
fp.exp += shared::INVALID_FP;
return fp;
}
// Check if we have a literal 0 or overflow here.
// If we have an exponent of -63, we can still have a valid shift,
// giving a case where we have too many errors and need to round-up.
if -fp.exp + 1 == 65 {
// Have more than 64 bits below the minimum exponent, must be 0.
return fp_zero;
}
shared::round::<F, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| {
is_above || (is_odd && is_halfway)
});
});
fp
}
// ERRORS
// ------
// Calculate if the errors in calculating the extended-precision float.
//
// Specifically, we want to know if we are close to a halfway representation,
// or halfway between `b` and `b+1`, or `b+h`. The halfway representation
// has the form:
// `SEEEEEEEHMMMMMMMMMMMMMMMMMMMMMMM100...`
// where:
// S = Sign Bit
// E = Exponent Bits
// H = Hidden Bit
// M = Mantissa Bits
//
// The halfway representation has a bit set 1-after the mantissa digits,
// and no bits set immediately afterward, making it impossible to
// round between `b` and `b+1` with this representation.
/// Get the full error scale.
#[inline(always)]
const fn error_scale() -> u32 {
8
}
/// Get the half error scale.
#[inline(always)]
const fn error_halfscale() -> u32 {
error_scale() / 2
}
/// Determine if the number of errors is tolerable for float precision.
#[cfg_attr(not(feature = "compact"), inline(always))]
fn error_is_accurate<F: RawFloat>(errors: u32, fp: &ExtendedFloat80) -> bool {
// Check we can't have a literal 0 denormal float.
debug_assert!(fp.exp >= -64, "cannot have a literal 0 float");
// Determine if extended-precision float is a good approximation.
// If the error has affected too many units, the float will be
// inaccurate, or if the representation is too close to halfway
// that any operations could affect this halfway representation.
// See the documentation for dtoa for more information.
// This is always a valid u32, since `fp.exp >= -64`
// will always be positive and the significand size is `{23, 52}`.
let mantissa_shift = 64 - F::MANTISSA_SIZE - 1;
// The unbiased exponent checks is `unbiased_exp <= F::MANTISSA_SIZE
// - F::EXPONENT_BIAS -64 + 1`, or `biased_exp <= F::MANTISSA_SIZE - 63`,
// or `biased_exp <= mantissa_shift`.
let extrabits = match fp.exp <= -mantissa_shift {
// Denormal, since shifting to the hidden bit still has a negative exponent.
// The unbiased check calculation for bits is `1 - F::EXPONENT_BIAS - unbiased_exp`,
// or `1 - biased_exp`.
true => 1 - fp.exp,
false => 64 - F::MANTISSA_SIZE - 1,
};
// Our logic is as follows: we want to determine if the actual
// mantissa and the errors during calculation differ significantly
// from the rounding point. The rounding point for round-nearest
// is the halfway point, IE, this when the truncated bits start
// with b1000..., while the rounding point for the round-toward
// is when the truncated bits are equal to 0.
// To do so, we can check whether the rounding point +/- the error
// are >/< the actual lower n bits.
//
// For whether we need to use signed or unsigned types for this
// analysis, see this example, using u8 rather than u64 to simplify
// things.
//
// # Comparisons
// cmp1 = (halfway - errors) < extra
// cmp1 = extra < (halfway + errors)
//
// # Large Extrabits, Low Errors
//
// extrabits = 8
// halfway = 0b10000000
// extra = 0b10000010
// errors = 0b00000100
// halfway - errors = 0b01111100
// halfway + errors = 0b10000100
//
// Unsigned:
// halfway - errors = 124
// halfway + errors = 132
// extra = 130
// cmp1 = true
// cmp2 = true
// Signed:
// halfway - errors = 124
// halfway + errors = -124
// extra = -126
// cmp1 = false
// cmp2 = true
//
// # Conclusion
//
// Since errors will always be small, and since we want to detect
// if the representation is accurate, we need to use an **unsigned**
// type for comparisons.
let maskbits = extrabits as u64;
let errors = errors as u64;
// Round-to-nearest, need to use the halfway point.
if extrabits > 64 {
// Underflow, we have a shift larger than the mantissa.
// Representation is valid **only** if the value is close enough
// overflow to the next bit within errors. If it overflows,
// the representation is **not** valid.
!fp.mant.overflowing_add(errors).1
} else {
let mask = lower_n_mask(maskbits);
let extra = fp.mant & mask;
// Round-to-nearest, need to check if we're close to halfway.
// IE, b10100 | 100000, where `|` signifies the truncation point.
let halfway = lower_n_halfway(maskbits);
let cmp1 = halfway.wrapping_sub(errors) < extra;
let cmp2 = extra < halfway.wrapping_add(errors);
// If both comparisons are true, we have significant rounding error,
// and the value cannot be exactly represented. Otherwise, the
// representation is valid.
!(cmp1 && cmp2)
}
}
// MATH
// ----
/// Normalize float-point number.
///
/// Shift the mantissa so the number of leading zeros is 0, or the value
/// itself is 0.
///
/// Get the number of bytes shifted.
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn normalize(fp: &mut ExtendedFloat80) -> i32 {
// Note:
// Using the ctlz intrinsic via `leading_zeros` is way faster (~10x)
// than shifting 1-bit at a time, via while loop, and also way
// faster (~2x) than an unrolled loop that checks at 32, 16, 4,
// 2, and 1 bit.
//
// Using a modulus of pow2 (which will get optimized to a bitwise
// and with 0x3F or faster) is slightly slower than an if/then,
// however, removing the if/then will likely optimize more branched
// code as it removes conditional logic.
// Calculate the number of leading zeros, and then zero-out
// any overflowing bits, to avoid shl overflow when `self.mant == 0`.
if fp.mant != 0 {
let shift = fp.mant.leading_zeros() as i32;
fp.mant <<= shift;
fp.exp -= shift;
shift
} else {
0
}
}
/// Multiply two normalized extended-precision floats, as if by `a*b`.
///
/// The precision is maximal when the numbers are normalized, however,
/// decent precision will occur as long as both values have high bits
/// set. The result is not normalized.
///
/// Algorithm:
/// 1. Non-signed multiplication of mantissas (requires 2x as many bits as
/// input).
/// 2. Normalization of the result (not done here).
/// 3. Addition of exponents.
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn mul(x: &ExtendedFloat80, y: &ExtendedFloat80) -> ExtendedFloat80 {
// Logic check, values must be decently normalized prior to multiplication.
debug_assert!(x.mant >> 32 != 0, "cannot have a literal 0 float");
debug_assert!(y.mant >> 32 != 0, "cannot have a literal 0 float");
// Extract high-and-low masks.
const LOMASK: u64 = u32::MAX as u64;
let x1 = x.mant >> 32;
let x0 = x.mant & LOMASK;
let y1 = y.mant >> 32;
let y0 = y.mant & LOMASK;
// Get our products
let x1_y0 = x1 * y0;
let x0_y1 = x0 * y1;
let x0_y0 = x0 * y0;
let x1_y1 = x1 * y1;
let mut tmp = (x1_y0 & LOMASK) + (x0_y1 & LOMASK) + (x0_y0 >> 32);
// round up
tmp += 1 << (32 - 1);
ExtendedFloat80 {
mant: x1_y1 + (x1_y0 >> 32) + (x0_y1 >> 32) + (tmp >> 32),
exp: x.exp + y.exp + 64,
}
}
// POWERS
// ------
/// Pre-calculated powers of base N for the Bellerophon algorithm.
pub struct BellerophonPowers {
// Pre-calculated small powers.
pub small: &'static [u64],
// Pre-calculated large powers.
pub large: &'static [u64],
/// Pre-calculated small powers as 64-bit integers
pub small_int: &'static [u64],
// Step between large powers and number of small powers.
pub step: i32,
// Exponent bias for the large powers.
pub bias: i32,
/// `ceil(log2(radix))` scaled as a multiplier.
pub log2: i64,
/// Bit shift for the log2 multiplier.
pub log2_shift: i32,
}
/// Allow indexing of values without bounds checking
impl BellerophonPowers {
#[inline(always)]
pub const fn get_small(&self, index: usize) -> ExtendedFloat80 {
let mant = self.small[index];
let exp = (1 - 64) + ((self.log2 * index as i64) >> self.log2_shift);
ExtendedFloat80 {
mant,
exp: exp as i32,
}
}
#[inline(always)]
pub const fn get_large(&self, index: usize) -> ExtendedFloat80 {
let mant = self.large[index];
let biased_e = index as i64 * self.step as i64 - self.bias as i64;
let exp = (1 - 64) + ((self.log2 * biased_e) >> self.log2_shift);
ExtendedFloat80 {
mant,
exp: exp as i32,
}
}
#[inline(always)]
pub const fn get_small_int(&self, index: usize) -> u64 {
self.small_int[index]
}
}
File diff suppressed because it is too large Load Diff
+214
View File
@@ -0,0 +1,214 @@
//! Optimized float parser for radixes powers of 2.
//!
//! Note: this does not require the mantissa radix and the
//! exponent base to be the same.
#![cfg(feature = "power-of-two")]
#![doc(hidden)]
#[cfg(not(feature = "compact"))]
use lexical_parse_integer::algorithm;
use lexical_util::digit::char_to_valid_digit_const;
use lexical_util::format::NumberFormat;
use lexical_util::iterator::{AsBytes, DigitsIter};
use lexical_util::step::u64_step;
use crate::float::{ExtendedFloat80, RawFloat};
use crate::mask::lower_n_halfway;
use crate::number::Number;
use crate::shared;
// ALGORITHM
// ---------
/// Algorithm specialized for radixes of powers-of-two.
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn binary<F: RawFloat, const FORMAT: u128>(num: &Number, lossy: bool) -> ExtendedFloat80 {
let format = NumberFormat::<{ FORMAT }> {};
debug_assert!(
matches!(format.radix(), 2 | 4 | 8 | 16 | 32),
"algorithm requires a power-of-two"
);
let fp_zero = ExtendedFloat80 {
mant: 0,
exp: 0,
};
// Normalize our mantissa for simpler results.
let ctlz = num.mantissa.leading_zeros();
let mantissa = num.mantissa << ctlz;
// Quick check if we're close to a halfway point.
// Since we're using powers-of-two, we can clearly tell if we're at
// a halfway point, unless it's even and we're exactly halfway so far.
// This is true even for radixes like 8 and 32, where `log2(radix)`
// is not a power-of-two. If it's odd and we're at halfway, we'll
// always round-up **anyway**.
//
// We need to check the truncated bits are equal to `0b100000....`,
// if it's above that, always round-up. If it's odd, we can always
// disambiguate the float. If it's even, and exactly halfway, this
// step fails.
let power2 = shared::calculate_power2::<F, FORMAT>(num.exponent, ctlz);
if -power2 + 1 >= 64 {
// Have more than 63 bits below the minimum exponent, must be 0.
// Since we can't have partial digit rounding, this is true always
// if the power-of-two >= 64.
return fp_zero;
}
// Get our shift to shift the digits to the hidden bit, or correct spot.
// This differs for denormal floats, so do that carefully, but that's
// relative to the current leading zeros of the float.
let shift = shared::calculate_shift::<F>(power2);
// Determine if we can see if we're at a halfway point.
let last_bit = 1u64 << shift;
let truncated = last_bit - 1;
let halfway = lower_n_halfway(shift as u64);
let is_even = mantissa & last_bit == 0;
let is_halfway = mantissa & truncated == halfway;
if !lossy && is_even && is_halfway && num.many_digits {
// Exactly halfway and even, cannot safely determine our representation.
// Bias the exponent so we know it's invalid.
return ExtendedFloat80 {
mant: mantissa,
exp: power2 + shared::INVALID_FP,
};
}
// Shift our digits into place, and round up if needed.
let is_above = mantissa & truncated > halfway;
let round_up = is_above || (!is_even && is_halfway);
let mut fp = ExtendedFloat80 {
mant: mantissa,
exp: power2,
};
shared::round::<F, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |_, _, _| round_up);
});
fp
}
/// Iteratively parse and consume digits without overflowing.
///
/// We're guaranteed to have a large number of digits here
/// (in general, 20+ or much higher), due to how close we
/// are to a halfway representation, so an unchecked loop
/// optimization isn't worth it.
#[cfg_attr(not(feature = "compact"), inline(always))]
#[allow(unused_mut)]
pub fn parse_u64_digits<'a, Iter, const FORMAT: u128>(
mut iter: Iter,
mantissa: &mut u64,
step: &mut usize,
overflowed: &mut bool,
zero: &mut bool,
) where
Iter: DigitsIter<'a>,
{
let format = NumberFormat::<{ FORMAT }> {};
let radix = format.radix() as u64;
// Try to parse 8 digits at a time, if we can.
#[cfg(not(feature = "compact"))]
if can_try_parse_multidigit!(iter, radix) {
debug_assert!(radix < 16, "larger radices will wrap on radix^8");
let radix8 = format.radix8() as u64;
while *step > 8 {
if let Some(v) = algorithm::try_parse_8digits::<u64, _, FORMAT>(&mut iter) {
*mantissa = mantissa.wrapping_mul(radix8).wrapping_add(v);
*step -= 8;
} else {
break;
}
}
}
// Parse single digits at a time.
for &c in iter {
let digit = char_to_valid_digit_const(c, radix as u32);
if !*overflowed {
let result = mantissa.checked_mul(radix).and_then(|x| x.checked_add(digit as u64));
if let Some(mant) = result {
*mantissa = mant;
} else {
*overflowed = true;
*zero &= digit == 0;
}
} else {
*zero &= digit == 0;
}
*step = step.saturating_sub(1);
}
}
/// Fallback, slow algorithm optimized for powers-of-two.
///
/// This avoids the need for arbitrary-precision arithmetic, since the result
/// will always be a near-halfway representation where rounded-down it's even.
pub fn slow_binary<F: RawFloat, const FORMAT: u128>(num: Number) -> ExtendedFloat80 {
let format = NumberFormat::<{ FORMAT }> {};
let radix = format.radix();
debug_assert!(matches!(radix, 2 | 4 | 8 | 16 | 32), "algorithm requires a power-of-two");
// This assumes the sign bit has already been parsed, and we're
// starting with the integer digits, and the float format has been
// correctly validated.
// This is quite simple: parse till we get overflow, check if all
// the remaining digits are zero/non-zero, and determine if we round-up
// or down as a result.
let mut mantissa = 0_u64;
let mut overflow = false;
let mut zero = true;
// Parse the integer digits.
let mut step = u64_step(radix);
let mut integer = num.integer.bytes::<FORMAT>();
integer.integer_iter().skip_zeros();
parse_u64_digits::<_, FORMAT>(
integer.integer_iter(),
&mut mantissa,
&mut step,
&mut overflow,
&mut zero,
);
// Parse the fraction digits.
if let Some(fraction) = num.fraction {
let mut fraction = fraction.bytes::<FORMAT>();
if mantissa == 0 {
fraction.fraction_iter().skip_zeros();
}
parse_u64_digits::<_, FORMAT>(
fraction.fraction_iter(),
&mut mantissa,
&mut step,
&mut overflow,
&mut zero,
);
}
// Note: we're not guaranteed to have overflowed here, although it's
// very, very likely. We can also skip the exponent, since we already
// know it, and we already know the total parsed digits.
// Normalize our mantissa for simpler results.
let ctlz = mantissa.leading_zeros();
mantissa <<= ctlz;
let power2 = shared::calculate_power2::<F, FORMAT>(num.exponent, ctlz);
let mut fp = ExtendedFloat80 {
mant: mantissa,
exp: power2,
};
shared::round::<F, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |_, _, _| !zero);
});
fp
}
+200
View File
@@ -0,0 +1,200 @@
//! Extended helper trait for generic float types.
//!
//! This adapted from the Rust implementation, based on the fast-float-rust
//! implementation, and is similarly subject to an Apache2.0/MIT license.
#![doc(hidden)]
#[cfg(feature = "f16")]
use lexical_util::bf16::bf16;
use lexical_util::extended_float::ExtendedFloat;
#[cfg(feature = "f16")]
use lexical_util::f16::f16;
use lexical_util::num::{AsCast, Float};
#[cfg(all(not(feature = "std"), feature = "compact"))]
use crate::libm::{powd, powf};
use crate::limits::{ExactFloat, MaxDigits};
#[cfg(not(feature = "compact"))]
use crate::table::{get_small_f32_power, get_small_f64_power, get_small_int_power};
/// Alias with ~80 bits of precision, 64 for the mantissa and 16 for exponent.
/// This exponent is biased, and if the exponent is negative, it represents
/// a value with a bias of `i32::MIN + F::EXPONENT_BIAS`.
pub type ExtendedFloat80 = ExtendedFloat<u64>;
/// Helper trait to add more float characteristics for parsing floats.
pub trait RawFloat: Float + ExactFloat + MaxDigits {
// Maximum mantissa for the fast-path (`1 << 53` for f64).
const MAX_MANTISSA_FAST_PATH: u64 = 2_u64 << Self::MANTISSA_SIZE;
// Largest exponent value `(1 << EXP_BITS) - 1`.
const INFINITE_POWER: i32 = Self::MAX_EXPONENT + Self::EXPONENT_BIAS;
/// Minimum exponent that for a fast path case, or
/// `-⌊(MANTISSA_SIZE+1)/log2(r)⌋` where `r` is the radix with
/// powers-of-two removed.
#[must_use]
#[inline(always)]
fn min_exponent_fast_path(radix: u32) -> i64 {
Self::exponent_limit(radix).0
}
/// Maximum exponent that for a fast path case, or
/// `⌊(MANTISSA_SIZE+1)/log2(r)⌋` where `r` is the radix with
/// powers-of-two removed.
#[must_use]
#[inline(always)]
fn max_exponent_fast_path(radix: u32) -> i64 {
Self::exponent_limit(radix).1
}
// Maximum exponent that can be represented for a disguised-fast path case.
// This is `max_exponent_fast_path(radix) + ⌊(MANTISSA_SIZE+1)/log2(radix)⌋`
#[must_use]
#[inline(always)]
fn max_exponent_disguised_fast_path(radix: u32) -> i64 {
Self::max_exponent_fast_path(radix) + Self::mantissa_limit(radix)
}
/// Get a small power-of-radix for fast-path multiplication.
fn pow_fast_path(exponent: usize, radix: u32) -> Self;
/// Get a small, integral power-of-radix for fast-path multiplication.
#[must_use]
#[inline(always)]
fn int_pow_fast_path(exponent: usize, radix: u32) -> u64 {
#[cfg(not(feature = "compact"))]
return get_small_int_power(exponent, radix);
#[cfg(feature = "compact")]
return (radix as u64).wrapping_pow(exponent as u32);
}
}
impl RawFloat for f32 {
#[inline(always)]
fn pow_fast_path(exponent: usize, radix: u32) -> Self {
#[cfg(not(feature = "compact"))]
return get_small_f32_power(exponent, radix);
#[cfg(feature = "compact")]
return powf(radix as f32, exponent as f32);
}
}
impl RawFloat for f64 {
#[inline(always)]
fn pow_fast_path(exponent: usize, radix: u32) -> Self {
#[cfg(not(feature = "compact"))]
return get_small_f64_power(exponent, radix);
#[cfg(feature = "compact")]
return powd(radix as f64, exponent as f64);
}
}
#[cfg(feature = "f16")]
impl RawFloat for f16 {
#[inline(always)]
fn pow_fast_path(_: usize, _: u32) -> Self {
unimplemented!()
}
}
#[cfg(feature = "f16")]
impl RawFloat for bf16 {
#[inline(always)]
fn pow_fast_path(_: usize, _: u32) -> Self {
unimplemented!()
}
}
/// Helper trait to add more float characteristics for the Eisel-Lemire
/// algorithm.
pub trait LemireFloat: RawFloat {
// Round-to-even only happens for negative values of q
// when `q ≥ 4` in the 64-bit case and when `q ≥ 17` in
// the 32-bitcase.
//
// When `q ≥ 0`,we have that `5^q ≤ 2m+1`. In the 64-bit case,we
// have `5^q ≤ 2m+1 ≤ 2^54` or `q ≤ 23`. In the 32-bit case,we have
// `5^q ≤ 2m+1 ≤ 2^25` or `q ≤ 10`.
//
// When q < 0, we have `w ≥ (2m+1)×5^q`. We must have that `w < 2^64`
// so `(2m+1)×5^q < 2^64`. We have that `2m+1 > 2^53` (64-bit case)
// or `2m+1 > 2^24` (32-bit case). Hence,we must have `2^53×5^q < 2^64`
// (64-bit) and `2^24×5^q < 2^64` (32-bit). Hence we have `5^q < 2^11`
// or `q ≥ 4` (64-bit case) and `5^q < 2^40` or `q ≥ 17` (32-bitcase).
//
// Thus we have that we only need to round ties to even when
// we have that `q ∈ [4,23]` (in the 64-bit case) or `q∈[17,10]`
// (in the 32-bit case). In both cases,the power of five (`5^|q|`)
// fits in a 64-bit word.
const MIN_EXPONENT_ROUND_TO_EVEN: i32;
const MAX_EXPONENT_ROUND_TO_EVEN: i32;
/// Minimum normal exponent value `-(1 << (EXPONENT_SIZE - 1)) + 1`.
const MINIMUM_EXPONENT: i32;
/// Smallest decimal exponent for a non-zero value.
const SMALLEST_POWER_OF_TEN: i32;
/// Largest decimal exponent for a non-infinite value.
const LARGEST_POWER_OF_TEN: i32;
}
impl LemireFloat for f32 {
const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17;
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10;
const MINIMUM_EXPONENT: i32 = -127;
const SMALLEST_POWER_OF_TEN: i32 = -65;
const LARGEST_POWER_OF_TEN: i32 = 38;
}
impl LemireFloat for f64 {
const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -4;
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23;
const MINIMUM_EXPONENT: i32 = -1023;
const SMALLEST_POWER_OF_TEN: i32 = -342;
const LARGEST_POWER_OF_TEN: i32 = 308;
}
#[cfg(feature = "f16")]
impl LemireFloat for f16 {
const MIN_EXPONENT_ROUND_TO_EVEN: i32 = 0;
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 0;
const MINIMUM_EXPONENT: i32 = 0;
const SMALLEST_POWER_OF_TEN: i32 = 0;
const LARGEST_POWER_OF_TEN: i32 = 0;
}
#[cfg(feature = "f16")]
impl LemireFloat for bf16 {
const MIN_EXPONENT_ROUND_TO_EVEN: i32 = 0;
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 0;
const MINIMUM_EXPONENT: i32 = 0;
const SMALLEST_POWER_OF_TEN: i32 = 0;
const LARGEST_POWER_OF_TEN: i32 = 0;
}
#[inline(always)]
#[cfg(all(feature = "std", feature = "compact"))]
pub fn powf(x: f32, y: f32) -> f32 {
x.powf(y)
}
#[inline(always)]
#[cfg(all(feature = "std", feature = "compact"))]
pub fn powd(x: f64, y: f64) -> f64 {
x.powf(y)
}
/// Converts an `ExtendedFloat` to the closest machine float type.
#[must_use]
#[inline(always)]
pub fn extended_to_float<F: Float>(x: ExtendedFloat80) -> F {
let mut word = x.mant;
word |= (x.exp as u64) << F::MANTISSA_SIZE;
F::from_bits(F::Unsigned::as_cast(word))
}
+107
View File
@@ -0,0 +1,107 @@
//! Platform-specific, assembly instructions to avoid
//! intermediate rounding on architectures with FPUs.
//!
//! This is adapted from the implementation in the Rust core library,
//! the original implementation can be [here](https://github.com/rust-lang/rust/blob/master/library/core/src/num/dec2flt/fpu.rs).
//!
//! It is therefore also subject to a Apache2.0/MIT license.
#![doc(hidden)]
pub use fpu_precision::set_precision;
// On x86, the x87 FPU is used for float operations if the SSE/SSE2 extensions
// are not available. The x87 FPU operates with 80 bits of precision by default,
// which means that operations will round to 80 bits causing double rounding to
// happen when values are eventually represented as 32/64 bit float values. To
// overcome this, the FPU control word can be set so that the computations are
// performed in the desired precision.
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
mod fpu_precision {
// We only support the latest nightly, which is 1.59+.
// The `asm!` macro was stabilized in 1.59.0.
use core::arch::asm;
use core::mem::size_of;
/// A structure used to preserve the original value of the FPU control word,
/// so that it can be restored when the structure is dropped.
///
/// The x87 FPU is a 16-bits register whose fields are as follows:
///
/// | 12-15 | 10-11 | 8-9 | 6-7 | 5 | 4 | 3 | 2 | 1 | 0 |
/// |------:|------:|----:|----:|---:|---:|---:|---:|---:|---:|
/// | | RC | PC | | PM | UM | OM | ZM | DM | IM |
///
/// The documentation for all of the fields is available in the IA-32
/// Architectures Software Developer's Manual (Volume 1).
///
/// The only field which is relevant for the following code is PC, Precision
/// Control. This field determines the precision of the operations
/// performed by the FPU. It can be set to:
/// - 0b00, single precision i.e., 32-bits
/// - 0b10, double precision i.e., 64-bits
/// - 0b11, double extended precision i.e., 80-bits (default state)
/// The 0b01 value is reserved and should not be used.
pub struct FPUControlWord(u16);
fn set_cw(cw: u16) {
// SAFETY: the `fldcw` instruction has been audited to be able to work correctly
// with any `u16`
unsafe {
asm!(
"fldcw word ptr [{}]",
in(reg) &cw,
options(nostack),
)
}
}
/// Sets the precision field of the FPU to `T` and returns a
/// `FPUControlWord`.
pub fn set_precision<T>() -> FPUControlWord {
let mut cw = 0_u16;
// Compute the value for the Precision Control field that is appropriate for
// `T`.
let cw_precision = match size_of::<T>() {
4 => 0x0000, // 32 bits
8 => 0x0200, // 64 bits
_ => 0x0300, // default, 80 bits
};
// Get the original value of the control word to restore it later, when the
// `FPUControlWord` structure is dropped
// SAFETY: the `fnstcw` instruction has been audited to be able to work
// correctly with any `u16`
unsafe {
asm!(
"fnstcw word ptr [{}]",
in(reg) &mut cw,
options(nostack),
)
}
// Set the control word to the desired precision. This is achieved by masking
// away the old precision (bits 8 and 9, 0x300) and replacing it with
// the precision flag computed above.
set_cw((cw & 0xFCFF) | cw_precision);
FPUControlWord(cw)
}
impl Drop for FPUControlWord {
fn drop(&mut self) {
set_cw(self.0)
}
}
}
// In most architectures, floating point operations have an explicit bit size,
// therefore the precision of the computation is determined on a per-operation
// basis.
#[cfg(any(not(target_arch = "x86"), target_feature = "sse2"))]
mod fpu_precision {
#[inline]
pub const fn set_precision<T>() {
}
}
+239
View File
@@ -0,0 +1,239 @@
//! Implementation of the Eisel-Lemire algorithm.
//!
//! This is adapted from [fast-float-rust](https://github.com/aldanor/fast-float-rust),
//! a port of [fast_float](https://github.com/fastfloat/fast_float) to Rust.
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
use crate::float::{ExtendedFloat80, LemireFloat};
use crate::number::Number;
use crate::shared;
use crate::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE};
/// Ensure truncation of digits doesn't affect our computation, by doing 2
/// passes.
#[must_use]
#[inline(always)]
pub fn lemire<F: LemireFloat>(num: &Number, lossy: bool) -> ExtendedFloat80 {
// If significant digits were truncated, then we can have rounding error
// only if `mantissa + 1` produces a different result. We also avoid
// redundantly using the Eisel-Lemire algorithm if it was unable to
// correctly round on the first pass.
let mut fp = compute_float::<F>(num.exponent, num.mantissa, lossy);
if !lossy
&& num.many_digits
&& fp.exp >= 0
&& fp != compute_float::<F>(num.exponent, num.mantissa + 1, false)
{
// Need to re-calculate, since the previous values are rounded
// when the slow path algorithm expects a normalized extended float.
fp = compute_error::<F>(num.exponent, num.mantissa);
}
fp
}
/// Compute a float using an extended-precision representation.
///
/// Fast conversion of a the significant digits and decimal exponent
/// a float to a extended representation with a binary float. This
/// algorithm will accurately parse the vast majority of cases,
/// and uses a 128-bit representation (with a fallback 192-bit
/// representation).
///
/// This algorithm scales the exponent by the decimal exponent
/// using pre-computed powers-of-5, and calculates if the
/// representation can be unambiguously rounded to the nearest
/// machine float. Near-halfway cases are not handled here,
/// and are represented by a negative, biased binary exponent.
///
/// The algorithm is described in detail in "Daniel Lemire, Number Parsing
/// at a Gigabyte per Second" in section 5, "Fast Algorithm", and
/// section 6, "Exact Numbers And Ties", available online:
/// <https://arxiv.org/abs/2101.11408.pdf>.
#[inline]
#[must_use]
#[allow(clippy::missing_inline_in_public_items)] // reason="public for testing only"
pub fn compute_float<F: LemireFloat>(q: i64, mut w: u64, lossy: bool) -> ExtendedFloat80 {
let fp_zero = ExtendedFloat80 {
mant: 0,
exp: 0,
};
let fp_inf = ExtendedFloat80 {
mant: 0,
exp: F::INFINITE_POWER,
};
// Short-circuit if the value can only be a literal 0 or infinity.
if w == 0 || q < F::SMALLEST_POWER_OF_TEN as i64 {
return fp_zero;
} else if q > F::LARGEST_POWER_OF_TEN as i64 {
return fp_inf;
}
// Normalize our significant digits, so the most-significant bit is set.
let lz = w.leading_zeros() as i32;
w <<= lz;
let (lo, hi) = compute_product_approx(q, w, F::MANTISSA_SIZE as usize + 3);
if !lossy && lo == 0xFFFF_FFFF_FFFF_FFFF {
// If we have failed to approximate `w x 5^-q` with our 128-bit value.
// Since the addition of 1 could lead to an overflow which could then
// round up over the half-way point, this can lead to improper rounding
// of a float.
//
// However, this can only occur if `q ∈ [-27, 55]`. The upper bound of q
// is 55 because `5^55 < 2^128`, however, this can only happen if `5^q > 2^64`,
// since otherwise the product can be represented in 64-bits, producing
// an exact result. For negative exponents, rounding-to-even can
// only occur if `5^-q < 2^64`.
//
// For detailed explanations of rounding for negative exponents, see
// <https://arxiv.org/pdf/2101.11408.pdf#section.9.1>. For detailed
// explanations of rounding for positive exponents, see
// <https://arxiv.org/pdf/2101.11408.pdf#section.8>.
let inside_safe_exponent = (-27..=55).contains(&q);
if !inside_safe_exponent {
return compute_error_scaled::<F>(q, hi, lz);
}
}
let upperbit = (hi >> 63) as i32;
let mut mantissa = hi >> (upperbit + 64 - F::MANTISSA_SIZE - 3);
let mut power2 = power(q as i32) + upperbit - lz - F::MINIMUM_EXPONENT;
if power2 <= 0 {
if -power2 + 1 >= 64 {
// Have more than 64 bits below the minimum exponent, must be 0.
return fp_zero;
}
// Have a subnormal value.
mantissa >>= -power2 + 1;
mantissa += mantissa & 1;
mantissa >>= 1;
power2 = (mantissa >= (1_u64 << F::MANTISSA_SIZE)) as i32;
return ExtendedFloat80 {
mant: mantissa,
exp: power2,
};
}
// Need to handle rounding ties. Normally, we need to round up,
// but if we fall right in between and and we have an even basis, we
// need to round down.
//
// This will only occur if:
// 1. The lower 64 bits of the 128-bit representation is 0. IE, `5^q` fits in
// single 64-bit word.
// 2. The least-significant bit prior to truncated mantissa is odd.
// 3. All the bits truncated when shifting to mantissa bits + 1 are 0.
//
// Or, we may fall between two floats: we are exactly halfway.
if lo <= 1
&& q >= F::MIN_EXPONENT_ROUND_TO_EVEN as i64
&& q <= F::MAX_EXPONENT_ROUND_TO_EVEN as i64
&& mantissa & 3 == 1
&& (mantissa << (upperbit + 64 - F::MANTISSA_SIZE - 3)) == hi
{
// Zero the lowest bit, so we don't round up.
mantissa &= !1_u64;
}
// Round-to-even, then shift the significant digits into place.
mantissa += mantissa & 1;
mantissa >>= 1;
if mantissa >= (2_u64 << F::MANTISSA_SIZE) {
// Rounding up overflowed, so the carry bit is set. Set the
// mantissa to 1 (only the implicit, hidden bit is set) and
// increase the exponent.
mantissa = 1_u64 << F::MANTISSA_SIZE;
power2 += 1;
}
// Zero out the hidden bit.
mantissa &= !(1_u64 << F::MANTISSA_SIZE);
if power2 >= F::INFINITE_POWER {
// Exponent is above largest normal value, must be infinite.
return fp_inf;
}
ExtendedFloat80 {
mant: mantissa,
exp: power2,
}
}
/// Fallback algorithm to calculate the non-rounded representation.
/// This calculates the extended representation, and then normalizes
/// the resulting representation, so the high bit is set.
#[must_use]
#[inline(always)]
pub fn compute_error<F: LemireFloat>(q: i64, mut w: u64) -> ExtendedFloat80 {
let lz = w.leading_zeros() as i32;
w <<= lz;
let hi = compute_product_approx(q, w, F::MANTISSA_SIZE as usize + 3).1;
compute_error_scaled::<F>(q, hi, lz)
}
/// Compute the error from a mantissa scaled to the exponent.
#[must_use]
#[inline(always)]
pub const fn compute_error_scaled<F: LemireFloat>(q: i64, mut w: u64, lz: i32) -> ExtendedFloat80 {
// Want to normalize the float, but this is faster than ctlz on most
// architectures.
let hilz = (w >> 63) as i32 ^ 1;
w <<= hilz;
let power2 = power(q as i32) + F::EXPONENT_BIAS - hilz - lz - 62;
ExtendedFloat80 {
mant: w,
exp: power2 + shared::INVALID_FP,
}
}
/// Calculate a base 2 exponent from a decimal exponent.
/// This uses a pre-computed integer approximation for
/// log2(10), where 217706 / 2^16 is accurate for the
/// entire range of non-finite decimal exponents.
#[inline(always)]
const fn power(q: i32) -> i32 {
(q.wrapping_mul(152_170 + 65536) >> 16) + 63
}
#[inline(always)]
const fn full_multiplication(a: u64, b: u64) -> (u64, u64) {
let r = (a as u128) * (b as u128);
(r as u64, (r >> 64) as u64)
}
// This will compute or rather approximate `w * 5**q` and return a pair of
// 64-bit words approximating the result, with the "high" part corresponding to
// the most significant bits and the low part corresponding to the least
// significant bits.
#[inline]
fn compute_product_approx(q: i64, w: u64, precision: usize) -> (u64, u64) {
debug_assert!(q >= SMALLEST_POWER_OF_FIVE as i64, "must be within our required pow5 range");
debug_assert!(q <= LARGEST_POWER_OF_FIVE as i64, "must be within our required pow5 range");
debug_assert!(precision <= 64, "requires a 64-bit or smaller float");
let mask = if precision < 64 {
0xFFFF_FFFF_FFFF_FFFF_u64 >> precision
} else {
0xFFFF_FFFF_FFFF_FFFF_u64
};
// `5^q < 2^64`, then the multiplication always provides an exact value.
// That means whenever we need to round ties to even, we always have
// an exact value.
let index = (q - SMALLEST_POWER_OF_FIVE as i64) as usize;
let (lo5, hi5) = POWER_OF_FIVE_128[index];
// Only need one multiplication as long as there is 1 zero but
// in the explicit mantissa bits, +1 for the hidden bit, +1 to
// determine the rounding direction, +1 for if the computed
// product has a leading zero.
let (mut first_lo, mut first_hi) = full_multiplication(w, lo5);
if first_hi & mask == mask {
// Need to do a second multiplication to get better precision
// for the lower product. This will always be exact
// where q is < 55, since 5^55 < 2^128. If this wraps,
// then we need to need to round up the hi product.
let (_, second_hi) = full_multiplication(w, hi5);
first_lo = first_lo.wrapping_add(second_hi);
if second_hi > first_lo {
first_hi += 1;
}
}
(first_lo, first_hi)
}
+583
View File
@@ -0,0 +1,583 @@
//! Fast lexical string-to-float conversion routines.
//!
//! This contains high-performance methods to parse floats from bytes.
//! Using [`from_lexical`] is analogous to [`parse`][`core-parse`],
//! while enabling parsing from bytes as well as [`str`].
//!
//!
//! [`from_lexical`]: FromLexical::from_lexical
//! [`core-parse`]: core::str::FromStr
//!
//! # Getting Started
//!
//! To parse a number from bytes, use [`from_lexical`]:
//!
//! ```rust
//! # #[no_std]
//! # use core::str;
//! use lexical_parse_float::{Error, FromLexical};
//!
//! let value = f64::from_lexical("1234.5".as_bytes());
//! assert_eq!(value, Ok(1234.5));
//!
//! let value = f64::from_lexical("1.2345e325".as_bytes());
//! assert_eq!(value, Ok(f64::INFINITY));
//!
//! let value = f64::from_lexical("1234.5 }, {\"Key\", \"Value\"}}".as_bytes());
//! assert_eq!(value, Err(Error::InvalidDigit(6)));
//! ```
//!
//! If wishing to incrementally parse a string from bytes, that is, parse as
//! many characters until an invalid digit is found, you can use the partial
//! parsers. This is useful in parsing data where the type is known, such as
//! JSON, but where the end of the number is not yet known.
//!
//! ```rust
//! # #[no_std]
//! # use core::str;
//! use lexical_parse_float::{Error, FromLexical};
//!
//! let value = f64::from_lexical_partial("1234.5 }, {\"Key\", \"Value\"}}".as_bytes());
//! assert_eq!(value, Ok((1234.5, 6)));
//!
//! let value = f64::from_lexical_partial("1.2345e325".as_bytes());
//! assert_eq!(value, Ok((f64::INFINITY, 10)));
//! ```
//!
//! # Options/Formatting API
//!
//! Each float parser contains extensive formatting control through
//! [`mod@options`] and [`mod@format`], including digit [`separator`]
//! support (that is, floats such as `1_2__3.4_5`), if integral,
//! fractional, or any significant digits are required, if to disable
//! parsing non-finite values, if `+` signs are invalid or required,
//! and much more. For more comprehensive examples, see the
//! [`format`](#format) and [Comprehensive Configuration] sections
//! below.
//!
//! [`separator`]: NumberFormat::digit_separator
//! [Comprehensive Configuration]: #comprehensive-configuration
//!
//! ```rust
//! # #[cfg(feature = "radix")] {
//! # use core::str;
//! use lexical_parse_float::{Error, FromLexicalWithOptions, NumberFormatBuilder, Options};
//!
//! const FORMAT: u128 = NumberFormatBuilder::new()
//! // require a `+` or `-` sign before the number
//! .required_mantissa_sign(true)
//! // require a `+` or `-` sign before the exponent digits
//! .required_exponent_sign(true)
//! // build the format, panicking if the format is invalid
//! .build_strict();
//! const OPTIONS: Options = Options::new();
//!
//! let value = "+1.234e+300";
//! let result = f64::from_lexical_with_options::<FORMAT>(value.as_bytes(), &OPTIONS);
//! assert_eq!(result, Ok(1.234e+300));
//!
//! let value = "1.234e+300";
//! let result = f64::from_lexical_with_options::<FORMAT>(value.as_bytes(), &OPTIONS);
//! assert_eq!(result, Err(Error::MissingSign(0)));
//! # }
//! ```
//!
//! # Features
//!
//! * `format` - Add support for parsing custom integer formats.
//! * `power-of-two` - Add support for parsing power-of-two integer strings.
//! * `radix` - Add support for strings of any radix.
//! * `compact` - Reduce code size at the cost of performance.
//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and
//! [`bf16`][`brain-float`] floats.
//! * `std` (Default) - Disable to allow use in a [`no_std`] environment.
//!
//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html
//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format
//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format
//!
//! A complete description of supported features includes:
//!
//! #### format
//!
//! Add support custom float parsing specifications. This should be used in
//! conjunction with [`Options`] for extensible float parsing.
//!
//! ##### JSON
//!
//! For example, in JSON, the following floats are valid or invalid:
//!
//! ```text
//! -1 // valid
//! +1 // invalid
//! 1 // valid
//! 1. // invalid
//! .1 // invalid
//! 0.1 // valid
//! nan // invalid
//! inf // invalid
//! Infinity // invalid
//! ```
//!
//! All of the finite numbers are valid in Rust, and Rust provides constants
//! for non-finite floats. In order to parse standard-conforming JSON floats
//! using lexical, you may use the following approach:
//!
//! ```rust
//! # #[cfg(feature = "format")] {
//! use lexical_parse_float::{format, options, Error, FromLexicalWithOptions, Result};
//!
//! fn parse_json_float(bytes: &[u8]) -> Result<f64> {
//! f64::from_lexical_with_options::<{ format::JSON }>(bytes, &options::JSON)
//! }
//!
//! assert_eq!(parse_json_float(b"-1"), Ok(-1.0));
//! assert_eq!(parse_json_float(b"+1"), Err(Error::InvalidPositiveSign(0)));
//! assert_eq!(parse_json_float(b"1"), Ok(1.0));
//! assert_eq!(parse_json_float(b"1."), Err(Error::EmptyFraction(2)));
//! assert_eq!(parse_json_float(b"0.1"), Ok(0.1));
//! assert_eq!(parse_json_float(b"nan"), Err(Error::EmptyInteger(0)));
//! assert_eq!(parse_json_float(b"inf"), Err(Error::EmptyInteger(0)));
//! assert_eq!(parse_json_float(b"Infinity"), Err(Error::EmptyInteger(0)));
//! # }
//! ```
//!
//! ##### Custom Format
//!
//! An example building and using a custom format, with many of the available
//! options is:
//!
//! ```rust
//! # #[cfg(feature = "format")] {
//! # use core::{num, str};
//! use lexical_parse_float::{Error, NumberFormatBuilder, Options, FromLexicalWithOptions};
//!
//! const FORMAT: u128 = NumberFormatBuilder::new()
//! // enable the use of digit separators with `_`
//! .digit_separator(num::NonZeroU8::new(b'_'))
//! // require digits before and after the decimal point,
//! // if the decimal point is present.
//! .required_integer_digits(true)
//! .required_fraction_digits(true)
//! // do not allow a leading `+` sign, so `+123` is invalid
//! .no_positive_mantissa_sign(true)
//! // do not allow `0` before an integer, so `01.1` is invalid.
//! // however, `0.1` is valid.
//! .no_integer_leading_zeros(true)
//! // allow digit separators anywhere, including consecutive ones
//! .leading_digit_separator(true)
//! .trailing_digit_separator(true)
//! .internal_digit_separator(true)
//! .consecutive_digit_separator(true)
//! // make it so the exponent character, `e`, is case-sensitive
//! // that is, `E` is not considered a valid exponent character
//! .case_sensitive_exponent(true)
//! .build_strict();
//! const OPTIONS: Options = Options::builder()
//! // change the string representation of NaN from `NaN` to `nan`
//! .nan_string(Some(b"nan"))
//! // disable a short infinity: long infinity is still allowed
//! .inf_string(None)
//! .build_strict();
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"1_2.3_4", &OPTIONS);
//! assert_eq!(value, Ok(12.34));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"-inf", &OPTIONS);
//! assert_eq!(value, Err(Error::EmptyInteger(1)));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"Infinity", &OPTIONS);
//! assert_eq!(value, Ok(f64::INFINITY));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"nan", &OPTIONS);
//! assert_eq!(value.map(|x| x.is_nan()), Ok(true));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"+1_2.3_4", &OPTIONS);
//! assert_eq!(value, Err(Error::InvalidPositiveSign(0)));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"0.3_4", &OPTIONS);
//! assert_eq!(value, Ok(0.34));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"12", &OPTIONS);
//! assert_eq!(value, Ok(12.0));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"12.", &OPTIONS);
//! assert_eq!(value, Err(Error::EmptyFraction(3)));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"1.234e5", &OPTIONS);
//! assert_eq!(value, Ok(1.234e5));
//!
//! let value = f64::from_lexical_with_options::<FORMAT>(b"1.234E5", &OPTIONS);
//! assert_eq!(value, Err(Error::InvalidDigit(5)));
//! # }
//! ```
//!
//! Enabling the [`format`](crate#format) API significantly increases compile
//! times, however, it enables a large amount of customization in how floats are
//! written.
//!
//! #### power-of-two
//!
//! Enable parsing numbers with radixes that are powers of two, that is, `2`,
//! `4`, `8`, `16`, and `32`.
//!
//! ```rust
//! # #[cfg(feature = "power-of-two")] {
//! use lexical_parse_float::{NumberFormatBuilder, Options, FromLexicalWithOptions};
//!
//! const BINARY: u128 = NumberFormatBuilder::binary();
//! const OPTIONS: Options = Options::new();
//! let value = "1.0011101111100111011011001000101101000011100101011";
//! let result = f64::from_lexical_with_options::<BINARY>(value.as_bytes(), &OPTIONS);
//! assert_eq!(result, Ok(1.234f64));
//! # }
//! ```
//!
//! #### radix
//!
//! Enable parsing numbers using all radixes from `2` to `36`. This requires
//! more static storage than [`power-of-two`][crate#power-of-two], and increases
//! compile times, but can be quite useful for esoteric programming languages
//! which use duodecimal floats, for example.
//!
//! ```rust
//! # #[cfg(feature = "radix")] {
//! use lexical_parse_float::{NumberFormatBuilder, Options, FromLexicalWithOptions};
//!
//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12);
//! const OPTIONS: Options = Options::new();
//! let value = "1.29842830A44BAA2";
//! let result = f64::from_lexical_with_options::<FORMAT>(value.as_bytes(), &OPTIONS);
//! assert_eq!(result, Ok(1.234f64));
//! # }
//! ```
//!
//! #### compact
//!
//! Reduce the generated code size at the cost of performance. This minimizes
//! the number of static tables, inlining, and generics used, drastically
//! reducing the size of the generated binaries. However, this resulting
//! performance of the generated code is much lower.
//!
//! #### f16
//!
//! This enables the use of the half-precision floats [`f16`][`ieee-f16`] and
//! [`bf16`][`brain-float`]. However, since these have limited hardware support
//! and are primarily used for vectorized operations, they are parsed as if
//! they were an [`f32`]. Due to the low precision of 16-bit floats, the results
//! may appear to have significant rounding error.
//!
//! ```rust
//! # #[cfg(feature = "f16")] {
//! # use core::str;
//! use lexical_parse_float::{f16, FromLexical};
//!
//! let value = "1.234375";
//! let result = f16::from_lexical(value.as_bytes());
//! assert_eq!(result, Ok(f16::from_f64_const(1.234f64)));
//! # }
//! ```
//!
//! #### std
//!
//! Enable use of the standard library. Currently, the standard library
//! is not used, and may be disabled without any change in functionality
//! on stable.
//!
//! # Comprehensive Configuration
//!
//! `lexical-parse-float` provides two main levels of configuration:
//! - The [`NumberFormatBuilder`], creating a packed struct with custom
//! formatting options.
//! - The [`Options`] API.
//!
//! ## Number Format
//!
//! The number format class provides numerous flags to specify number writing.
//! When the [`power-of-two`](#power-of-two) feature is enabled, additional
//! flags are added:
//! - The radix for the significant digits (default `10`).
//! - The radix for the exponent base (default `10`).
//! - The radix for the exponent digits (default `10`).
//!
//! When the [`format`](#format) feature is enabled, numerous other syntax and
//! digit separator flags are enabled, including:
//! - A digit separator character, to group digits for increased legibility.
//! - Whether leading, trailing, internal, and consecutive digit separators are
//! allowed.
//! - Toggling required float components, such as digits before the decimal
//! point.
//! - Toggling whether special floats are allowed or are case-sensitive.
//!
//! Many pre-defined constants therefore exist to simplify common use-cases,
//! including:
//! - [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more.
//! - [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings,
//! and many more.
//!
//! For a list of all supported fields, see [Parse
//! Float Fields][NumberFormatBuilder#parse-float-fields].
//!
//! <!-- Spacer for rustfmt -->
#![cfg_attr(
feature = "format",
doc = "
[`JSON`]: format::JSON
[`XML`]: format::XML
[`TOML`]: format::TOML
[`YAML`]: format::YAML
[`SQLite`]: format::SQLITE
[`Rust`]: format::RUST_LITERAL
[`Python`]: format::PYTHON_LITERAL
[`C#`]: format::CSHARP_LITERAL
[`FORTRAN`]: format::FORTRAN_LITERAL
[`COBOL`]: format::COBOL_LITERAL
"
)]
#![cfg_attr(
not(feature = "format"),
doc = "
[`JSON`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.JSON.html
[`XML`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.XML.html
[`TOML`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.TOML.html
[`YAML`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.YAML.html
[`SQLite`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.SQLITE.html
[`Rust`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.RUST_LITERAL.html
[`Python`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.PYTHON_LITERAL.html
[`C#`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.CSHARP_LITERAL.html
[`FORTRAN`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.FORTRAN_LITERAL.html
[`COBOL`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.COBOL_LITERAL.html
"
)]
//!
//! ## Options API
//!
//! The Options API provides high-level options to specify number parsing
//! or writing, options not intrinsically tied to a number format.
//! For example, the Options API provides:
//! - The [`exponent`][OptionsBuilder::exponent] character (defaults to `b'e'`
//! or `b'^'`, depending on the radix).
//! - The [`decimal point`][OptionsBuilder::decimal_point] character (defaults
//! to `b'.'`).
//! - Custom [`NaN`][f64::NAN] and [`Infinity`][f64::INFINITY] string
//! [`representations`][Options::nan_string].
//!
//!
//! In addition, pre-defined constants for each category of options may
//! be found in their respective modules, for example, [`JSON`][`JSON-OPTS`].
//!
//! [`JSON-OPTS`]: options::JSON
//!
//! ## Examples
//!
//! An example of creating your own options to parse European-style
//! numbers (which use commas as decimal points, and periods as digit
//! separators) is as follows:
//!
//! ```
//! # #[cfg(feature = "format")] {
//! # use core::num;
//! use lexical_parse_float::{format, FromLexicalWithOptions, NumberFormatBuilder, Options};
//!
//! // This creates a format to parse a European-style float number.
//! // The decimal point is a comma, and the digit separators (optional)
//! // are periods.
//! const EUROPEAN: u128 = NumberFormatBuilder::new()
//! .digit_separator(num::NonZeroU8::new(b'.'))
//! .build_strict();
//! const COMMA_OPTIONS: Options = Options::builder()
//! .decimal_point(b',')
//! .build_strict();
//! assert_eq!(
//! f32::from_lexical_with_options::<EUROPEAN>(b"300,10", &COMMA_OPTIONS),
//! Ok(300.10)
//! );
//!
//! // Another example, using a pre-defined constant for JSON.
//! const JSON: u128 = format::JSON;
//! const JSON_OPTIONS: Options = Options::new();
//! assert_eq!(
//! f32::from_lexical_with_options::<JSON>(b"0e1", &JSON_OPTIONS),
//! Ok(0.0)
//! );
//! assert_eq!(
//! f32::from_lexical_with_options::<JSON>(b"1E+2", &JSON_OPTIONS),
//! Ok(100.0)
//! );
//! # }
//! ```
//!
//! # Higher-Level APIs
//!
//! If you would like an API that supports multiple numeric conversions rather
//! than just writing integers, use [`lexical`] or [`lexical-core`] instead.
//!
//! [`lexical`]: https://crates.io/crates/lexical
//! [`lexical-core`]: https://crates.io/crates/lexical-core
//!
//! # Version Support
//!
//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for
//! const generic support. Older versions of lexical support older Rust
//! versions.
//!
//! # Algorithm
//!
//! The default implementations are highly optimized both for simple
//! strings, as well as input with large numbers of digits. In order to
//! keep performance optimal for simple strings, we avoid overly branching
//! to minimize the number of branches (and therefore optimization checks).
//! Most of the branches in the code are resolved at compile-time, and
//! the resulting ASM as well as comprehensive benchmarks are monitored
//! to ensure there are no regressions.
//!
//! For simple floats, we use an optimized digit parser with multiple-digit
//! optimizations (parsing 8 digits in 3 multiplication instructions),
//! and then use machine floats to create an exact representation with
//! high throughput. In more complex cases, we use the Eisel-Lemire
//! algorithm, described in "Number Parsing at a Gigabyte per Second",
//! available online [here](https://arxiv.org/abs/2101.11408). The
//! Eisel-Lemire algorithm creates an extended representation using a
//! 128-bit (or a fallback 192-bit representation) of the significant
//! digits of the float, scaled to the proper exponent using pre-computed
//! powers-of-5.
//!
//! If the Eisel-Lemire algorithm is unable to unambiguously round the float,
//! we fallback to using optimized, big-integer algorithms, which are
//! described in [Algorithm Approach](#algorithm-approach) below.
//!
//! ## Machine Float-Only Algorithm
//!
//! We also support an algorithm that uses only machine floats for the
//! fast-path algorithm, however, this may be slower for floats with large
//! exponents since it uses an iterative algorithm. A code sample
//! using this is:
//!
//! ```rust
//! use lexical_parse_float::Options;
//! use lexical_parse_float::format::STANDARD;
//! use lexical_parse_float::parse::ParseFloat;
//!
//! const OPTIONS: Options = Options::new();
//! let result = f64::fast_path_complete::<{ STANDARD }>(b"1.34000", &OPTIONS);
//! assert_eq!(result, Ok(1.34000));
//! ```
//!
//! # Design
//!
//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md)
//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Benchmarks.md)
//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks)
//! - [Big Integer Implementation](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/BigInteger.md)
//!
//! # Safety Guarantees
//!
//! <div class="warning info-warning">
//! <style>
//! .info-warning::before {
//! color: #87CEFAb0 !important;
//! }
//! .info-warning {
//! border-left: 2px solid #87CEFAb0 !important;
//! }
//! </style>
//!
//! This module uses some unsafe code to achieve accept acceptable performance.
//! The safety guarantees and logic are described below.
//!
//! </div>
//!
//! The primary use of unsafe code is in the big integer implementation, which
//! for performance reasons requires unchecked indexing at certain points, where
//! rust cannot elide the index check. The use of unsafe code can be found in
//! the calculation of the [hi] bits, however, every invocation requires the
//! buffer to be of sufficient [length][longbits]. The other major source is the
//! implementation of methods such as [push_unchecked], however, the safety
//! invariants for each caller to create a safe API are documented and has
//! similar safety guarantees to a regular vector. All other invocations of
//! unsafe code are indexing a buffer where the index is proven to be within
//! bounds within a few lines of code of the unsafe index.
//!
//! [hi]: <https://github.com/Alexhuszagh/rust-lexical/blob/15d4c8c92d70b1fb9bd6d33f582ffe27e0e74f99/lexical-parse-float/src/bigint.rs#L266>
//! [longbits]: <https://github.com/Alexhuszagh/rust-lexical/blob/15d4c8c92d70b1fb9bd6d33f582ffe27e0e74f99/lexical-parse-float/src/bigint.rs#L550-L557>
//! [push_unchecked]: <https://github.com/Alexhuszagh/rust-lexical/blob/15d4c8c92d70b1fb9bd6d33f582ffe27e0e74f99/lexical-parse-float/src/bigint.rs#L377-L386>
//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html
// FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below
// Clippy reasons were stabilized in 1.81.0.
// We want to have the same safety guarantees as Rust core,
// so we allow unused unsafe to clearly document safety guarantees.
#![allow(unused_unsafe)]
#![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![deny(
clippy::doc_markdown,
clippy::unnecessary_safety_comment,
clippy::semicolon_if_nothing_returned,
clippy::unwrap_used,
clippy::as_underscore
)]
#![allow(
// used when concepts are logically separate
clippy::match_same_arms,
// loss of precision is intentional
clippy::integer_division,
// mathematical names use 1-character identifiers
clippy::min_ident_chars,
// these are not cryptographically secure contexts
clippy::integer_division_remainder_used,
// this can be intentional
clippy::module_name_repetitions,
// this is intentional: already passing a pointer and need performance
clippy::needless_pass_by_value,
// we use this for inline formatting for unsafe blocks
clippy::semicolon_inside_block,
)]
#[macro_use]
pub mod shared;
pub mod bellerophon;
pub mod bigint;
pub mod binary;
pub mod float;
pub mod fpu;
pub mod lemire;
pub mod libm;
pub mod limits;
pub mod mask;
pub mod number;
pub mod options;
pub mod parse;
pub mod slow;
pub mod table;
mod api;
mod table_bellerophon_decimal;
mod table_bellerophon_radix;
mod table_binary;
mod table_decimal;
mod table_large;
mod table_lemire;
mod table_radix;
mod table_small;
#[macro_use(parse_sign)]
extern crate lexical_parse_integer;
// Re-exports
#[cfg(feature = "f16")]
pub use lexical_util::bf16::bf16;
pub use lexical_util::error::Error;
#[cfg(feature = "f16")]
pub use lexical_util::f16::f16;
pub use lexical_util::format::{self, NumberFormat, NumberFormatBuilder};
pub use lexical_util::options::ParseOptions;
pub use lexical_util::result::Result;
pub use self::api::{FromLexical, FromLexicalWithOptions};
#[doc(inline)]
pub use self::options::{Options, OptionsBuilder};
File diff suppressed because it is too large Load Diff
+979
View File
@@ -0,0 +1,979 @@
//! Determine the limits of exact exponent and mantissas for floats.
#![doc(hidden)]
use lexical_util::assert::debug_assert_radix;
#[cfg(feature = "f16")]
use lexical_util::bf16::bf16;
#[cfg(feature = "f16")]
use lexical_util::f16::f16;
// EXACT EXPONENT
// --------------
// Calculating the exponent limit requires determining the largest exponent
// we can calculate for a radix that can be **exactly** store in the
// float type. If the value is a power-of-two, then we simply
// need to scale the minimum, denormal exp and maximum exp to the type
// size. Otherwise, we need to calculate the number of digits
// that can fit into the type's precision, after removing a power-of-two
// (since these values can be represented exactly).
//
// The mantissa limit is the number of digits we can remove from
// the exponent into the mantissa, and is therefore is the
// `⌊ precision / log2(radix) ⌋`, where precision does not include
// the hidden bit.
//
// The algorithm for calculating both `exponent_limit` and `mantissa_limit`,
// in Python, can be done as follows:
//
// DO NOT MODIFY: Generated by `src/etc/limits.py`
// EXACT FLOAT
// -----------
/// Get exact exponent limit for radix.
#[doc(hidden)]
pub trait ExactFloat {
/// Get min and max exponent limits (exact) from radix.
fn exponent_limit(radix: u32) -> (i64, i64);
/// Get the number of digits that can be shifted from exponent to mantissa.
fn mantissa_limit(radix: u32) -> i64;
}
impl ExactFloat for f32 {
#[inline(always)]
fn exponent_limit(radix: u32) -> (i64, i64) {
debug_assert_radix(radix);
f32_exponent_limit(radix)
}
#[inline(always)]
fn mantissa_limit(radix: u32) -> i64 {
debug_assert_radix(radix);
f32_mantissa_limit(radix)
}
}
impl ExactFloat for f64 {
#[inline(always)]
fn exponent_limit(radix: u32) -> (i64, i64) {
debug_assert_radix(radix);
f64_exponent_limit(radix)
}
#[inline(always)]
fn mantissa_limit(radix: u32) -> i64 {
debug_assert_radix(radix);
f64_mantissa_limit(radix)
}
}
#[cfg(feature = "f16")]
impl ExactFloat for f16 {
#[inline(always)]
fn exponent_limit(_: u32) -> (i64, i64) {
unimplemented!()
}
#[inline(always)]
fn mantissa_limit(_: u32) -> i64 {
unimplemented!()
}
}
#[cfg(feature = "f16")]
impl ExactFloat for bf16 {
#[inline(always)]
fn exponent_limit(_: u32) -> (i64, i64) {
unimplemented!()
}
#[inline(always)]
fn mantissa_limit(_: u32) -> i64 {
unimplemented!()
}
}
//#[cfg(feature = "f128")]
//impl ExactFloat for f128 {
// #[inline(always)]
// fn exponent_limit(radix: u32) -> (i64, i64) {
// debug_assert_radix(radix);
// f128_exponent_limit(radix)
// }
// }
//
// #[inline(always)]
// fn mantissa_limit(radix: u32) -> i64 {
// debug_assert_radix(radix);
// f128_mantissa_limit(radix)
// }
//}
// CONST FN
// --------
/// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "radix")]
pub const fn f32_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
2 => (-127, 127),
3 => (-15, 15),
4 => (-63, 63),
5 => (-10, 10),
6 => (-15, 15),
7 => (-8, 8),
8 => (-42, 42),
9 => (-7, 7),
10 => (-10, 10),
11 => (-6, 6),
12 => (-15, 15),
13 => (-6, 6),
14 => (-8, 8),
15 => (-6, 6),
16 => (-31, 31),
17 => (-5, 5),
18 => (-7, 7),
19 => (-5, 5),
20 => (-10, 10),
21 => (-5, 5),
22 => (-6, 6),
23 => (-5, 5),
24 => (-15, 15),
25 => (-5, 5),
26 => (-6, 6),
27 => (-5, 5),
28 => (-8, 8),
29 => (-4, 4),
30 => (-6, 6),
31 => (-4, 4),
32 => (-25, 25),
33 => (-4, 4),
34 => (-5, 5),
35 => (-4, 4),
36 => (-7, 7),
_ => (0, 0),
}
}
/// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn f32_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
2 => (-127, 127),
4 => (-63, 63),
8 => (-42, 42),
10 => (-10, 10),
16 => (-31, 31),
32 => (-25, 25),
_ => (0, 0),
}
}
/// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn f32_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
10 => (-10, 10),
_ => (0, 0),
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "radix")]
pub const fn f32_mantissa_limit(radix: u32) -> i64 {
match radix {
2 => 24,
3 => 15,
4 => 12,
5 => 10,
6 => 9,
7 => 8,
8 => 8,
9 => 7,
10 => 7,
11 => 6,
12 => 6,
13 => 6,
14 => 6,
15 => 6,
16 => 6,
17 => 5,
18 => 5,
19 => 5,
20 => 5,
21 => 5,
22 => 5,
23 => 5,
24 => 5,
25 => 5,
26 => 5,
27 => 5,
28 => 4,
29 => 4,
30 => 4,
31 => 4,
32 => 4,
33 => 4,
34 => 4,
35 => 4,
36 => 4,
_ => 0,
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn f32_mantissa_limit(radix: u32) -> i64 {
match radix {
2 => 24,
4 => 12,
8 => 8,
10 => 7,
16 => 6,
32 => 4,
_ => 0,
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn f32_mantissa_limit(radix: u32) -> i64 {
match radix {
10 => 7,
_ => 0,
}
}
/// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "radix")]
pub const fn f64_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
2 => (-1023, 1023),
3 => (-33, 33),
4 => (-511, 511),
5 => (-22, 22),
6 => (-33, 33),
7 => (-18, 18),
8 => (-341, 341),
9 => (-16, 16),
10 => (-22, 22),
11 => (-15, 15),
12 => (-33, 33),
13 => (-14, 14),
14 => (-18, 18),
15 => (-13, 13),
16 => (-255, 255),
17 => (-12, 12),
18 => (-16, 16),
19 => (-12, 12),
20 => (-22, 22),
21 => (-12, 12),
22 => (-15, 15),
23 => (-11, 11),
24 => (-33, 33),
25 => (-11, 11),
26 => (-14, 14),
27 => (-11, 11),
28 => (-18, 18),
29 => (-10, 10),
30 => (-13, 13),
31 => (-10, 10),
32 => (-204, 204),
33 => (-10, 10),
34 => (-12, 12),
35 => (-10, 10),
36 => (-16, 16),
_ => (0, 0),
}
}
// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn f64_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
2 => (-1023, 1023),
4 => (-511, 511),
8 => (-341, 341),
10 => (-22, 22),
16 => (-255, 255),
32 => (-204, 204),
_ => (0, 0),
}
}
/// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn f64_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
10 => (-22, 22),
_ => (0, 0),
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "radix")]
pub const fn f64_mantissa_limit(radix: u32) -> i64 {
match radix {
2 => 53,
3 => 33,
4 => 26,
5 => 22,
6 => 20,
7 => 18,
8 => 17,
9 => 16,
10 => 15,
11 => 15,
12 => 14,
13 => 14,
14 => 13,
15 => 13,
16 => 13,
17 => 12,
18 => 12,
19 => 12,
20 => 12,
21 => 12,
22 => 11,
23 => 11,
24 => 11,
25 => 11,
26 => 11,
27 => 11,
28 => 11,
29 => 10,
30 => 10,
31 => 10,
32 => 10,
33 => 10,
34 => 10,
35 => 10,
36 => 10,
_ => 0,
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn f64_mantissa_limit(radix: u32) -> i64 {
match radix {
2 => 53,
4 => 26,
8 => 17,
10 => 15,
16 => 13,
32 => 10,
_ => 0,
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn f64_mantissa_limit(radix: u32) -> i64 {
match radix {
10 => 15,
_ => 0,
}
}
/// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "f128")]
#[cfg(feature = "radix")]
pub const fn f128_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
2 => (-16494, 16383),
3 => (-71, 71),
4 => (-8247, 8191),
5 => (-48, 48),
6 => (-71, 71),
7 => (-40, 40),
8 => (-5498, 5461),
9 => (-35, 35),
10 => (-48, 48),
11 => (-32, 32),
12 => (-71, 71),
13 => (-30, 30),
14 => (-40, 40),
15 => (-28, 28),
16 => (-4123, 4095),
17 => (-27, 27),
18 => (-35, 35),
19 => (-26, 26),
20 => (-48, 48),
21 => (-25, 25),
22 => (-32, 32),
23 => (-24, 24),
24 => (-71, 71),
25 => (-24, 24),
26 => (-30, 30),
27 => (-23, 23),
28 => (-40, 40),
29 => (-23, 23),
30 => (-28, 28),
31 => (-22, 22),
32 => (-3298, 3276),
33 => (-22, 22),
34 => (-27, 27),
35 => (-22, 22),
36 => (-35, 35),
// Invalid radix
_ => (0, 0),
}
}
/// Get the exponent limit as a const fn.
#[inline(always)]
#[cfg(feature = "f128")]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn f128_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
2 => (-16494, 16383),
4 => (-8247, 8191),
8 => (-5498, 5461),
10 => (-48, 48),
16 => (-4123, 4095),
32 => (-3298, 3276),
// Invalid radix
_ => (0, 0),
}
}
/// Get the exponent limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "f128")]
#[cfg(not(feature = "power-of-two"))]
pub const fn f128_exponent_limit(radix: u32) -> (i64, i64) {
match radix {
10 => (-48, 48),
// Invalid radix
_ => (0, 0),
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "f128")]
#[cfg(feature = "radix")]
pub const fn f128_mantissa_limit(radix: u32) -> i64 {
match radix {
2 => 113,
3 => 71,
4 => 56,
5 => 48,
6 => 43,
7 => 40,
8 => 37,
9 => 35,
10 => 34,
11 => 32,
12 => 31,
13 => 30,
14 => 29,
15 => 28,
16 => 28,
17 => 27,
18 => 27,
19 => 26,
20 => 26,
21 => 25,
22 => 25,
23 => 24,
24 => 24,
25 => 24,
26 => 24,
27 => 23,
28 => 23,
29 => 23,
30 => 23,
31 => 22,
32 => 22,
33 => 22,
34 => 22,
35 => 22,
36 => 21,
// Invalid radix
_ => 0,
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "f128")]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn f128_mantissa_limit(radix: u32) -> i64 {
match radix {
2 => 113,
4 => 56,
8 => 37,
10 => 34,
16 => 28,
32 => 22,
// Invalid radix
_ => 0,
}
}
/// Get the mantissa limit as a const fn.
#[must_use]
#[inline(always)]
#[cfg(feature = "f128")]
#[cfg(not(feature = "power-of-two"))]
pub const fn f128_mantissa_limit(radix: u32) -> i64 {
match radix {
10 => 34,
// Invalid radix
_ => 0,
}
}
// POWER LIMITS
// ------------
// The code used to generate these limits is as follows:
//
// ```text
// import math
//
// def find_power(base, max_value):
// '''Using log is unreliable, since it uses float math.'''
//
// power = 0
// while base**power < max_value:
// power += 1
// return power - 1
//
// def print_function(bits):
// print('#[inline(always)]')
// print(f'pub const fn u{bits}_power_limit(radix: u32) -> u32 {{')
// print(' match radix {')
// max_value = 2**bits - 1
// for radix in range(2, 37):
// power = find_power(radix, max_value)
// print(f' {radix} => {power},')
// print(' // Any other radix should be unreachable.')
// print(' _ => 1,')
// print(' }')
// print('}')
// print('')
//
// print_function(32)
// print_function(64)
// ```
/// Get the maximum value for `radix^N` that can be represented in a u32.
/// This is calculated as `⌊log(2^32 - 1, b)⌋`.
#[must_use]
#[inline(always)]
#[cfg(feature = "radix")]
pub const fn u32_power_limit(radix: u32) -> u32 {
match radix {
2 => 31,
3 => 20,
4 => 15,
5 => 13,
6 => 12,
7 => 11,
8 => 10,
9 => 10,
10 => 9,
11 => 9,
12 => 8,
13 => 8,
14 => 8,
15 => 8,
16 => 7,
17 => 7,
18 => 7,
19 => 7,
20 => 7,
21 => 7,
22 => 7,
23 => 7,
24 => 6,
25 => 6,
26 => 6,
27 => 6,
28 => 6,
29 => 6,
30 => 6,
31 => 6,
32 => 6,
33 => 6,
34 => 6,
35 => 6,
36 => 6,
// Any other radix should be unreachable.
_ => 1,
}
}
/// This is calculated as `⌊log(2^32 - 1, b)⌋`.
#[must_use]
#[inline(always)]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn u32_power_limit(radix: u32) -> u32 {
match radix {
2 => 31,
4 => 15,
5 => 13,
8 => 10,
10 => 9,
16 => 7,
32 => 6,
// Any other radix should be unreachable.
_ => 1,
}
}
/// This is calculated as `⌊log(2^32 - 1, b)⌋`.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn u32_power_limit(radix: u32) -> u32 {
match radix {
5 => 13,
10 => 9,
// Any other radix should be unreachable.
_ => 1,
}
}
/// Get the maximum value for `radix^N` that can be represented in a u64.
/// This is calculated as `⌊log(2^64 - 1, b)⌋`.
#[must_use]
#[inline(always)]
#[cfg(feature = "radix")]
pub const fn u64_power_limit(radix: u32) -> u32 {
match radix {
2 => 63,
3 => 40,
4 => 31,
5 => 27,
6 => 24,
7 => 22,
8 => 21,
9 => 20,
10 => 19,
11 => 18,
12 => 17,
13 => 17,
14 => 16,
15 => 16,
16 => 15,
17 => 15,
18 => 15,
19 => 15,
20 => 14,
21 => 14,
22 => 14,
23 => 14,
24 => 13,
25 => 13,
26 => 13,
27 => 13,
28 => 13,
29 => 13,
30 => 13,
31 => 12,
32 => 12,
33 => 12,
34 => 12,
35 => 12,
36 => 12,
// Any other radix should be unreachable.
_ => 1,
}
}
/// Get the maximum value for `radix^N` that can be represented in a u64.
/// This is calculated as `⌊log(2^64 - 1, b)⌋`.
#[must_use]
#[inline(always)]
#[cfg(all(feature = "power-of-two", not(feature = "radix")))]
pub const fn u64_power_limit(radix: u32) -> u32 {
match radix {
2 => 63,
4 => 31,
5 => 27,
8 => 21,
10 => 19,
16 => 15,
32 => 12,
// Any other radix should be unreachable.
_ => 1,
}
}
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn u64_power_limit(radix: u32) -> u32 {
match radix {
5 => 27,
10 => 19,
// Any other radix should be unreachable.
_ => 1,
}
}
// MAX DIGITS
// ----------
/// Calculate the maximum number of digits possible in the mantissa.
///
/// Returns the maximum number of digits plus one.
///
/// We can exactly represent a float in radix `b` from radix 2 if
/// `b` is divisible by 2. This function calculates the exact number of
/// digits required to exactly represent that float. This makes sense,
/// and the exact reference and I quote is:
///
/// > A necessary and sufficient condition for all numbers representable in
/// > radix β
/// > with a finite number of digits to be representable in radix γ with a
/// > finite number of digits is that β should divide an integer power of γ.
///
/// According to the "Handbook of Floating Point Arithmetic",
/// for IEEE754, with `emin` being the min exponent, `p2` being the
/// precision, and `b` being the radix, the number of digits follows as:
///
/// `emin + p2 + ⌊(emin + 1) log(2, b) log(1 2^(p2), b)⌋`
///
/// For f16, this follows as:
/// emin = -14
/// p2 = 11
///
/// For bfloat16 , this follows as:
/// emin = -126
/// p2 = 8
///
/// For f32, this follows as:
/// emin = -126
/// p2 = 24
///
/// For f64, this follows as:
/// emin = -1022
/// p2 = 53
///
/// For f128, this follows as:
/// emin = -16382
/// p2 = 113
///
/// In Python:
/// `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2),
/// b))`
///
/// This was used to calculate the maximum number of digits for [2, 36].
///
/// The minimum, denormal exponent can be calculated as follows: given
/// the number of exponent bits `exp_bits`, and the number of bits
/// in the mantissa `mantissa_bits`, we have an exponent bias
/// `exp_bias` equal to `2^(exp_bits-1) - 1 + mantissa_bits`. We
/// therefore have a denormal exponent `denormal_exp` equal to
/// `1 - exp_bias` and the minimum, denormal float `min_float` is
/// therefore `2^denormal_exp`.
///
/// For f16, this follows as:
/// exp_bits = 5
/// mantissa_bits = 10
/// exp_bias = 25
/// denormal_exp = -24
/// min_float = 5.96 * 10^8
///
/// For bfloat16, this follows as:
/// exp_bits = 8
/// mantissa_bits = 7
/// exp_bias = 134
/// denormal_exp = -133
/// min_float = 9.18 * 10^41
///
/// For f32, this follows as:
/// exp_bits = 8
/// mantissa_bits = 23
/// exp_bias = 150
/// denormal_exp = -149
/// min_float = 1.40 * 10^45
///
/// For f64, this follows as:
/// exp_bits = 11
/// mantissa_bits = 52
/// exp_bias = 1075
/// denormal_exp = -1074
/// min_float = 5.00 * 10^324
///
/// For f128, this follows as:
/// exp_bits = 15
/// mantissa_bits = 112
/// exp_bias = 16495
/// denormal_exp = -16494
/// min_float = 6.48 * 10^4966
///
/// These match statements can be generated with the following Python
/// code:
/// ```python
/// import math
///
/// def digits(emin, p2, b):
/// return -emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))
///
/// def max_digits(emin, p2):
/// radices = [6, 10, 12, 14, 18, 20, 22, 24 26 28, 30, 34, 36]
/// print('match radix {')
/// for radix in radices:
/// value = digits(emin, p2, radix)
/// print(f' {radix} => Some({value + 2}),')
/// print(' // Powers of two should be unreachable.')
/// print(' // Odd numbers will have infinite digits.')
/// print(' _ => None,')
/// print('}')
/// ```
#[allow(clippy::doc_markdown)] // reason="not meant to be function parameters"
pub trait MaxDigits {
fn max_digits(radix: u32) -> Option<usize>;
}
/// emin = -126
/// p2 = 24
impl MaxDigits for f32 {
#[inline(always)]
fn max_digits(radix: u32) -> Option<usize> {
debug_assert_radix(radix);
f32_max_digits(radix)
}
}
/// emin = -1022
/// p2 = 53
impl MaxDigits for f64 {
#[inline(always)]
fn max_digits(radix: u32) -> Option<usize> {
debug_assert_radix(radix);
f64_max_digits(radix)
}
}
#[cfg(feature = "f16")]
impl MaxDigits for f16 {
#[inline(always)]
fn max_digits(_: u32) -> Option<usize> {
unimplemented!()
}
}
#[cfg(feature = "f16")]
impl MaxDigits for bf16 {
#[inline(always)]
fn max_digits(_: u32) -> Option<usize> {
unimplemented!()
}
}
///// `emin = -16382`
///// `p2 = 113`
//#[cfg(feature = "f128")]
//impl MaxDigits for f128 {
// #[inline(always)]
// fn max_digits(radix: u32) -> Option<usize> {
// match radix {
// 6 => Some(10159),
// 10 => Some(11565),
// 12 => Some(11927),
// 14 => Some(12194),
// 18 => Some(12568),
// 20 => Some(12706),
// 22 => Some(12823),
// 24 => Some(12924),
// 26 => Some(13012),
// 28 => Some(13089),
// 30 => Some(13158),
// 34 => Some(13277),
// 36 => Some(13328),
// // Powers of two should be unreachable.
// // Odd numbers will have infinite digits.
// _ => None,
// }
// }
//}
// CONST FN
// --------
/// Get the maximum number of significant digits as a const fn.
#[must_use]
#[inline(always)]
pub const fn f32_max_digits(radix: u32) -> Option<usize> {
match radix {
6 => Some(103),
10 => Some(114),
12 => Some(117),
14 => Some(119),
18 => Some(122),
20 => Some(123),
22 => Some(123),
24 => Some(124),
26 => Some(125),
28 => Some(125),
30 => Some(126),
34 => Some(127),
36 => Some(127),
// Powers of two should be unreachable.
// Odd numbers will have infinite digits.
_ => None,
}
}
/// Get the maximum number of significant digits as a const fn.
#[must_use]
#[inline(always)]
pub const fn f64_max_digits(radix: u32) -> Option<usize> {
match radix {
6 => Some(682),
10 => Some(769),
12 => Some(792),
14 => Some(808),
18 => Some(832),
20 => Some(840),
22 => Some(848),
24 => Some(854),
26 => Some(859),
28 => Some(864),
30 => Some(868),
34 => Some(876),
36 => Some(879),
// Powers of two should be unreachable.
// Odd numbers will have infinite digits.
_ => None,
}
}
+58
View File
@@ -0,0 +1,58 @@
//! Utilities to generate bitmasks.
#![doc(hidden)]
/// Generate a bitwise mask for the lower `n` bits.
///
/// # Examples
///
/// ```rust
/// # use lexical_parse_float::mask::lower_n_mask;
/// assert_eq!(lower_n_mask(2), 0b11);
/// ```
#[must_use]
#[inline(always)]
#[allow(clippy::match_bool)] // reason="easier to visualize logic"
pub const fn lower_n_mask(n: u64) -> u64 {
debug_assert!(n <= 64, "lower_n_mask() overflow in shl.");
match n == 64 {
true => u64::MAX,
false => (1 << n) - 1,
}
}
/// Calculate the halfway point for the lower `n` bits.
///
/// # Examples
///
/// ```rust
/// # use lexical_parse_float::mask::lower_n_halfway;
/// assert_eq!(lower_n_halfway(2), 0b10);
/// ```
#[must_use]
#[inline(always)]
#[allow(clippy::match_bool)] // reason="easier to visualize logic"
pub const fn lower_n_halfway(n: u64) -> u64 {
debug_assert!(n <= 64, "lower_n_halfway() overflow in shl.");
match n == 0 {
true => 0,
false => nth_bit(n - 1),
}
}
/// Calculate a scalar factor of 2 above the halfway point.
///
/// # Examples
///
/// ```rust
/// # use lexical_parse_float::mask::nth_bit;
/// assert_eq!(nth_bit(2), 0b100);
/// ```
#[must_use]
#[inline(always)]
pub const fn nth_bit(n: u64) -> u64 {
debug_assert!(n < 64, "nth_bit() overflow in shl.");
1 << n
}
+145
View File
@@ -0,0 +1,145 @@
//! Representation of a float as the significant digits and exponent.
//!
//! This is adapted from [fast-float-rust](https://github.com/aldanor/fast-float-rust),
//! a port of [fast_float](https://github.com/fastfloat/fast_float) to Rust.
#![doc(hidden)]
#![allow(clippy::exhaustive_structs)] // reason = "only public for testing"
use lexical_util::format::NumberFormat;
use crate::float::RawFloat;
use crate::fpu::set_precision;
/// Representation of a number as the significant digits and exponent.
///
/// This is only used if the exponent base and the significant digit
/// radix are the same, since we need to be able to move powers in and
/// out of the exponent.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Number<'a> {
/// The exponent of the float, scaled to the mantissa.
pub exponent: i64,
/// The significant digits of the float.
pub mantissa: u64,
/// If the float is negative.
pub is_negative: bool,
/// If the significant digits were truncated.
pub many_digits: bool,
/// The significant integer digits.
pub integer: &'a [u8],
/// The significant fraction digits.
pub fraction: Option<&'a [u8]>,
}
impl Number<'_> {
/// Detect if the float can be accurately reconstructed from native floats.
#[must_use]
#[inline(always)]
pub fn is_fast_path<F: RawFloat, const FORMAT: u128>(&self) -> bool {
let format = NumberFormat::<FORMAT> {};
debug_assert!(
format.mantissa_radix() == format.exponent_base(),
"fast path requires same radix"
);
F::min_exponent_fast_path(format.radix()) <= self.exponent
&& self.exponent <= F::max_exponent_disguised_fast_path(format.radix())
&& self.mantissa <= F::MAX_MANTISSA_FAST_PATH
&& !self.many_digits
}
/// The fast path algorithm using machine-sized integers and floats.
///
/// This is extracted into a separate function so that it can be attempted
/// before constructing a Decimal. This only works if both the mantissa
/// and the exponent can be exactly represented as a machine float,
/// since IEE-754 guarantees no rounding will occur.
///
/// There is an exception: disguised fast-path cases, where we can shift
/// powers-of-10 from the exponent to the significant digits.
// `set_precision` doesn't return a unit value on x87 FPUs.
#[must_use]
#[allow(clippy::missing_inline_in_public_items)] // reason = "only public for testing"
#[allow(clippy::let_unit_value)] // reason = "intentional ASM drop for X87 FPUs"
pub fn try_fast_path<F: RawFloat, const FORMAT: u128>(&self) -> Option<F> {
let format = NumberFormat::<FORMAT> {};
debug_assert!(
format.mantissa_radix() == format.exponent_base(),
"fast path requires same radix"
);
// The fast path crucially depends on arithmetic being rounded to the correct
// number of bits without any intermediate rounding. On x86 (without SSE
// or SSE2) this requires the precision of the x87 FPU stack to be
// changed so that it directly rounds to 64/32 bit. The `set_precision`
// function takes care of setting the precision on architectures which
// require setting it by changing the global state (like the control word of the
// x87 FPU).
let _cw = set_precision::<F>();
if self.is_fast_path::<F, FORMAT>() {
let radix = format.radix();
let max_exponent = F::max_exponent_fast_path(radix);
let mut value = if self.exponent <= max_exponent {
// normal fast path
let value = F::as_cast(self.mantissa);
if self.exponent < 0 {
value / F::pow_fast_path((-self.exponent) as usize, radix)
} else {
value * F::pow_fast_path(self.exponent as usize, radix)
}
} else {
// disguised fast path
let shift = self.exponent - max_exponent;
let int_power = F::int_pow_fast_path(shift as usize, radix);
let mantissa = self.mantissa.checked_mul(int_power)?;
if mantissa > F::MAX_MANTISSA_FAST_PATH {
return None;
}
F::as_cast(mantissa) * F::pow_fast_path(max_exponent as usize, radix)
};
if self.is_negative {
value = -value;
}
Some(value)
} else {
None
}
}
/// Force a fast-path algorithm, even when it may not be accurate.
// `set_precision` doesn't return a unit value on x87 FPUs.
#[must_use]
#[allow(clippy::missing_inline_in_public_items)] // reason = "only public for testing"
#[allow(clippy::let_unit_value)] // reason = "intentional ASM drop for X87 FPUs"
pub fn force_fast_path<F: RawFloat, const FORMAT: u128>(&self) -> F {
let format = NumberFormat::<FORMAT> {};
debug_assert!(
format.mantissa_radix() == format.exponent_base(),
"fast path requires same radix"
);
let _cw = set_precision::<F>();
let radix = format.radix();
let mut value = F::as_cast(self.mantissa);
let max_exponent = F::max_exponent_fast_path(radix);
let mut exponent = self.exponent.abs();
if self.exponent < 0 {
while exponent > max_exponent {
value /= F::pow_fast_path(max_exponent as usize, radix);
exponent -= max_exponent;
}
value /= F::pow_fast_path(exponent as usize, radix);
} else {
while exponent > max_exponent {
value *= F::pow_fast_path(max_exponent as usize, radix);
exponent -= max_exponent;
}
value *= F::pow_fast_path(exponent as usize, radix);
}
if self.is_negative {
value = -value;
}
value
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+286
View File
@@ -0,0 +1,286 @@
//! Shared utilities and algorithms.
#![doc(hidden)]
#[cfg(feature = "power-of-two")]
use lexical_util::format::NumberFormat;
use lexical_util::num::AsPrimitive;
use crate::float::{ExtendedFloat80, RawFloat};
use crate::mask::{lower_n_halfway, lower_n_mask};
// 8 DIGIT
// -------
/// Check if we can try to parse 8 digits at one.
#[cfg(not(feature = "compact"))]
macro_rules! can_try_parse_multidigit {
($iter:expr, $radix:expr) => {
$iter.is_contiguous() && (cfg!(not(feature = "power-of-two")) || $radix <= 10)
};
}
// POWER2
// ------
/// Calculate the shift to move the significant digits into place.
#[inline(always)]
pub fn calculate_shift<F: RawFloat>(power2: i32) -> i32 {
let mantissa_shift = 64 - F::MANTISSA_SIZE - 1;
if -power2 >= mantissa_shift {
-power2 + 1
} else {
mantissa_shift
}
}
/// Calculate the biased, binary exponent from the mantissa shift and exponent.
#[inline(always)]
#[cfg(feature = "power-of-two")]
pub fn calculate_power2<F: RawFloat, const FORMAT: u128>(exponent: i64, ctlz: u32) -> i32 {
let format = NumberFormat::<{ FORMAT }> {};
exponent as i32 * log2(format.exponent_base()) + F::EXPONENT_BIAS - ctlz as i32
}
/// Bias for marking an invalid extended float.
pub const INVALID_FP: i32 = i16::MIN as i32;
// LOG2
// ----
/// Quick log2 that evaluates at compile time for the radix.
/// Note that this may produce inaccurate results for other radixes:
/// we don't care since it's only called for powers-of-two.
#[inline(always)]
pub const fn log2(radix: u32) -> i32 {
match radix {
2 => 1,
4 => 2,
8 => 3,
16 => 4,
32 => 5,
// Fallthrough to 1 for non-power-of-two radixes.
_ => 1,
}
}
// STARTS WITH
// -----------
/// Check if left iter starts with right iter.
///
/// This optimizes decently well, to the following ASM for pure slices:
///
/// ```text
/// starts_with_slc:
/// xor eax, eax
/// .LBB0_1:
/// cmp rcx, rax
/// je .LBB0_2
/// cmp rsi, rax
/// je .LBB0_5
/// movzx r8d, byte ptr [rdi + rax]
/// lea r9, [rax + 1]
/// cmp r8b, byte ptr [rdx + rax]
/// mov rax, r9
/// je .LBB0_1
/// .LBB0_5:
/// xor eax, eax
/// ret
/// .LBB0_2:
/// mov al, 1
/// ret
/// ```
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn starts_with<'a, 'b, Iter1, Iter2>(mut x: Iter1, mut y: Iter2) -> bool
where
Iter1: Iterator<Item = &'a u8>,
Iter2: Iterator<Item = &'b u8>,
{
loop {
// Only call `next()` on x if y is not None, otherwise,
// we may incorrectly consume an x character.
let yi = y.next();
if yi.is_none() {
return true;
} else if x.next() != yi {
return false;
}
}
}
/// Check if left iter starts with right iter without case-sensitivity.
///
/// This optimizes decently well, to the following ASM for pure slices:
///
/// ```text
/// starts_with_uncased:
/// xor eax, eax
/// .LBB1_1:
/// cmp rcx, rax
/// je .LBB1_2
/// cmp rsi, rax
/// je .LBB1_5
/// movzx r8d, byte ptr [rdi + rax]
/// xor r8b, byte ptr [rdx + rax]
/// add rax, 1
/// test r8b, -33
/// je .LBB1_1
/// .LBB1_5:
/// xor eax, eax
/// ret
/// .LBB1_2:
/// mov al, 1
/// ret
/// ```
#[cfg_attr(not(feature = "compact"), inline(always))]
#[allow(clippy::unwrap_used)] // reason="yi cannot be none due to previous check"
pub fn starts_with_uncased<'a, 'b, Iter1, Iter2>(mut x: Iter1, mut y: Iter2) -> bool
where
Iter1: Iterator<Item = &'a u8>,
Iter2: Iterator<Item = &'b u8>,
{
// We use a faster optimization here for ASCII letters, which NaN
// and infinite strings **must** be. [A-Z] is 0x41-0x5A, while
// [a-z] is 0x61-0x7A. Therefore, the xor must be 0 or 32 if they
// are case-insensitive equal, but only if at least 1 of the inputs
// is an ASCII letter.
loop {
let yi = y.next();
if yi.is_none() {
return true;
}
let yi = *yi.unwrap();
let is_not_equal = x.next().map_or(true, |&xi| {
let xor = xi ^ yi;
xor != 0 && xor != 0x20
});
if is_not_equal {
return false;
}
}
}
// ROUNDING
// --------
/// Round an extended-precision float to the nearest machine float.
///
/// Shifts the significant digits into place, adjusts the exponent,
/// so it can be easily converted to a native float.
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn round<F, Cb>(fp: &mut ExtendedFloat80, cb: Cb)
where
F: RawFloat,
Cb: Fn(&mut ExtendedFloat80, i32),
{
let fp_inf = ExtendedFloat80 {
mant: 0,
exp: F::INFINITE_POWER,
};
// Calculate our shift in significant digits.
let mantissa_shift = 64 - F::MANTISSA_SIZE - 1;
// Check for a denormal float, if after the shift the exponent is negative.
if -fp.exp >= mantissa_shift {
// Have a denormal float that isn't a literal 0.
// The extra 1 is to adjust for the denormal float, which is
// `1 - F::EXPONENT_BIAS`. This works as before, because our
// old logic rounded to `F::DENORMAL_EXPONENT` (now 1), and then
// checked if `exp == F::DENORMAL_EXPONENT` and no hidden mask
// bit was set. Here, we handle that here, rather than later.
//
// This might round-down to 0, but shift will be at **max** 65,
// for halfway cases rounding towards 0.
let shift = -fp.exp + 1;
debug_assert!(shift <= 65);
cb(fp, shift.min(64));
// Check for round-up: if rounding-nearest carried us to the hidden bit.
fp.exp = (fp.mant >= F::HIDDEN_BIT_MASK.as_u64()) as i32;
return;
}
// The float is normal, round to the hidden bit.
cb(fp, mantissa_shift);
// Check if we carried, and if so, shift the bit to the hidden bit.
let carry_mask = F::CARRY_MASK.as_u64();
if fp.mant & carry_mask == carry_mask {
fp.mant >>= 1;
fp.exp += 1;
}
// Handle if we carried and check for overflow again.
if fp.exp >= F::INFINITE_POWER {
// Exponent is above largest normal value, must be infinite.
*fp = fp_inf;
return;
}
// Remove the hidden bit.
fp.mant &= F::MANTISSA_MASK.as_u64();
}
/// Shift right N-bytes and round towards a direction.
///
/// Callback should take the following parameters:
/// 1. `is_odd`
/// 1. `is_halfway`
/// 1. `is_above`
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn round_nearest_tie_even<Cb>(fp: &mut ExtendedFloat80, shift: i32, cb: Cb)
where
// `is_odd`, `is_halfway`, `is_above`
Cb: Fn(bool, bool, bool) -> bool,
{
// Ensure we've already handled denormal values that underflow.
debug_assert!(shift <= 64);
// Extract the truncated bits using mask.
// Calculate if the value of the truncated bits are either above
// the mid-way point, or equal to it.
//
// For example, for 4 truncated bytes, the mask would be 0b1111
// and the midway point would be 0b1000.
let mask = lower_n_mask(shift as u64);
let halfway = lower_n_halfway(shift as u64);
let truncated_bits = fp.mant & mask;
let is_above = truncated_bits > halfway;
let is_halfway = truncated_bits == halfway;
// Bit shift so the leading bit is in the hidden bit.
// This optimizes pretty well:
// ```text
// mov ecx, esi
// shr rdi, cl
// xor eax, eax
// cmp esi, 64
// cmovne rax, rdi
// ret
// ```
fp.mant = match shift == 64 {
true => 0,
false => fp.mant >> shift,
};
fp.exp += shift;
// Extract the last bit after shifting (and determine if it is odd).
let is_odd = fp.mant & 1 == 1;
// Calculate if we need to roundup.
// We need to roundup if we are above halfway, or if we are odd
// and at half-way (need to tie-to-even). Avoid the branch here.
fp.mant += cb(is_odd, is_halfway, is_above) as u64;
}
/// Round our significant digits into place, truncating them.
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn round_down(fp: &mut ExtendedFloat80, shift: i32) {
// Might have a shift greater than 64 if we have an error.
fp.mant = match shift == 64 {
true => 0,
false => fp.mant >> shift,
};
fp.exp += shift;
}
+838
View File
@@ -0,0 +1,838 @@
//! Slow, fallback cases where we cannot unambiguously round a float.
//!
//! This occurs when we cannot determine the exact representation using
//! both the fast path (native) cases nor the Lemire/Bellerophon algorithms,
//! and therefore must fallback to a slow, arbitrary-precision representation.
#![doc(hidden)]
use core::cmp;
#[cfg(not(feature = "compact"))]
use lexical_parse_integer::algorithm;
use lexical_util::digit::char_to_valid_digit_const;
#[cfg(feature = "radix")]
use lexical_util::digit::digit_to_char_const;
use lexical_util::format::NumberFormat;
use lexical_util::iterator::{AsBytes, DigitsIter, Iter};
use lexical_util::num::{AsPrimitive, Integer};
#[cfg(feature = "radix")]
use crate::bigint::Bigfloat;
use crate::bigint::{Bigint, Limb};
use crate::float::{extended_to_float, ExtendedFloat80, RawFloat};
use crate::limits::{u32_power_limit, u64_power_limit};
use crate::number::Number;
use crate::shared;
// ALGORITHM
// ---------
/// Parse the significant digits and biased, binary exponent of a float.
///
/// This is a fallback algorithm that uses a big-integer representation
/// of the float, and therefore is considerably slower than faster
/// approximations. However, it will always determine how to round
/// the significant digits to the nearest machine float, allowing
/// use to handle near half-way cases.
///
/// Near half-way cases are halfway between two consecutive machine floats.
/// For example, the float `16777217.0` has a bitwise representation of
/// `100000000000000000000000 1`. Rounding to a single-precision float,
/// the trailing `1` is truncated. Using round-nearest, tie-even, any
/// value above `16777217.0` must be rounded up to `16777218.0`, while
/// any value before or equal to `16777217.0` must be rounded down
/// to `16777216.0`. These near-halfway conversions therefore may require
/// a large number of digits to unambiguously determine how to round.
#[must_use]
#[allow(clippy::unwrap_used)] // reason = "none is a developer error"
pub fn slow_radix<F: RawFloat, const FORMAT: u128>(
num: Number,
fp: ExtendedFloat80,
) -> ExtendedFloat80 {
// Ensure our preconditions are valid:
// 1. The significant digits are not shifted into place.
debug_assert!(fp.mant & (1 << 63) != 0, "number must be normalized");
let format = NumberFormat::<{ FORMAT }> {};
// This assumes the sign bit has already been parsed, and we're
// starting with the integer digits, and the float format has been
// correctly validated.
let sci_exp = scientific_exponent::<FORMAT>(&num);
// We have 3 major algorithms we use for this:
// 1. An algorithm with a finite number of digits and a positive exponent.
// 2. An algorithm with a finite number of digits and a negative exponent.
// 3. A fallback algorithm with a non-finite number of digits.
// In order for a float in radix `b` with a finite number of digits
// to have a finite representation in radix `y`, `b` should divide
// an integer power of `y`. This means for binary, all even radixes
// have finite representations, and all odd ones do not.
#[cfg(feature = "radix")]
{
if let Some(max_digits) = F::max_digits(format.radix()) {
// Can use our finite number of digit algorithm.
digit_comp::<F, FORMAT>(num, fp, sci_exp, max_digits)
} else {
// Fallback to infinite digits.
byte_comp::<F, FORMAT>(num, fp, sci_exp)
}
}
#[cfg(not(feature = "radix"))]
{
// Can use our finite number of digit algorithm.
let max_digits = F::max_digits(format.radix()).unwrap();
digit_comp::<F, FORMAT>(num, fp, sci_exp, max_digits)
}
}
/// Algorithm that generates the mantissa for a finite representation.
///
/// For a positive exponent relative to the significant digits, this
/// is just a multiplication by an exponent power. For a negative
/// exponent relative to the significant digits, we scale the real
/// digits to the theoretical digits for `b` and determine if we
/// need to round-up.
#[must_use]
#[inline(always)]
#[allow(clippy::cast_possible_wrap)] // reason = "the value range is [-324, 308]"
pub fn digit_comp<F: RawFloat, const FORMAT: u128>(
num: Number,
fp: ExtendedFloat80,
sci_exp: i32,
max_digits: usize,
) -> ExtendedFloat80 {
let (bigmant, digits) = parse_mantissa::<FORMAT>(num, max_digits);
// This can't underflow, since `digits` is at most `max_digits`.
let exponent = sci_exp + 1 - digits as i32;
if exponent >= 0 {
positive_digit_comp::<F, FORMAT>(bigmant, exponent)
} else {
negative_digit_comp::<F, FORMAT>(bigmant, fp, exponent)
}
}
/// Generate the significant digits with a positive exponent relative to
/// mantissa.
#[must_use]
#[inline(always)]
#[allow(clippy::unwrap_used)] // reason = "none is a developer error"
#[allow(clippy::cast_possible_wrap)] // reason = "can't wrap in practice: max is ~1000 limbs"
#[allow(clippy::missing_inline_in_public_items)] // reason = "only public for testing"
pub fn positive_digit_comp<F: RawFloat, const FORMAT: u128>(
mut bigmant: Bigint,
exponent: i32,
) -> ExtendedFloat80 {
let format = NumberFormat::<{ FORMAT }> {};
// Simple, we just need to multiply by the power of the radix.
// Now, we can calculate the mantissa and the exponent from this.
// The binary exponent is the binary exponent for the mantissa
// shifted to the hidden bit.
bigmant.pow(format.radix(), exponent as u32).unwrap();
// Get the exact representation of the float from the big integer.
// hi64 checks **all** the remaining bits after the mantissa,
// so it will check if **any** truncated digits exist.
let (mant, is_truncated) = bigmant.hi64();
let exp = bigmant.bit_length() as i32 - 64 + F::EXPONENT_BIAS;
let mut fp = ExtendedFloat80 {
mant,
exp,
};
// Shift the digits into position and determine if we need to round-up.
shared::round::<F, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| {
is_above || (is_halfway && is_truncated) || (is_odd && is_halfway)
});
});
fp
}
/// Generate the significant digits with a negative exponent relative to
/// mantissa.
///
/// This algorithm is quite simple: we have the significant digits `m1 * b^N1`,
/// where `m1` is the bigint mantissa, `b` is the radix, and `N1` is the radix
/// exponent. We then calculate the theoretical representation of `b+h`, which
/// is `m2 * 2^N2`, where `m2` is the bigint mantissa and `N2` is the binary
/// exponent. If we had infinite, efficient floating precision, this would be
/// equal to `m1 / b^-N1` and then compare it to `m2 * 2^N2`.
///
/// Since we cannot divide and keep precision, we must multiply the other:
/// if we want to do `m1 / b^-N1 >= m2 * 2^N2`, we can do
/// `m1 >= m2 * b^-N1 * 2^N2` Going to the decimal case, we can show and example
/// and simplify this further: `m1 >= m2 * 2^N2 * 10^-N1`. Since we can remove
/// a power-of-two, this is `m1 >= m2 * 2^(N2 - N1) * 5^-N1`. Therefore, if
/// `N2 - N1 > 0`, we need have `m1 >= m2 * 2^(N2 - N1) * 5^-N1`, otherwise,
/// we have `m1 * 2^(N1 - N2) >= m2 * 5^-N1`, where the resulting exponents
/// are all positive.
///
/// This allows us to compare both floats using integers efficiently
/// without any loss of precision.
#[must_use]
#[inline(always)]
#[allow(clippy::match_bool)] // reason = "simplifies documentation"
#[allow(clippy::unwrap_used)] // reason = "unwrap panics if a developer error"
#[allow(clippy::comparison_chain)] // reason = "logically different conditions for algorithm"
#[allow(clippy::missing_inline_in_public_items)] // reason = "only exposed for unittesting"
pub fn negative_digit_comp<F: RawFloat, const FORMAT: u128>(
bigmant: Bigint,
mut fp: ExtendedFloat80,
exponent: i32,
) -> ExtendedFloat80 {
// Ensure our preconditions are valid:
// 1. The significant digits are not shifted into place.
debug_assert!(fp.mant & (1 << 63) != 0, "the significant digits must be normalized");
let format = NumberFormat::<FORMAT> {};
let radix = format.radix();
// Get the significant digits and radix exponent for the real digits.
let mut real_digits = bigmant;
let real_exp = exponent;
debug_assert!(real_exp < 0, "algorithm only works with negative numbers");
// Round down our extended-precision float and calculate `b`.
let mut b = fp;
shared::round::<F, _>(&mut b, shared::round_down);
let b = extended_to_float::<F>(b);
// Get the significant digits and the binary exponent for `b+h`.
let theor = bh(b);
let mut theor_digits = Bigint::from_u64(theor.mant);
let theor_exp = theor.exp;
// We need to scale the real digits and `b+h` digits to be the same
// order. We currently have `real_exp`, in `radix`, that needs to be
// shifted to `theor_digits` (since it is negative), and `theor_exp`
// to either `theor_digits` or `real_digits` as a power of 2 (since it
// may be positive or negative). Try to remove as many powers of 2
// as possible. All values are relative to `theor_digits`, that is,
// reflect the power you need to multiply `theor_digits` by.
let (binary_exp, halfradix_exp, radix_exp) = match radix.is_even() {
// Can remove a power-of-two.
// Both are on opposite-sides of equation, can factor out a
// power of two.
//
// Example: 10^-10, 2^-10 -> ( 0, 10, 0)
// Example: 10^-10, 2^-15 -> (-5, 10, 0)
// Example: 10^-10, 2^-5 -> ( 5, 10, 0)
// Example: 10^-10, 2^5 -> (15, 10, 0)
true => (theor_exp - real_exp, -real_exp, 0),
// Cannot remove a power-of-two.
false => (theor_exp, 0, -real_exp),
};
if halfradix_exp != 0 {
theor_digits.pow(radix / 2, halfradix_exp as u32).unwrap();
}
if radix_exp != 0 {
theor_digits.pow(radix, radix_exp as u32).unwrap();
}
if binary_exp > 0 {
theor_digits.pow(2, binary_exp as u32).unwrap();
} else if binary_exp < 0 {
real_digits.pow(2, (-binary_exp) as u32).unwrap();
}
// Compare our theoretical and real digits and round nearest, tie even.
let ord = real_digits.data.cmp(&theor_digits.data);
shared::round::<F, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |is_odd, _, _| {
// Can ignore `is_halfway` and `is_above`, since those were
// calculates using less significant digits.
match ord {
cmp::Ordering::Greater => true,
cmp::Ordering::Less => false,
cmp::Ordering::Equal if is_odd => true,
cmp::Ordering::Equal => false,
}
});
});
fp
}
/// Try to parse 8 digits at a time.
///
/// - `format` - The numerical format specification as a packed 128-bit integer
/// - `iter` - An iterator over all bytes in the buffer
/// - `value` - The currently parsed value.
/// - `count` - The total number of parsed digits
/// - `counter` - The number of parsed digits since creating the current u32
/// - `step` - The maximum number of digits for the radix that can fit in a u32.
/// - `max_digits` - The maximum number of digits that can affect floating-point
/// rounding.
#[cfg(not(feature = "compact"))]
macro_rules! try_parse_8digits {
(
$format:ident,
$iter:ident,
$value:ident,
$count:ident,
$counter:ident,
$step:ident,
$max_digits:ident
) => {{
let format = NumberFormat::<$format> {};
let radix = format.radix() as Limb;
// Try 8-digit optimizations.
if can_try_parse_multidigit!($iter, radix) {
debug_assert!(radix < 16);
let radix8 = format.radix8() as Limb;
while $step - $counter >= 8 && $max_digits - $count >= 8 {
if let Some(v) = algorithm::try_parse_8digits::<Limb, _, FORMAT>(&mut $iter) {
$value = $value.wrapping_mul(radix8).wrapping_add(v);
$counter += 8;
$count += 8;
} else {
break;
}
}
}
}};
}
/// Add a digit to the temporary value.
///
/// - `c` - The character to convert to a digit.
/// - `value` - The currently parsed value.
/// - `count` - The total number of parsed digits
/// - `counter` - The number of parsed digits since creating the current u32
macro_rules! add_digit {
($c:ident, $radix:ident, $value:ident, $counter:ident, $count:ident) => {{
let digit = char_to_valid_digit_const($c, $radix);
$value *= $radix as Limb;
$value += digit as Limb;
// Increment our counters.
$counter += 1;
$count += 1;
}};
}
/// Add a temporary value to our mantissa.
///
/// - `format` - The numerical format specification as a packed 128-bit integer
/// - `result` - The big integer,
/// - `power` - The power to scale the big integer by.
/// - `value` - The value to add to the big integer,
/// - `counter` - The number of parsed digits since creating the current u32
macro_rules! add_temporary {
// Multiply by the small power and add the native value.
(@mul $result:ident, $power:expr, $value:expr) => {
$result.data.mul_small($power).unwrap();
$result.data.add_small($value).unwrap();
};
// Add a temporary where we won't read the counter results internally.
(@end $format:ident, $result:ident, $counter:ident, $value:ident) => {
if $counter != 0 {
let small_power = f64::int_pow_fast_path($counter, $format.radix());
add_temporary!(@mul $result, small_power as Limb, $value);
}
};
// Add the maximum native value.
(@max $format:ident, $result:ident, $counter:ident, $value:ident, $max:ident) => {
add_temporary!(@mul $result, $max, $value);
$counter = 0;
$value = 0;
};
}
/// Round-up a truncated value.
///
/// - `format` - The numerical format specification as a packed 128-bit integer
/// - `result` - The big integer,
/// - `count` - The total number of parsed digits
macro_rules! round_up_truncated {
($format:ident, $result:ident, $count:ident) => {{
// Need to round-up.
// Can't just add 1, since this can accidentally round-up
// values to a halfway point, which can cause invalid results.
add_temporary!(@mul $result, $format.radix() as Limb, 1);
$count += 1;
}};
}
/// Check and round-up the fraction if any non-zero digits exist.
///
/// - `format` - The numerical format specification as a packed 128-bit integer
/// - `iter` - An iterator over all bytes in the buffer
/// - `result` - The big integer,
/// - `count` - The total number of parsed digits
macro_rules! round_up_nonzero {
($format:ident, $iter:expr, $result:ident, $count:ident) => {{
// NOTE: All digits must already be valid.
let mut iter = $iter;
// First try reading 8-digits at a time.
if iter.is_contiguous() {
while let Some(value) = iter.peek_u64() {
// SAFETY: safe since we have at least 8 bytes in the buffer.
unsafe { iter.step_by_unchecked(8) };
if value != 0x3030_3030_3030_3030 {
// Have non-zero digits, exit early.
round_up_truncated!($format, $result, $count);
return ($result, $count);
}
}
}
for &digit in iter {
if digit != b'0' {
round_up_truncated!($format, $result, $count);
return ($result, $count);
}
}
}};
}
/// Parse the full mantissa into a big integer.
///
/// Returns the parsed mantissa and the number of digits in the mantissa.
/// The max digits is the maximum number of digits plus one.
#[must_use]
#[allow(clippy::cognitive_complexity)] // reason = "complexity broken into macros"
#[allow(clippy::missing_inline_in_public_items)] // reason = "only public for testing"
pub fn parse_mantissa<const FORMAT: u128>(num: Number, max_digits: usize) -> (Bigint, usize) {
let format = NumberFormat::<FORMAT> {};
let radix = format.radix();
// Iteratively process all the data in the mantissa.
// We do this via small, intermediate values which once we reach
// the maximum number of digits we can process without overflow,
// we add the temporary to the big integer.
let mut counter: usize = 0;
let mut count: usize = 0;
let mut value: Limb = 0;
let mut result = Bigint::new();
// Now use our pre-computed small powers iteratively.
let step = if Limb::BITS == 32 {
u32_power_limit(format.radix())
} else {
u64_power_limit(format.radix())
} as usize;
let max_native = (format.radix() as Limb).pow(step as u32);
// Process the integer digits.
let mut integer = num.integer.bytes::<FORMAT>();
let mut integer_iter = integer.integer_iter();
integer_iter.skip_zeros();
'integer: loop {
#[cfg(not(feature = "compact"))]
try_parse_8digits!(FORMAT, integer_iter, value, count, counter, step, max_digits);
// Parse a digit at a time, until we reach step.
while counter < step && count < max_digits {
if let Some(&c) = integer_iter.next() {
add_digit!(c, radix, value, counter, count);
} else {
break 'integer;
}
}
// Check if we've exhausted our max digits.
if count == max_digits {
// Need to check if we're truncated, and round-up accordingly.
// SAFETY: safe since `counter <= step`.
add_temporary!(@end format, result, counter, value);
round_up_nonzero!(format, integer_iter, result, count);
if let Some(fraction) = num.fraction {
let mut fraction = fraction.bytes::<FORMAT>();
round_up_nonzero!(format, fraction.fraction_iter(), result, count);
}
return (result, count);
} else {
// Add our temporary from the loop.
// SAFETY: safe since `counter <= step`.
add_temporary!(@max format, result, counter, value, max_native);
}
}
// Process the fraction digits.
if let Some(fraction) = num.fraction {
let mut fraction = fraction.bytes::<FORMAT>();
let mut fraction_iter = fraction.integer_iter();
if count == 0 {
// No digits added yet, can skip leading fraction zeros too.
fraction_iter.skip_zeros();
}
'fraction: loop {
#[cfg(not(feature = "compact"))]
try_parse_8digits!(FORMAT, fraction_iter, value, count, counter, step, max_digits);
// Parse a digit at a time, until we reach step.
while counter < step && count < max_digits {
if let Some(&c) = fraction_iter.next() {
add_digit!(c, radix, value, counter, count);
} else {
break 'fraction;
}
}
// Check if we've exhausted our max digits.
if count == max_digits {
// SAFETY: safe since `counter <= step`.
add_temporary!(@end format, result, counter, value);
round_up_nonzero!(format, fraction_iter, result, count);
return (result, count);
} else {
// Add our temporary from the loop.
// SAFETY: safe since `counter <= step`.
add_temporary!(@max format, result, counter, value, max_native);
}
}
}
// We will always have a remainder, as long as we entered the loop
// once, or counter % step is 0.
// SAFETY: safe since `counter <= step`.
add_temporary!(@end format, result, counter, value);
(result, count)
}
/// Compare actual integer digits to the theoretical digits.
///
/// - `iter` - An iterator over all bytes in the buffer
/// - `num` - The actual digits of the real floating point number.
/// - `den` - The theoretical digits created by `b+h` to determine if `b` or
/// `b+1`
#[cfg(feature = "radix")]
macro_rules! integer_compare {
($iter:ident, $num:ident, $den:ident, $radix:ident) => {{
// Compare the integer digits.
while !$num.data.is_empty() {
// All digits **must** be valid.
let actual = match $iter.next() {
Some(&v) => v,
// Could have hit the decimal point.
_ => break,
};
let rem = $num.data.quorem(&$den.data) as u32;
let expected = digit_to_char_const(rem, $radix);
$num.data.mul_small($radix as Limb).unwrap();
if actual < expected {
return cmp::Ordering::Less;
} else if actual > expected {
return cmp::Ordering::Greater;
}
}
// Still have integer digits, check if any are non-zero.
if $num.data.is_empty() {
for &digit in $iter {
if digit != b'0' {
return cmp::Ordering::Greater;
}
}
}
}};
}
/// Compare actual fraction digits to the theoretical digits.
///
/// - `iter` - An iterator over all bytes in the buffer
/// - `num` - The actual digits of the real floating point number.
/// - `den` - The theoretical digits created by `b+h` to determine if `b` or
/// `b+1`
#[cfg(feature = "radix")]
macro_rules! fraction_compare {
($iter:ident, $num:ident, $den:ident, $radix:ident) => {{
// Compare the fraction digits.
// We can only be here if we hit a decimal point.
while !$num.data.is_empty() {
// All digits **must** be valid.
let actual = match $iter.next() {
Some(&v) => v,
// No more actual digits, or hit the exponent.
_ => return cmp::Ordering::Less,
};
let rem = $num.data.quorem(&$den.data) as u32;
let expected = digit_to_char_const(rem, $radix);
$num.data.mul_small($radix as Limb).unwrap();
if actual < expected {
return cmp::Ordering::Less;
} else if actual > expected {
return cmp::Ordering::Greater;
}
}
// Still have fraction digits, check if any are non-zero.
for &digit in $iter {
if digit != b'0' {
return cmp::Ordering::Greater;
}
}
}};
}
/// Compare theoretical digits to halfway point from theoretical digits.
///
/// Generates a float representing the halfway point, and generates
/// theoretical digits as bytes, and compares the generated digits to
/// the actual input.
///
/// Compares the known string to theoretical digits generated on the
/// fly for `b+h`, where a string representation of a float is between
/// `b` and `b+u`, where `b+u` is 1 unit in the least-precision. Therefore,
/// the string must be close to `b+h`.
///
/// Adapted from "Bigcomp: Deciding Truncated, Near Halfway Conversions",
/// available [here](https://www.exploringbinary.com/bigcomp-deciding-truncated-near-halfway-conversions/).
#[cfg(feature = "radix")]
#[allow(clippy::unwrap_used)] // reason = "none is a developer error due to shl overflow"
#[allow(clippy::comparison_chain)] // reason = "logically different conditions for algorithm"
pub fn byte_comp<F: RawFloat, const FORMAT: u128>(
number: Number,
mut fp: ExtendedFloat80,
sci_exp: i32,
) -> ExtendedFloat80 {
// Ensure our preconditions are valid:
// 1. The significant digits are not shifted into place.
debug_assert!(fp.mant & (1 << 63) != 0);
let format = NumberFormat::<FORMAT> {};
// Round down our extended-precision float and calculate `b`.
let mut b = fp;
shared::round::<F, _>(&mut b, shared::round_down);
let b = extended_to_float::<F>(b);
// Calculate `b+h` to create a ratio for our theoretical digits.
let theor = Bigfloat::from_float(bh::<F>(b));
// Now, create a scaling factor for the digit count.
let mut factor = Bigfloat::from_u32(1);
factor.pow(format.radix(), sci_exp.unsigned_abs()).unwrap();
let mut num: Bigfloat;
let mut den: Bigfloat;
if sci_exp < 0 {
// Need to have the basen factor be the numerator, and the `fp`
// be the denominator. Since we assumed that `theor` was the numerator,
// if it's the denominator, we need to multiply it into the numerator.
num = factor;
num.data *= &theor.data;
den = Bigfloat::from_u32(1);
den.exp = -theor.exp;
} else {
num = theor;
den = factor;
}
// Scale the denominator so it has the number of bits
// in the radix as the number of leading zeros.
let wlz = integral_binary_factor(format.radix());
let nlz = den.leading_zeros().wrapping_sub(wlz) & (32 - 1);
if nlz != 0 {
den.shl_bits(nlz as usize).unwrap();
den.exp -= nlz as i32;
}
// Need to scale the numerator or denominator to the same value.
// We don't want to shift the denominator, so...
let diff = den.exp - num.exp;
let shift = diff.unsigned_abs() as usize;
if diff < 0 {
// Need to shift the numerator left.
num.shl(shift).unwrap();
num.exp -= shift as i32;
} else if diff > 0 {
// Need to shift denominator left, go by a power of Limb::BITS.
// After this, the numerator will be non-normalized, and the
// denominator will be normalized. We need to add one to the
// quotient,since we're calculating the ceiling of the divmod.
let (q, r) = shift.ceil_divmod(Limb::BITS as usize);
let r = -r;
if r != 0 {
num.shl_bits(r as usize).unwrap();
num.exp -= r;
}
if q != 0 {
den.shl_limbs(q).unwrap();
den.exp -= Limb::BITS as i32 * q as i32;
}
}
// Compare our theoretical and real digits and round nearest, tie even.
let ord = compare_bytes::<FORMAT>(number, num, den);
shared::round::<F, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |is_odd, _, _| {
// Can ignore `is_halfway` and `is_above`, since those were
// calculates using less significant digits.
match ord {
cmp::Ordering::Greater => true,
cmp::Ordering::Less => false,
cmp::Ordering::Equal if is_odd => true,
cmp::Ordering::Equal => false,
}
});
});
fp
}
/// Compare digits between the generated values the ratio and the actual view.
///
/// - `number` - The representation of the float as a big number, with the
/// parsed digits.
/// - `num` - The actual digits of the real floating point number.
/// - `den` - The theoretical digits created by `b+h` to determine if `b` or
/// `b+1`
#[cfg(feature = "radix")]
#[allow(clippy::unwrap_used)] // reason = "none is a developer error due to a missing fraction"
pub fn compare_bytes<const FORMAT: u128>(
number: Number,
mut num: Bigfloat,
den: Bigfloat,
) -> cmp::Ordering {
let format = NumberFormat::<FORMAT> {};
let radix = format.radix();
// Now need to compare the theoretical digits. First, I need to trim
// any leading zeros, and will also need to ignore trailing ones.
let mut integer = number.integer.bytes::<{ FORMAT }>();
let mut integer_iter = integer.integer_iter();
integer_iter.skip_zeros();
if integer_iter.is_buffer_empty() {
// Cannot be empty, since we must have at least **some** significant digits.
let mut fraction = number.fraction.unwrap().bytes::<{ FORMAT }>();
let mut fraction_iter = fraction.fraction_iter();
fraction_iter.skip_zeros();
fraction_compare!(fraction_iter, num, den, radix);
} else {
integer_compare!(integer_iter, num, den, radix);
if let Some(fraction) = number.fraction {
let mut fraction = fraction.bytes::<{ FORMAT }>();
let mut fraction_iter = fraction.fraction_iter();
fraction_compare!(fraction_iter, num, den, radix);
} else if !num.data.is_empty() {
// We had more theoretical digits, but no more actual digits.
return cmp::Ordering::Less;
}
}
// Exhausted both, must be equal.
cmp::Ordering::Equal
}
// SCALING
// -------
/// Calculate the scientific exponent from a `Number` value.
/// Any other attempts would require slowdowns for faster algorithms.
#[must_use]
#[inline(always)]
pub fn scientific_exponent<const FORMAT: u128>(num: &Number) -> i32 {
// This has the significant digits and exponent relative to those
// digits: therefore, we just need to scale to mantissa to `[1, radix)`.
// This doesn't need to be very fast.
let format = NumberFormat::<FORMAT> {};
// Use power reduction to make this faster: we need at least
// `F::MANTISSA_SIZE` bits, so we must have at least radix^4 digits.
// IF we're using base 3, we can have at most 11 divisions, and
// base 36, at most ~4. So, this is reasonably efficient.
let radix = format.radix() as u64;
let radix2 = radix * radix;
let radix4 = radix2 * radix2;
let mut mantissa = num.mantissa;
let mut exponent = num.exponent;
while mantissa >= radix4 {
mantissa /= radix4;
exponent += 4;
}
while mantissa >= radix2 {
mantissa /= radix2;
exponent += 2;
}
while mantissa >= radix {
mantissa /= radix;
exponent += 1;
}
exponent as i32
}
/// Calculate `b` from a a representation of `b` as a float.
#[must_use]
#[inline(always)]
pub fn b<F: RawFloat>(float: F) -> ExtendedFloat80 {
ExtendedFloat80 {
mant: float.mantissa().as_u64(),
exp: float.exponent(),
}
}
/// Calculate `b+h` from a a representation of `b` as a float.
#[must_use]
#[inline(always)]
pub fn bh<F: RawFloat>(float: F) -> ExtendedFloat80 {
let fp = b(float);
ExtendedFloat80 {
mant: (fp.mant << 1) + 1,
exp: fp.exp - 1,
}
}
// NOTE: There will never be binary factors here.
/// Calculate the integral ceiling of the binary factor from a basen number.
#[must_use]
#[inline(always)]
#[cfg(feature = "radix")]
pub const fn integral_binary_factor(radix: u32) -> u32 {
match radix {
3 => 2,
5 => 3,
6 => 3,
7 => 3,
9 => 4,
10 => 4,
11 => 4,
12 => 4,
13 => 4,
14 => 4,
15 => 4,
17 => 5,
18 => 5,
19 => 5,
20 => 5,
21 => 5,
22 => 5,
23 => 5,
24 => 5,
25 => 5,
26 => 5,
27 => 5,
28 => 5,
29 => 5,
30 => 5,
31 => 5,
33 => 6,
34 => 6,
35 => 6,
36 => 6,
// Invalid radix
_ => 0,
}
}
/// Calculate the integral ceiling of the binary factor from a basen number.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "radix"))]
pub const fn integral_binary_factor(radix: u32) -> u32 {
match radix {
10 => 4,
// Invalid radix
_ => 0,
}
}
+8
View File
@@ -0,0 +1,8 @@
//! Pre-computed tables for parsing float strings.
#![doc(hidden)]
// Re-export all the feature-specific files.
pub use crate::table_large::*;
#[cfg(not(feature = "compact"))]
pub use crate::table_small::*;
@@ -0,0 +1,132 @@
//! Cached exponents for basen values with 80-bit extended floats.
//!
//! Exact versions of base**n as an extended-precision float, with both
//! large and small powers. Use the large powers to minimize the amount
//! of compounded error. This is used in the Bellerophon algorithm.
//!
//! These values were calculated using Python, using the arbitrary-precision
//! integer to calculate exact extended-representation of each value.
//! These values are all normalized.
//!
//! These files takes ~30 KB of storage.
//!
//! Total array storage:
//! With radix: ~20 KB:
//! 2534 u64
//!
//! DO NOT MODIFY: Generated by `etc/bellerophon_table.py`
#![cfg(feature = "compact")]
#![doc(hidden)]
use crate::bellerophon::BellerophonPowers;
/// Get Bellerophon powers from radix.
#[inline(always)]
#[cfg(not(feature = "radix"))]
pub const fn bellerophon_powers(_: u32) -> &'static BellerophonPowers {
&BASE10_POWERS
}
// HIGH LEVEL
// ----------
pub const BASE10_POWERS: BellerophonPowers = BellerophonPowers {
small: &BASE10_SMALL_MANTISSA,
large: &BASE10_LARGE_MANTISSA,
small_int: &BASE10_SMALL_INT_POWERS,
step: BASE10_STEP,
bias: BASE10_BIAS,
log2: BASE10_LOG2_MULT,
log2_shift: BASE10_LOG2_SHIFT,
};
// LOW-LEVEL
// ---------
const BASE10_SMALL_MANTISSA: [u64; 10] = [
9223372036854775808, // 10^0
11529215046068469760, // 10^1
14411518807585587200, // 10^2
18014398509481984000, // 10^3
11258999068426240000, // 10^4
14073748835532800000, // 10^5
17592186044416000000, // 10^6
10995116277760000000, // 10^7
13743895347200000000, // 10^8
17179869184000000000, // 10^9
];
const BASE10_LARGE_MANTISSA: [u64; 66] = [
11555125961253852697, // 10^-350
13451937075301367670, // 10^-340
15660115838168849784, // 10^-330
18230774251475056848, // 10^-320
10611707258198326947, // 10^-310
12353653155963782858, // 10^-300
14381545078898527261, // 10^-290
16742321987285426889, // 10^-280
9745314011399999080, // 10^-270
11345038669416679861, // 10^-260
13207363278391631158, // 10^-250
15375394465392026070, // 10^-240
17899314949046850752, // 10^-230
10418772551374772303, // 10^-220
12129047596099288555, // 10^-210
14120069793541087484, // 10^-200
16437924692338667210, // 10^-190
9568131466127621947, // 10^-180
11138771039116687545, // 10^-170
12967236152753102995, // 10^-160
15095849699286165408, // 10^-150
17573882009934360870, // 10^-140
10229345649675443343, // 10^-130
11908525658859223294, // 10^-120
13863348470604074297, // 10^-110
16139061738043178685, // 10^-100
9394170331095332911, // 10^-90
10936253623915059621, // 10^-80
12731474852090538039, // 10^-70
14821387422376473014, // 10^-60
17254365866976409468, // 10^-50
10043362776618689222, // 10^-40
11692013098647223345, // 10^-30
13611294676837538538, // 10^-20
15845632502852867518, // 10^-10
9223372036854775808, // 10^0
10737418240000000000, // 10^10
12500000000000000000, // 10^20
14551915228366851806, // 10^30
16940658945086006781, // 10^40
9860761315262647567, // 10^50
11479437019748901445, // 10^60
13363823550460978230, // 10^70
15557538194652854267, // 10^80
18111358157653424735, // 10^90
10542197943230523224, // 10^100
12272733663244316382, // 10^110
14287342391028437277, // 10^120
16632655625031838749, // 10^130
9681479787123295682, // 10^140
11270725851789228247, // 10^150
13120851772591970218, // 10^160
15274681817498023410, // 10^170
17782069995880619867, // 10^180
10350527006597618960, // 10^190
12049599325514420588, // 10^200
14027579833653779454, // 10^210
16330252207878254650, // 10^220
9505457831475799117, // 10^230
11065809325636130661, // 10^240
12882297539194266616, // 10^250
14996968138956309548, // 10^260
17458768723248864463, // 10^270
10162340898095201970, // 10^280
11830521861667747109, // 10^290
13772540099066387756, // 10^300
];
const BASE10_SMALL_INT_POWERS: [u64; 10] =
[1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000];
const BASE10_STEP: i32 = 10;
const BASE10_BIAS: i32 = 350;
const BASE10_LOG2_MULT: i64 = 217706;
const BASE10_LOG2_SHIFT: i32 = 16;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,173 @@
//! Pre-computed tables for writing non-decimal strings.
#![cfg(feature = "power-of-two")]
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
use lexical_util::num::Float;
#[cfg(not(feature = "radix"))]
use crate::table_decimal::*;
// HELPERS
// -------
/// Get lookup table for small int powers.
///
/// # Safety
///
/// Safe as long as the radix provided is valid, and exponent is smaller
/// than the table for the radix.
#[inline(always)]
#[cfg(not(feature = "radix"))]
pub const fn get_small_int_power(exponent: usize, radix: u32) -> u64 {
// NOTE: don't check the radix since we also use it for half radix, or 5.
match radix {
2 => get_small_int_power2(exponent),
4 => get_small_int_power4(exponent),
5 => get_small_int_power5(exponent),
8 => get_small_int_power8(exponent),
10 => get_small_int_power10(exponent),
16 => get_small_int_power16(exponent),
32 => get_small_int_power32(exponent),
_ => unreachable!(),
}
}
/// Get lookup table for small f32 powers.
#[inline(always)]
#[cfg(not(feature = "radix"))]
pub fn get_small_f32_power(exponent: usize, radix: u32) -> f32 {
match radix {
2 => get_small_f32_power2(exponent),
4 => get_small_f32_power4(exponent),
8 => get_small_f32_power8(exponent),
10 => get_small_f32_power10(exponent),
16 => get_small_f32_power16(exponent),
32 => get_small_f32_power32(exponent),
_ => unreachable!(),
}
}
/// Get lookup table for small f64 powers.
///
/// # Safety
///
/// Safe as long as the radix provided is valid, and exponent is smaller
/// than the table for the radix.
#[inline(always)]
#[cfg(not(feature = "radix"))]
pub fn get_small_f64_power(exponent: usize, radix: u32) -> f64 {
match radix {
2 => get_small_f64_power2(exponent),
4 => get_small_f64_power4(exponent),
8 => get_small_f64_power8(exponent),
10 => get_small_f64_power10(exponent),
16 => get_small_f64_power16(exponent),
32 => get_small_f64_power32(exponent),
_ => unreachable!(),
}
}
// NOTE:
// These functions use the fact that **all** powers-of-two
// can be exactly represented and cheaply using bit shifts for
// integers, or by setting the exponent directly.
/// Get pre-computed int power of 2.
#[inline(always)]
pub const fn get_small_int_power2(exponent: usize) -> u64 {
1 << exponent
}
/// Get pre-computed f32 power of 2.
#[inline(always)]
pub fn get_small_f32_power2(exponent: usize) -> f32 {
// Can't handle values above the denormal size.
debug_assert!(exponent as i32 <= f32::EXPONENT_BIAS - f32::MANTISSA_SIZE);
let shift = (f32::EXPONENT_BIAS - f32::MANTISSA_SIZE) as u32;
let bits = (exponent as u32 + shift) << f32::MANTISSA_SIZE;
f32::from_bits(bits)
}
/// Get pre-computed f64 power of 2.
#[inline(always)]
pub fn get_small_f64_power2(exponent: usize) -> f64 {
// Can't handle values above the denormal size.
debug_assert!(exponent as i32 <= f64::EXPONENT_BIAS - f64::MANTISSA_SIZE);
let shift = (f64::EXPONENT_BIAS - f64::MANTISSA_SIZE) as u64;
let bits = (exponent as u64 + shift) << f64::MANTISSA_SIZE;
f64::from_bits(bits)
}
/// Get pre-computed int power of 4.
#[inline(always)]
pub const fn get_small_int_power4(exponent: usize) -> u64 {
get_small_int_power2(2 * exponent)
}
/// Get pre-computed f32 power of 4.
#[inline(always)]
pub fn get_small_f32_power4(exponent: usize) -> f32 {
get_small_f32_power2(2 * exponent)
}
/// Get pre-computed f64 power of 4.
#[inline(always)]
pub fn get_small_f64_power4(exponent: usize) -> f64 {
get_small_f64_power2(2 * exponent)
}
/// Get pre-computed int power of 8.
#[inline(always)]
pub const fn get_small_int_power8(exponent: usize) -> u64 {
get_small_int_power2(3 * exponent)
}
/// Get pre-computed f32 power of 8.
#[inline(always)]
pub fn get_small_f32_power8(exponent: usize) -> f32 {
get_small_f32_power2(3 * exponent)
}
/// Get pre-computed f64 power of 8.
#[inline(always)]
pub fn get_small_f64_power8(exponent: usize) -> f64 {
get_small_f64_power2(3 * exponent)
}
/// Get pre-computed int power of 16.
#[inline(always)]
pub const fn get_small_int_power16(exponent: usize) -> u64 {
get_small_int_power2(4 * exponent)
}
/// Get pre-computed f32 power of 16.
#[inline(always)]
pub fn get_small_f32_power16(exponent: usize) -> f32 {
get_small_f32_power2(4 * exponent)
}
/// Get pre-computed f64 power of 16.
#[inline(always)]
pub fn get_small_f64_power16(exponent: usize) -> f64 {
get_small_f64_power2(4 * exponent)
}
/// Get pre-computed int power of 32.
#[inline(always)]
pub const fn get_small_int_power32(exponent: usize) -> u64 {
get_small_int_power2(5 * exponent)
}
/// Get pre-computed f32 power of 32.
#[inline(always)]
pub fn get_small_f32_power32(exponent: usize) -> f32 {
get_small_f32_power2(5 * exponent)
}
/// Get pre-computed f64 power of 32.
#[inline(always)]
pub fn get_small_f64_power32(exponent: usize) -> f64 {
get_small_f64_power2(5 * exponent)
}
@@ -0,0 +1,173 @@
//! Pre-computed tables for writing decimal strings.
#![doc(hidden)]
#![cfg(not(feature = "compact"))]
#[cfg(not(feature = "radix"))]
use crate::bigint::Limb;
use crate::limits::{f32_exponent_limit, f64_exponent_limit, f64_mantissa_limit, u64_power_limit};
// HELPERS
// -------
/// Get lookup table for small int powers.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn get_small_int_power(exponent: usize, radix: u32) -> u64 {
// NOTE: don't check the radix since we also use it for half radix, or 5.
match radix {
5 => get_small_int_power5(exponent),
10 => get_small_int_power10(exponent),
_ => unreachable!(),
}
}
/// Get lookup table for small f32 powers.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn get_small_f32_power(exponent: usize, radix: u32) -> f32 {
_ = radix;
get_small_f32_power10(exponent)
}
/// Get lookup table for small f64 powers.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "power-of-two"))]
pub const fn get_small_f64_power(exponent: usize, radix: u32) -> f64 {
_ = radix;
get_small_f64_power10(exponent)
}
/// Get pre-computed power for a large power of radix.
#[must_use]
#[inline(always)]
#[cfg(not(feature = "radix"))]
pub const fn get_large_int_power(_: u32) -> (&'static [Limb], u32) {
(&LARGE_POW5, LARGE_POW5_STEP)
}
/// Get pre-computed int power of 5.
#[must_use]
#[inline(always)]
pub const fn get_small_int_power5(exponent: usize) -> u64 {
SMALL_INT_POW5[exponent]
}
/// Get pre-computed int power of 10.
#[must_use]
#[inline(always)]
pub const fn get_small_int_power10(exponent: usize) -> u64 {
SMALL_INT_POW10[exponent]
}
/// Get pre-computed f32 power of 10.
#[must_use]
#[inline(always)]
pub const fn get_small_f32_power10(exponent: usize) -> f32 {
SMALL_F32_POW10[exponent]
}
/// Get pre-computed f64 power of 10.
#[must_use]
#[inline(always)]
pub const fn get_small_f64_power10(exponent: usize) -> f64 {
SMALL_F64_POW10[exponent]
}
// TABLES
// ------
/// Pre-computed, small powers-of-5.
pub const SMALL_INT_POW5: [u64; 28] = [
1,
5,
25,
125,
625,
3125,
15625,
78125,
390625,
1953125,
9765625,
48828125,
244140625,
1220703125,
6103515625,
30517578125,
152587890625,
762939453125,
3814697265625,
19073486328125,
95367431640625,
476837158203125,
2384185791015625,
11920928955078125,
59604644775390625,
298023223876953125,
1490116119384765625,
7450580596923828125,
];
const _: () = assert!(SMALL_INT_POW5.len() > f64_mantissa_limit(5) as usize);
const _: () = assert!(SMALL_INT_POW5.len() == u64_power_limit(5) as usize + 1);
/// Pre-computed, small powers-of-10.
pub const SMALL_INT_POW10: [u64; 20] = [
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000,
];
const _: () = assert!(SMALL_INT_POW10.len() > f64_mantissa_limit(10) as usize);
const _: () = assert!(SMALL_INT_POW10.len() == u64_power_limit(10) as usize + 1);
/// Pre-computed, small powers-of-10.
pub const SMALL_F32_POW10: [f32; 16] =
[1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.];
const _: () = assert!(SMALL_F32_POW10.len() > f32_exponent_limit(10).1 as usize);
/// Pre-computed, small powers-of-10.
pub const SMALL_F64_POW10: [f64; 32] = [
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16,
1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0.,
];
const _: () = assert!(SMALL_F64_POW10.len() > f64_exponent_limit(10).1 as usize);
/// Pre-computed large power-of-5 for 32-bit limbs.
#[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))]
pub const LARGE_POW5: [u32; 10] = [
4279965485, 329373468, 4020270615, 2137533757, 4287402176, 1057042919, 1071430142, 2440757623,
381945767, 46164893,
];
/// Pre-computed large power-of-5 for 64-bit limbs.
#[cfg(all(target_pointer_width = "64", not(target_arch = "sparc")))]
pub const LARGE_POW5: [u64; 5] = [
1414648277510068013,
9180637584431281687,
4539964771860779200,
10482974169319127550,
198276706040285095,
];
/// Step for large power-of-5 for 32-bit limbs.
pub const LARGE_POW5_STEP: u32 = 135;
@@ -0,0 +1,11 @@
//! Pre-computed large value tables for writing float strings.
#![doc(hidden)]
// Re-export all the feature-specific files.
#[cfg(feature = "compact")]
pub use crate::table_bellerophon_decimal::*;
#[cfg(feature = "radix")]
pub use crate::table_bellerophon_radix::*;
#[cfg(not(feature = "compact"))]
pub use crate::table_lemire::*;
@@ -0,0 +1,677 @@
//! Pre-computed tables powers-of-5 for extended-precision representations.
//!
//! These tables enable fast scaling of the significant digits
//! of a float to the decimal exponent, with minimal rounding
//! errors, in a 128 or 192-bit representation.
//!
//! DO NOT MODIFY: Generated by `etc/lemire_table.py`
//!
//! This adapted from the Rust implementation, based on the fast-float-rust
//! implementation, and is similarly subject to an Apache2.0/MIT license.
#![doc(hidden)]
#![cfg(not(feature = "compact"))]
#![allow(clippy::unreadable_literal)] // reason="these are auto-generated"
pub const SMALLEST_POWER_OF_FIVE: i32 = -342;
pub const LARGEST_POWER_OF_FIVE: i32 = 308;
pub const N_POWERS_OF_FIVE: usize = (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;
// Use static to avoid long compile times: Rust compiler errors
// can have the entire table compiled multiple times, and then
// emit code multiple times, even if it's stripped out in
// the final binary.
#[rustfmt::skip]
pub static POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [
(0xeef453d6923bd65a, 0x113faa2906a13b3f), // 5^-342
(0x9558b4661b6565f8, 0x4ac7ca59a424c507), // 5^-341
(0xbaaee17fa23ebf76, 0x5d79bcf00d2df649), // 5^-340
(0xe95a99df8ace6f53, 0xf4d82c2c107973dc), // 5^-339
(0x91d8a02bb6c10594, 0x79071b9b8a4be869), // 5^-338
(0xb64ec836a47146f9, 0x9748e2826cdee284), // 5^-337
(0xe3e27a444d8d98b7, 0xfd1b1b2308169b25), // 5^-336
(0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7), // 5^-335
(0xb208ef855c969f4f, 0xbdbd2d335e51a935), // 5^-334
(0xde8b2b66b3bc4723, 0xad2c788035e61382), // 5^-333
(0x8b16fb203055ac76, 0x4c3bcb5021afcc31), // 5^-332
(0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d), // 5^-331
(0xd953e8624b85dd78, 0xd71d6dad34a2af0d), // 5^-330
(0x87d4713d6f33aa6b, 0x8672648c40e5ad68), // 5^-329
(0xa9c98d8ccb009506, 0x680efdaf511f18c2), // 5^-328
(0xd43bf0effdc0ba48, 0x212bd1b2566def2), // 5^-327
(0x84a57695fe98746d, 0x14bb630f7604b57), // 5^-326
(0xa5ced43b7e3e9188, 0x419ea3bd35385e2d), // 5^-325
(0xcf42894a5dce35ea, 0x52064cac828675b9), // 5^-324
(0x818995ce7aa0e1b2, 0x7343efebd1940993), // 5^-323
(0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8), // 5^-322
(0xca66fa129f9b60a6, 0xd41a26e077774ef6), // 5^-321
(0xfd00b897478238d0, 0x8920b098955522b4), // 5^-320
(0x9e20735e8cb16382, 0x55b46e5f5d5535b0), // 5^-319
(0xc5a890362fddbc62, 0xeb2189f734aa831d), // 5^-318
(0xf712b443bbd52b7b, 0xa5e9ec7501d523e4), // 5^-317
(0x9a6bb0aa55653b2d, 0x47b233c92125366e), // 5^-316
(0xc1069cd4eabe89f8, 0x999ec0bb696e840a), // 5^-315
(0xf148440a256e2c76, 0xc00670ea43ca250d), // 5^-314
(0x96cd2a865764dbca, 0x380406926a5e5728), // 5^-313
(0xbc807527ed3e12bc, 0xc605083704f5ecf2), // 5^-312
(0xeba09271e88d976b, 0xf7864a44c633682e), // 5^-311
(0x93445b8731587ea3, 0x7ab3ee6afbe0211d), // 5^-310
(0xb8157268fdae9e4c, 0x5960ea05bad82964), // 5^-309
(0xe61acf033d1a45df, 0x6fb92487298e33bd), // 5^-308
(0x8fd0c16206306bab, 0xa5d3b6d479f8e056), // 5^-307
(0xb3c4f1ba87bc8696, 0x8f48a4899877186c), // 5^-306
(0xe0b62e2929aba83c, 0x331acdabfe94de87), // 5^-305
(0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14), // 5^-304
(0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9), // 5^-303
(0xdb71e91432b1a24a, 0xc9e82cd9f69d6150), // 5^-302
(0x892731ac9faf056e, 0xbe311c083a225cd2), // 5^-301
(0xab70fe17c79ac6ca, 0x6dbd630a48aaf406), // 5^-300
(0xd64d3d9db981787d, 0x92cbbccdad5b108), // 5^-299
(0x85f0468293f0eb4e, 0x25bbf56008c58ea5), // 5^-298
(0xa76c582338ed2621, 0xaf2af2b80af6f24e), // 5^-297
(0xd1476e2c07286faa, 0x1af5af660db4aee1), // 5^-296
(0x82cca4db847945ca, 0x50d98d9fc890ed4d), // 5^-295
(0xa37fce126597973c, 0xe50ff107bab528a0), // 5^-294
(0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8), // 5^-293
(0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a), // 5^-292
(0x9faacf3df73609b1, 0x77b191618c54e9ac), // 5^-291
(0xc795830d75038c1d, 0xd59df5b9ef6a2417), // 5^-290
(0xf97ae3d0d2446f25, 0x4b0573286b44ad1d), // 5^-289
(0x9becce62836ac577, 0x4ee367f9430aec32), // 5^-288
(0xc2e801fb244576d5, 0x229c41f793cda73f), // 5^-287
(0xf3a20279ed56d48a, 0x6b43527578c1110f), // 5^-286
(0x9845418c345644d6, 0x830a13896b78aaa9), // 5^-285
(0xbe5691ef416bd60c, 0x23cc986bc656d553), // 5^-284
(0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8), // 5^-283
(0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9), // 5^-282
(0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53), // 5^-281
(0xe858ad248f5c22c9, 0xd1b3400f8f9cff68), // 5^-280
(0x91376c36d99995be, 0x23100809b9c21fa1), // 5^-279
(0xb58547448ffffb2d, 0xabd40a0c2832a78a), // 5^-278
(0xe2e69915b3fff9f9, 0x16c90c8f323f516c), // 5^-277
(0x8dd01fad907ffc3b, 0xae3da7d97f6792e3), // 5^-276
(0xb1442798f49ffb4a, 0x99cd11cfdf41779c), // 5^-275
(0xdd95317f31c7fa1d, 0x40405643d711d583), // 5^-274
(0x8a7d3eef7f1cfc52, 0x482835ea666b2572), // 5^-273
(0xad1c8eab5ee43b66, 0xda3243650005eecf), // 5^-272
(0xd863b256369d4a40, 0x90bed43e40076a82), // 5^-271
(0x873e4f75e2224e68, 0x5a7744a6e804a291), // 5^-270
(0xa90de3535aaae202, 0x711515d0a205cb36), // 5^-269
(0xd3515c2831559a83, 0xd5a5b44ca873e03), // 5^-268
(0x8412d9991ed58091, 0xe858790afe9486c2), // 5^-267
(0xa5178fff668ae0b6, 0x626e974dbe39a872), // 5^-266
(0xce5d73ff402d98e3, 0xfb0a3d212dc8128f), // 5^-265
(0x80fa687f881c7f8e, 0x7ce66634bc9d0b99), // 5^-264
(0xa139029f6a239f72, 0x1c1fffc1ebc44e80), // 5^-263
(0xc987434744ac874e, 0xa327ffb266b56220), // 5^-262
(0xfbe9141915d7a922, 0x4bf1ff9f0062baa8), // 5^-261
(0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9), // 5^-260
(0xc4ce17b399107c22, 0xcb550fb4384d21d3), // 5^-259
(0xf6019da07f549b2b, 0x7e2a53a146606a48), // 5^-258
(0x99c102844f94e0fb, 0x2eda7444cbfc426d), // 5^-257
(0xc0314325637a1939, 0xfa911155fefb5308), // 5^-256
(0xf03d93eebc589f88, 0x793555ab7eba27ca), // 5^-255
(0x96267c7535b763b5, 0x4bc1558b2f3458de), // 5^-254
(0xbbb01b9283253ca2, 0x9eb1aaedfb016f16), // 5^-253
(0xea9c227723ee8bcb, 0x465e15a979c1cadc), // 5^-252
(0x92a1958a7675175f, 0xbfacd89ec191ec9), // 5^-251
(0xb749faed14125d36, 0xcef980ec671f667b), // 5^-250
(0xe51c79a85916f484, 0x82b7e12780e7401a), // 5^-249
(0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810), // 5^-248
(0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15), // 5^-247
(0xdfbdcece67006ac9, 0x67a791e093e1d49a), // 5^-246
(0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0), // 5^-245
(0xaecc49914078536d, 0x58fae9f773886e18), // 5^-244
(0xda7f5bf590966848, 0xaf39a475506a899e), // 5^-243
(0x888f99797a5e012d, 0x6d8406c952429603), // 5^-242
(0xaab37fd7d8f58178, 0xc8e5087ba6d33b83), // 5^-241
(0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64), // 5^-240
(0x855c3be0a17fcd26, 0x5cf2eea09a55067f), // 5^-239
(0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e), // 5^-238
(0xd0601d8efc57b08b, 0xf13b94daf124da26), // 5^-237
(0x823c12795db6ce57, 0x76c53d08d6b70858), // 5^-236
(0xa2cb1717b52481ed, 0x54768c4b0c64ca6e), // 5^-235
(0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09), // 5^-234
(0xfe5d54150b090b02, 0xd3f93b35435d7c4c), // 5^-233
(0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf), // 5^-232
(0xc6b8e9b0709f109a, 0x359ab6419ca1091b), // 5^-231
(0xf867241c8cc6d4c0, 0xc30163d203c94b62), // 5^-230
(0x9b407691d7fc44f8, 0x79e0de63425dcf1d), // 5^-229
(0xc21094364dfb5636, 0x985915fc12f542e4), // 5^-228
(0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d), // 5^-227
(0x979cf3ca6cec5b5a, 0xa705992ceecf9c42), // 5^-226
(0xbd8430bd08277231, 0x50c6ff782a838353), // 5^-225
(0xece53cec4a314ebd, 0xa4f8bf5635246428), // 5^-224
(0x940f4613ae5ed136, 0x871b7795e136be99), // 5^-223
(0xb913179899f68584, 0x28e2557b59846e3f), // 5^-222
(0xe757dd7ec07426e5, 0x331aeada2fe589cf), // 5^-221
(0x9096ea6f3848984f, 0x3ff0d2c85def7621), // 5^-220
(0xb4bca50b065abe63, 0xfed077a756b53a9), // 5^-219
(0xe1ebce4dc7f16dfb, 0xd3e8495912c62894), // 5^-218
(0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c), // 5^-217
(0xb080392cc4349dec, 0xbd8d794d96aacfb3), // 5^-216
(0xdca04777f541c567, 0xecf0d7a0fc5583a0), // 5^-215
(0x89e42caaf9491b60, 0xf41686c49db57244), // 5^-214
(0xac5d37d5b79b6239, 0x311c2875c522ced5), // 5^-213
(0xd77485cb25823ac7, 0x7d633293366b828b), // 5^-212
(0x86a8d39ef77164bc, 0xae5dff9c02033197), // 5^-211
(0xa8530886b54dbdeb, 0xd9f57f830283fdfc), // 5^-210
(0xd267caa862a12d66, 0xd072df63c324fd7b), // 5^-209
(0x8380dea93da4bc60, 0x4247cb9e59f71e6d), // 5^-208
(0xa46116538d0deb78, 0x52d9be85f074e608), // 5^-207
(0xcd795be870516656, 0x67902e276c921f8b), // 5^-206
(0x806bd9714632dff6, 0xba1cd8a3db53b6), // 5^-205
(0xa086cfcd97bf97f3, 0x80e8a40eccd228a4), // 5^-204
(0xc8a883c0fdaf7df0, 0x6122cd128006b2cd), // 5^-203
(0xfad2a4b13d1b5d6c, 0x796b805720085f81), // 5^-202
(0x9cc3a6eec6311a63, 0xcbe3303674053bb0), // 5^-201
(0xc3f490aa77bd60fc, 0xbedbfc4411068a9c), // 5^-200
(0xf4f1b4d515acb93b, 0xee92fb5515482d44), // 5^-199
(0x991711052d8bf3c5, 0x751bdd152d4d1c4a), // 5^-198
(0xbf5cd54678eef0b6, 0xd262d45a78a0635d), // 5^-197
(0xef340a98172aace4, 0x86fb897116c87c34), // 5^-196
(0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0), // 5^-195
(0xbae0a846d2195712, 0x8974836059cca109), // 5^-194
(0xe998d258869facd7, 0x2bd1a438703fc94b), // 5^-193
(0x91ff83775423cc06, 0x7b6306a34627ddcf), // 5^-192
(0xb67f6455292cbf08, 0x1a3bc84c17b1d542), // 5^-191
(0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93), // 5^-190
(0x8e938662882af53e, 0x547eb47b7282ee9c), // 5^-189
(0xb23867fb2a35b28d, 0xe99e619a4f23aa43), // 5^-188
(0xdec681f9f4c31f31, 0x6405fa00e2ec94d4), // 5^-187
(0x8b3c113c38f9f37e, 0xde83bc408dd3dd04), // 5^-186
(0xae0b158b4738705e, 0x9624ab50b148d445), // 5^-185
(0xd98ddaee19068c76, 0x3badd624dd9b0957), // 5^-184
(0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6), // 5^-183
(0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c), // 5^-182
(0xd47487cc8470652b, 0x7647c3200069671f), // 5^-181
(0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073), // 5^-180
(0xa5fb0a17c777cf09, 0xf468107100525890), // 5^-179
(0xcf79cc9db955c2cc, 0x7182148d4066eeb4), // 5^-178
(0x81ac1fe293d599bf, 0xc6f14cd848405530), // 5^-177
(0xa21727db38cb002f, 0xb8ada00e5a506a7c), // 5^-176
(0xca9cf1d206fdc03b, 0xa6d90811f0e4851c), // 5^-175
(0xfd442e4688bd304a, 0x908f4a166d1da663), // 5^-174
(0x9e4a9cec15763e2e, 0x9a598e4e043287fe), // 5^-173
(0xc5dd44271ad3cdba, 0x40eff1e1853f29fd), // 5^-172
(0xf7549530e188c128, 0xd12bee59e68ef47c), // 5^-171
(0x9a94dd3e8cf578b9, 0x82bb74f8301958ce), // 5^-170
(0xc13a148e3032d6e7, 0xe36a52363c1faf01), // 5^-169
(0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1), // 5^-168
(0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9), // 5^-167
(0xbcb2b812db11a5de, 0x7415d448f6b6f0e7), // 5^-166
(0xebdf661791d60f56, 0x111b495b3464ad21), // 5^-165
(0x936b9fcebb25c995, 0xcab10dd900beec34), // 5^-164
(0xb84687c269ef3bfb, 0x3d5d514f40eea742), // 5^-163
(0xe65829b3046b0afa, 0xcb4a5a3112a5112), // 5^-162
(0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab), // 5^-161
(0xb3f4e093db73a093, 0x59ed216765690f56), // 5^-160
(0xe0f218b8d25088b8, 0x306869c13ec3532c), // 5^-159
(0x8c974f7383725573, 0x1e414218c73a13fb), // 5^-158
(0xafbd2350644eeacf, 0xe5d1929ef90898fa), // 5^-157
(0xdbac6c247d62a583, 0xdf45f746b74abf39), // 5^-156
(0x894bc396ce5da772, 0x6b8bba8c328eb783), // 5^-155
(0xab9eb47c81f5114f, 0x66ea92f3f326564), // 5^-154
(0xd686619ba27255a2, 0xc80a537b0efefebd), // 5^-153
(0x8613fd0145877585, 0xbd06742ce95f5f36), // 5^-152
(0xa798fc4196e952e7, 0x2c48113823b73704), // 5^-151
(0xd17f3b51fca3a7a0, 0xf75a15862ca504c5), // 5^-150
(0x82ef85133de648c4, 0x9a984d73dbe722fb), // 5^-149
(0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba), // 5^-148
(0xcc963fee10b7d1b3, 0x318df905079926a8), // 5^-147
(0xffbbcfe994e5c61f, 0xfdf17746497f7052), // 5^-146
(0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633), // 5^-145
(0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0), // 5^-144
(0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0), // 5^-143
(0x9c1661a651213e2d, 0x6bea10ca65c084e), // 5^-142
(0xc31bfa0fe5698db8, 0x486e494fcff30a62), // 5^-141
(0xf3e2f893dec3f126, 0x5a89dba3c3efccfa), // 5^-140
(0x986ddb5c6b3a76b7, 0xf89629465a75e01c), // 5^-139
(0xbe89523386091465, 0xf6bbb397f1135823), // 5^-138
(0xee2ba6c0678b597f, 0x746aa07ded582e2c), // 5^-137
(0x94db483840b717ef, 0xa8c2a44eb4571cdc), // 5^-136
(0xba121a4650e4ddeb, 0x92f34d62616ce413), // 5^-135
(0xe896a0d7e51e1566, 0x77b020baf9c81d17), // 5^-134
(0x915e2486ef32cd60, 0xace1474dc1d122e), // 5^-133
(0xb5b5ada8aaff80b8, 0xd819992132456ba), // 5^-132
(0xe3231912d5bf60e6, 0x10e1fff697ed6c69), // 5^-131
(0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1), // 5^-130
(0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2), // 5^-129
(0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde), // 5^-128
(0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b), // 5^-127
(0xad4ab7112eb3929d, 0x86c16c98d2c953c6), // 5^-126
(0xd89d64d57a607744, 0xe871c7bf077ba8b7), // 5^-125
(0x87625f056c7c4a8b, 0x11471cd764ad4972), // 5^-124
(0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf), // 5^-123
(0xd389b47879823479, 0x4aff1d108d4ec2c3), // 5^-122
(0x843610cb4bf160cb, 0xcedf722a585139ba), // 5^-121
(0xa54394fe1eedb8fe, 0xc2974eb4ee658828), // 5^-120
(0xce947a3da6a9273e, 0x733d226229feea32), // 5^-119
(0x811ccc668829b887, 0x806357d5a3f525f), // 5^-118
(0xa163ff802a3426a8, 0xca07c2dcb0cf26f7), // 5^-117
(0xc9bcff6034c13052, 0xfc89b393dd02f0b5), // 5^-116
(0xfc2c3f3841f17c67, 0xbbac2078d443ace2), // 5^-115
(0x9d9ba7832936edc0, 0xd54b944b84aa4c0d), // 5^-114
(0xc5029163f384a931, 0xa9e795e65d4df11), // 5^-113
(0xf64335bcf065d37d, 0x4d4617b5ff4a16d5), // 5^-112
(0x99ea0196163fa42e, 0x504bced1bf8e4e45), // 5^-111
(0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6), // 5^-110
(0xf07da27a82c37088, 0x5d767327bb4e5a4c), // 5^-109
(0x964e858c91ba2655, 0x3a6a07f8d510f86f), // 5^-108
(0xbbe226efb628afea, 0x890489f70a55368b), // 5^-107
(0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e), // 5^-106
(0x92c8ae6b464fc96f, 0x3b0b8bc90012929d), // 5^-105
(0xb77ada0617e3bbcb, 0x9ce6ebb40173744), // 5^-104
(0xe55990879ddcaabd, 0xcc420a6a101d0515), // 5^-103
(0x8f57fa54c2a9eab6, 0x9fa946824a12232d), // 5^-102
(0xb32df8e9f3546564, 0x47939822dc96abf9), // 5^-101
(0xdff9772470297ebd, 0x59787e2b93bc56f7), // 5^-100
(0x8bfbea76c619ef36, 0x57eb4edb3c55b65a), // 5^-99
(0xaefae51477a06b03, 0xede622920b6b23f1), // 5^-98
(0xdab99e59958885c4, 0xe95fab368e45eced), // 5^-97
(0x88b402f7fd75539b, 0x11dbcb0218ebb414), // 5^-96
(0xaae103b5fcd2a881, 0xd652bdc29f26a119), // 5^-95
(0xd59944a37c0752a2, 0x4be76d3346f0495f), // 5^-94
(0x857fcae62d8493a5, 0x6f70a4400c562ddb), // 5^-93
(0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952), // 5^-92
(0xd097ad07a71f26b2, 0x7e2000a41346a7a7), // 5^-91
(0x825ecc24c873782f, 0x8ed400668c0c28c8), // 5^-90
(0xa2f67f2dfa90563b, 0x728900802f0f32fa), // 5^-89
(0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9), // 5^-88
(0xfea126b7d78186bc, 0xe2f610c84987bfa8), // 5^-87
(0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9), // 5^-86
(0xc6ede63fa05d3143, 0x91503d1c79720dbb), // 5^-85
(0xf8a95fcf88747d94, 0x75a44c6397ce912a), // 5^-84
(0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba), // 5^-83
(0xc24452da229b021b, 0xfbe85badce996168), // 5^-82
(0xf2d56790ab41c2a2, 0xfae27299423fb9c3), // 5^-81
(0x97c560ba6b0919a5, 0xdccd879fc967d41a), // 5^-80
(0xbdb6b8e905cb600f, 0x5400e987bbc1c920), // 5^-79
(0xed246723473e3813, 0x290123e9aab23b68), // 5^-78
(0x9436c0760c86e30b, 0xf9a0b6720aaf6521), // 5^-77
(0xb94470938fa89bce, 0xf808e40e8d5b3e69), // 5^-76
(0xe7958cb87392c2c2, 0xb60b1d1230b20e04), // 5^-75
(0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2), // 5^-74
(0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3), // 5^-73
(0xe2280b6c20dd5232, 0x25c6da63c38de1b0), // 5^-72
(0x8d590723948a535f, 0x579c487e5a38ad0e), // 5^-71
(0xb0af48ec79ace837, 0x2d835a9df0c6d851), // 5^-70
(0xdcdb1b2798182244, 0xf8e431456cf88e65), // 5^-69
(0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff), // 5^-68
(0xac8b2d36eed2dac5, 0xe272467e3d222f3f), // 5^-67
(0xd7adf884aa879177, 0x5b0ed81dcc6abb0f), // 5^-66
(0x86ccbb52ea94baea, 0x98e947129fc2b4e9), // 5^-65
(0xa87fea27a539e9a5, 0x3f2398d747b36224), // 5^-64
(0xd29fe4b18e88640e, 0x8eec7f0d19a03aad), // 5^-63
(0x83a3eeeef9153e89, 0x1953cf68300424ac), // 5^-62
(0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7), // 5^-61
(0xcdb02555653131b6, 0x3792f412cb06794d), // 5^-60
(0x808e17555f3ebf11, 0xe2bbd88bbee40bd0), // 5^-59
(0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4), // 5^-58
(0xc8de047564d20a8b, 0xf245825a5a445275), // 5^-57
(0xfb158592be068d2e, 0xeed6e2f0f0d56712), // 5^-56
(0x9ced737bb6c4183d, 0x55464dd69685606b), // 5^-55
(0xc428d05aa4751e4c, 0xaa97e14c3c26b886), // 5^-54
(0xf53304714d9265df, 0xd53dd99f4b3066a8), // 5^-53
(0x993fe2c6d07b7fab, 0xe546a8038efe4029), // 5^-52
(0xbf8fdb78849a5f96, 0xde98520472bdd033), // 5^-51
(0xef73d256a5c0f77c, 0x963e66858f6d4440), // 5^-50
(0x95a8637627989aad, 0xdde7001379a44aa8), // 5^-49
(0xbb127c53b17ec159, 0x5560c018580d5d52), // 5^-48
(0xe9d71b689dde71af, 0xaab8f01e6e10b4a6), // 5^-47
(0x9226712162ab070d, 0xcab3961304ca70e8), // 5^-46
(0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22), // 5^-45
(0xe45c10c42a2b3b05, 0x8cb89a7db77c506a), // 5^-44
(0x8eb98a7a9a5b04e3, 0x77f3608e92adb242), // 5^-43
(0xb267ed1940f1c61c, 0x55f038b237591ed3), // 5^-42
(0xdf01e85f912e37a3, 0x6b6c46dec52f6688), // 5^-41
(0x8b61313bbabce2c6, 0x2323ac4b3b3da015), // 5^-40
(0xae397d8aa96c1b77, 0xabec975e0a0d081a), // 5^-39
(0xd9c7dced53c72255, 0x96e7bd358c904a21), // 5^-38
(0x881cea14545c7575, 0x7e50d64177da2e54), // 5^-37
(0xaa242499697392d2, 0xdde50bd1d5d0b9e9), // 5^-36
(0xd4ad2dbfc3d07787, 0x955e4ec64b44e864), // 5^-35
(0x84ec3c97da624ab4, 0xbd5af13bef0b113e), // 5^-34
(0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e), // 5^-33
(0xcfb11ead453994ba, 0x67de18eda5814af2), // 5^-32
(0x81ceb32c4b43fcf4, 0x80eacf948770ced7), // 5^-31
(0xa2425ff75e14fc31, 0xa1258379a94d028d), // 5^-30
(0xcad2f7f5359a3b3e, 0x96ee45813a04330), // 5^-29
(0xfd87b5f28300ca0d, 0x8bca9d6e188853fc), // 5^-28
(0x9e74d1b791e07e48, 0x775ea264cf55347e), // 5^-27
(0xc612062576589dda, 0x95364afe032a819e), // 5^-26
(0xf79687aed3eec551, 0x3a83ddbd83f52205), // 5^-25
(0x9abe14cd44753b52, 0xc4926a9672793543), // 5^-24
(0xc16d9a0095928a27, 0x75b7053c0f178294), // 5^-23
(0xf1c90080baf72cb1, 0x5324c68b12dd6339), // 5^-22
(0x971da05074da7bee, 0xd3f6fc16ebca5e04), // 5^-21
(0xbce5086492111aea, 0x88f4bb1ca6bcf585), // 5^-20
(0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6), // 5^-19
(0x9392ee8e921d5d07, 0x3aff322e62439fd0), // 5^-18
(0xb877aa3236a4b449, 0x9befeb9fad487c3), // 5^-17
(0xe69594bec44de15b, 0x4c2ebe687989a9b4), // 5^-16
(0x901d7cf73ab0acd9, 0xf9d37014bf60a11), // 5^-15
(0xb424dc35095cd80f, 0x538484c19ef38c95), // 5^-14
(0xe12e13424bb40e13, 0x2865a5f206b06fba), // 5^-13
(0x8cbccc096f5088cb, 0xf93f87b7442e45d4), // 5^-12
(0xafebff0bcb24aafe, 0xf78f69a51539d749), // 5^-11
(0xdbe6fecebdedd5be, 0xb573440e5a884d1c), // 5^-10
(0x89705f4136b4a597, 0x31680a88f8953031), // 5^-9
(0xabcc77118461cefc, 0xfdc20d2b36ba7c3e), // 5^-8
(0xd6bf94d5e57a42bc, 0x3d32907604691b4d), // 5^-7
(0x8637bd05af6c69b5, 0xa63f9a49c2c1b110), // 5^-6
(0xa7c5ac471b478423, 0xfcf80dc33721d54), // 5^-5
(0xd1b71758e219652b, 0xd3c36113404ea4a9), // 5^-4
(0x83126e978d4fdf3b, 0x645a1cac083126ea), // 5^-3
(0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4), // 5^-2
(0xcccccccccccccccc, 0xcccccccccccccccd), // 5^-1
(0x8000000000000000, 0x0), // 5^0
(0xa000000000000000, 0x0), // 5^1
(0xc800000000000000, 0x0), // 5^2
(0xfa00000000000000, 0x0), // 5^3
(0x9c40000000000000, 0x0), // 5^4
(0xc350000000000000, 0x0), // 5^5
(0xf424000000000000, 0x0), // 5^6
(0x9896800000000000, 0x0), // 5^7
(0xbebc200000000000, 0x0), // 5^8
(0xee6b280000000000, 0x0), // 5^9
(0x9502f90000000000, 0x0), // 5^10
(0xba43b74000000000, 0x0), // 5^11
(0xe8d4a51000000000, 0x0), // 5^12
(0x9184e72a00000000, 0x0), // 5^13
(0xb5e620f480000000, 0x0), // 5^14
(0xe35fa931a0000000, 0x0), // 5^15
(0x8e1bc9bf04000000, 0x0), // 5^16
(0xb1a2bc2ec5000000, 0x0), // 5^17
(0xde0b6b3a76400000, 0x0), // 5^18
(0x8ac7230489e80000, 0x0), // 5^19
(0xad78ebc5ac620000, 0x0), // 5^20
(0xd8d726b7177a8000, 0x0), // 5^21
(0x878678326eac9000, 0x0), // 5^22
(0xa968163f0a57b400, 0x0), // 5^23
(0xd3c21bcecceda100, 0x0), // 5^24
(0x84595161401484a0, 0x0), // 5^25
(0xa56fa5b99019a5c8, 0x0), // 5^26
(0xcecb8f27f4200f3a, 0x0), // 5^27
(0x813f3978f8940984, 0x4000000000000000), // 5^28
(0xa18f07d736b90be5, 0x5000000000000000), // 5^29
(0xc9f2c9cd04674ede, 0xa400000000000000), // 5^30
(0xfc6f7c4045812296, 0x4d00000000000000), // 5^31
(0x9dc5ada82b70b59d, 0xf020000000000000), // 5^32
(0xc5371912364ce305, 0x6c28000000000000), // 5^33
(0xf684df56c3e01bc6, 0xc732000000000000), // 5^34
(0x9a130b963a6c115c, 0x3c7f400000000000), // 5^35
(0xc097ce7bc90715b3, 0x4b9f100000000000), // 5^36
(0xf0bdc21abb48db20, 0x1e86d40000000000), // 5^37
(0x96769950b50d88f4, 0x1314448000000000), // 5^38
(0xbc143fa4e250eb31, 0x17d955a000000000), // 5^39
(0xeb194f8e1ae525fd, 0x5dcfab0800000000), // 5^40
(0x92efd1b8d0cf37be, 0x5aa1cae500000000), // 5^41
(0xb7abc627050305ad, 0xf14a3d9e40000000), // 5^42
(0xe596b7b0c643c719, 0x6d9ccd05d0000000), // 5^43
(0x8f7e32ce7bea5c6f, 0xe4820023a2000000), // 5^44
(0xb35dbf821ae4f38b, 0xdda2802c8a800000), // 5^45
(0xe0352f62a19e306e, 0xd50b2037ad200000), // 5^46
(0x8c213d9da502de45, 0x4526f422cc340000), // 5^47
(0xaf298d050e4395d6, 0x9670b12b7f410000), // 5^48
(0xdaf3f04651d47b4c, 0x3c0cdd765f114000), // 5^49
(0x88d8762bf324cd0f, 0xa5880a69fb6ac800), // 5^50
(0xab0e93b6efee0053, 0x8eea0d047a457a00), // 5^51
(0xd5d238a4abe98068, 0x72a4904598d6d880), // 5^52
(0x85a36366eb71f041, 0x47a6da2b7f864750), // 5^53
(0xa70c3c40a64e6c51, 0x999090b65f67d924), // 5^54
(0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d), // 5^55
(0x82818f1281ed449f, 0xbff8f10e7a8921a4), // 5^56
(0xa321f2d7226895c7, 0xaff72d52192b6a0d), // 5^57
(0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490), // 5^58
(0xfee50b7025c36a08, 0x2f236d04753d5b4), // 5^59
(0x9f4f2726179a2245, 0x1d762422c946590), // 5^60
(0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5), // 5^61
(0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2), // 5^62
(0x9b934c3b330c8577, 0x63cc55f49f88eb2f), // 5^63
(0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb), // 5^64
(0xf316271c7fc3908a, 0x8bef464e3945ef7a), // 5^65
(0x97edd871cfda3a56, 0x97758bf0e3cbb5ac), // 5^66
(0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317), // 5^67
(0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd), // 5^68
(0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a), // 5^69
(0xb975d6b6ee39e436, 0xb3e2fd538e122b44), // 5^70
(0xe7d34c64a9c85d44, 0x60dbbca87196b616), // 5^71
(0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd), // 5^72
(0xb51d13aea4a488dd, 0x6babab6398bdbe41), // 5^73
(0xe264589a4dcdab14, 0xc696963c7eed2dd1), // 5^74
(0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2), // 5^75
(0xb0de65388cc8ada8, 0x3b25a55f43294bcb), // 5^76
(0xdd15fe86affad912, 0x49ef0eb713f39ebe), // 5^77
(0x8a2dbf142dfcc7ab, 0x6e3569326c784337), // 5^78
(0xacb92ed9397bf996, 0x49c2c37f07965404), // 5^79
(0xd7e77a8f87daf7fb, 0xdc33745ec97be906), // 5^80
(0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3), // 5^81
(0xa8acd7c0222311bc, 0xc40832ea0d68ce0c), // 5^82
(0xd2d80db02aabd62b, 0xf50a3fa490c30190), // 5^83
(0x83c7088e1aab65db, 0x792667c6da79e0fa), // 5^84
(0xa4b8cab1a1563f52, 0x577001b891185938), // 5^85
(0xcde6fd5e09abcf26, 0xed4c0226b55e6f86), // 5^86
(0x80b05e5ac60b6178, 0x544f8158315b05b4), // 5^87
(0xa0dc75f1778e39d6, 0x696361ae3db1c721), // 5^88
(0xc913936dd571c84c, 0x3bc3a19cd1e38e9), // 5^89
(0xfb5878494ace3a5f, 0x4ab48a04065c723), // 5^90
(0x9d174b2dcec0e47b, 0x62eb0d64283f9c76), // 5^91
(0xc45d1df942711d9a, 0x3ba5d0bd324f8394), // 5^92
(0xf5746577930d6500, 0xca8f44ec7ee36479), // 5^93
(0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb), // 5^94
(0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e), // 5^95
(0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e), // 5^96
(0x95d04aee3b80ece5, 0xbba1f1d158724a12), // 5^97
(0xbb445da9ca61281f, 0x2a8a6e45ae8edc97), // 5^98
(0xea1575143cf97226, 0xf52d09d71a3293bd), // 5^99
(0x924d692ca61be758, 0x593c2626705f9c56), // 5^100
(0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c), // 5^101
(0xe498f455c38b997a, 0xb6dfb9c0f956447), // 5^102
(0x8edf98b59a373fec, 0x4724bd4189bd5eac), // 5^103
(0xb2977ee300c50fe7, 0x58edec91ec2cb657), // 5^104
(0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed), // 5^105
(0x8b865b215899f46c, 0xbd79e0d20082ee74), // 5^106
(0xae67f1e9aec07187, 0xecd8590680a3aa11), // 5^107
(0xda01ee641a708de9, 0xe80e6f4820cc9495), // 5^108
(0x884134fe908658b2, 0x3109058d147fdcdd), // 5^109
(0xaa51823e34a7eede, 0xbd4b46f0599fd415), // 5^110
(0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a), // 5^111
(0x850fadc09923329e, 0x3e2cf6bc604ddb0), // 5^112
(0xa6539930bf6bff45, 0x84db8346b786151c), // 5^113
(0xcfe87f7cef46ff16, 0xe612641865679a63), // 5^114
(0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e), // 5^115
(0xa26da3999aef7749, 0xe3be5e330f38f09d), // 5^116
(0xcb090c8001ab551c, 0x5cadf5bfd3072cc5), // 5^117
(0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6), // 5^118
(0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa), // 5^119
(0xc646d63501a1511d, 0xb281e1fd541501b8), // 5^120
(0xf7d88bc24209a565, 0x1f225a7ca91a4226), // 5^121
(0x9ae757596946075f, 0x3375788de9b06958), // 5^122
(0xc1a12d2fc3978937, 0x52d6b1641c83ae), // 5^123
(0xf209787bb47d6b84, 0xc0678c5dbd23a49a), // 5^124
(0x9745eb4d50ce6332, 0xf840b7ba963646e0), // 5^125
(0xbd176620a501fbff, 0xb650e5a93bc3d898), // 5^126
(0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe), // 5^127
(0x93ba47c980e98cdf, 0xc66f336c36b10137), // 5^128
(0xb8a8d9bbe123f017, 0xb80b0047445d4184), // 5^129
(0xe6d3102ad96cec1d, 0xa60dc059157491e5), // 5^130
(0x9043ea1ac7e41392, 0x87c89837ad68db2f), // 5^131
(0xb454e4a179dd1877, 0x29babe4598c311fb), // 5^132
(0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a), // 5^133
(0x8ce2529e2734bb1d, 0x1899e4a65f58660c), // 5^134
(0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f), // 5^135
(0xdc21a1171d42645d, 0x76707543f4fa1f73), // 5^136
(0x899504ae72497eba, 0x6a06494a791c53a8), // 5^137
(0xabfa45da0edbde69, 0x487db9d17636892), // 5^138
(0xd6f8d7509292d603, 0x45a9d2845d3c42b6), // 5^139
(0x865b86925b9bc5c2, 0xb8a2392ba45a9b2), // 5^140
(0xa7f26836f282b732, 0x8e6cac7768d7141e), // 5^141
(0xd1ef0244af2364ff, 0x3207d795430cd926), // 5^142
(0x8335616aed761f1f, 0x7f44e6bd49e807b8), // 5^143
(0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6), // 5^144
(0xcd036837130890a1, 0x36dba887c37a8c0f), // 5^145
(0x802221226be55a64, 0xc2494954da2c9789), // 5^146
(0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c), // 5^147
(0xc83553c5c8965d3d, 0x6f92829494e5acc7), // 5^148
(0xfa42a8b73abbf48c, 0xcb772339ba1f17f9), // 5^149
(0x9c69a97284b578d7, 0xff2a760414536efb), // 5^150
(0xc38413cf25e2d70d, 0xfef5138519684aba), // 5^151
(0xf46518c2ef5b8cd1, 0x7eb258665fc25d69), // 5^152
(0x98bf2f79d5993802, 0xef2f773ffbd97a61), // 5^153
(0xbeeefb584aff8603, 0xaafb550ffacfd8fa), // 5^154
(0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38), // 5^155
(0x952ab45cfa97a0b2, 0xdd945a747bf26183), // 5^156
(0xba756174393d88df, 0x94f971119aeef9e4), // 5^157
(0xe912b9d1478ceb17, 0x7a37cd5601aab85d), // 5^158
(0x91abb422ccb812ee, 0xac62e055c10ab33a), // 5^159
(0xb616a12b7fe617aa, 0x577b986b314d6009), // 5^160
(0xe39c49765fdf9d94, 0xed5a7e85fda0b80b), // 5^161
(0x8e41ade9fbebc27d, 0x14588f13be847307), // 5^162
(0xb1d219647ae6b31c, 0x596eb2d8ae258fc8), // 5^163
(0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb), // 5^164
(0x8aec23d680043bee, 0x25de7bb9480d5854), // 5^165
(0xada72ccc20054ae9, 0xaf561aa79a10ae6a), // 5^166
(0xd910f7ff28069da4, 0x1b2ba1518094da04), // 5^167
(0x87aa9aff79042286, 0x90fb44d2f05d0842), // 5^168
(0xa99541bf57452b28, 0x353a1607ac744a53), // 5^169
(0xd3fa922f2d1675f2, 0x42889b8997915ce8), // 5^170
(0x847c9b5d7c2e09b7, 0x69956135febada11), // 5^171
(0xa59bc234db398c25, 0x43fab9837e699095), // 5^172
(0xcf02b2c21207ef2e, 0x94f967e45e03f4bb), // 5^173
(0x8161afb94b44f57d, 0x1d1be0eebac278f5), // 5^174
(0xa1ba1ba79e1632dc, 0x6462d92a69731732), // 5^175
(0xca28a291859bbf93, 0x7d7b8f7503cfdcfe), // 5^176
(0xfcb2cb35e702af78, 0x5cda735244c3d43e), // 5^177
(0x9defbf01b061adab, 0x3a0888136afa64a7), // 5^178
(0xc56baec21c7a1916, 0x88aaa1845b8fdd0), // 5^179
(0xf6c69a72a3989f5b, 0x8aad549e57273d45), // 5^180
(0x9a3c2087a63f6399, 0x36ac54e2f678864b), // 5^181
(0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd), // 5^182
(0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5), // 5^183
(0x969eb7c47859e743, 0x9f644ae5a4b1b325), // 5^184
(0xbc4665b596706114, 0x873d5d9f0dde1fee), // 5^185
(0xeb57ff22fc0c7959, 0xa90cb506d155a7ea), // 5^186
(0x9316ff75dd87cbd8, 0x9a7f12442d588f2), // 5^187
(0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f), // 5^188
(0xe5d3ef282a242e81, 0x8f1668c8a86da5fa), // 5^189
(0x8fa475791a569d10, 0xf96e017d694487bc), // 5^190
(0xb38d92d760ec4455, 0x37c981dcc395a9ac), // 5^191
(0xe070f78d3927556a, 0x85bbe253f47b1417), // 5^192
(0x8c469ab843b89562, 0x93956d7478ccec8e), // 5^193
(0xaf58416654a6babb, 0x387ac8d1970027b2), // 5^194
(0xdb2e51bfe9d0696a, 0x6997b05fcc0319e), // 5^195
(0x88fcf317f22241e2, 0x441fece3bdf81f03), // 5^196
(0xab3c2fddeeaad25a, 0xd527e81cad7626c3), // 5^197
(0xd60b3bd56a5586f1, 0x8a71e223d8d3b074), // 5^198
(0x85c7056562757456, 0xf6872d5667844e49), // 5^199
(0xa738c6bebb12d16c, 0xb428f8ac016561db), // 5^200
(0xd106f86e69d785c7, 0xe13336d701beba52), // 5^201
(0x82a45b450226b39c, 0xecc0024661173473), // 5^202
(0xa34d721642b06084, 0x27f002d7f95d0190), // 5^203
(0xcc20ce9bd35c78a5, 0x31ec038df7b441f4), // 5^204
(0xff290242c83396ce, 0x7e67047175a15271), // 5^205
(0x9f79a169bd203e41, 0xf0062c6e984d386), // 5^206
(0xc75809c42c684dd1, 0x52c07b78a3e60868), // 5^207
(0xf92e0c3537826145, 0xa7709a56ccdf8a82), // 5^208
(0x9bbcc7a142b17ccb, 0x88a66076400bb691), // 5^209
(0xc2abf989935ddbfe, 0x6acff893d00ea435), // 5^210
(0xf356f7ebf83552fe, 0x583f6b8c4124d43), // 5^211
(0x98165af37b2153de, 0xc3727a337a8b704a), // 5^212
(0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c), // 5^213
(0xeda2ee1c7064130c, 0x1162def06f79df73), // 5^214
(0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8), // 5^215
(0xb9a74a0637ce2ee1, 0x6d953e2bd7173692), // 5^216
(0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437), // 5^217
(0x910ab1d4db9914a0, 0x1d9c9892400a22a2), // 5^218
(0xb54d5e4a127f59c8, 0x2503beb6d00cab4b), // 5^219
(0xe2a0b5dc971f303a, 0x2e44ae64840fd61d), // 5^220
(0x8da471a9de737e24, 0x5ceaecfed289e5d2), // 5^221
(0xb10d8e1456105dad, 0x7425a83e872c5f47), // 5^222
(0xdd50f1996b947518, 0xd12f124e28f77719), // 5^223
(0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f), // 5^224
(0xace73cbfdc0bfb7b, 0x636cc64d1001550b), // 5^225
(0xd8210befd30efa5a, 0x3c47f7e05401aa4e), // 5^226
(0x8714a775e3e95c78, 0x65acfaec34810a71), // 5^227
(0xa8d9d1535ce3b396, 0x7f1839a741a14d0d), // 5^228
(0xd31045a8341ca07c, 0x1ede48111209a050), // 5^229
(0x83ea2b892091e44d, 0x934aed0aab460432), // 5^230
(0xa4e4b66b68b65d60, 0xf81da84d5617853f), // 5^231
(0xce1de40642e3f4b9, 0x36251260ab9d668e), // 5^232
(0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019), // 5^233
(0xa1075a24e4421730, 0xb24cf65b8612f81f), // 5^234
(0xc94930ae1d529cfc, 0xdee033f26797b627), // 5^235
(0xfb9b7cd9a4a7443c, 0x169840ef017da3b1), // 5^236
(0x9d412e0806e88aa5, 0x8e1f289560ee864e), // 5^237
(0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2), // 5^238
(0xf5b5d7ec8acb58a2, 0xae10af696774b1db), // 5^239
(0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29), // 5^240
(0xbff610b0cc6edd3f, 0x17fd090a58d32af3), // 5^241
(0xeff394dcff8a948e, 0xddfc4b4cef07f5b0), // 5^242
(0x95f83d0a1fb69cd9, 0x4abdaf101564f98e), // 5^243
(0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1), // 5^244
(0xea53df5fd18d5513, 0x84c86189216dc5ed), // 5^245
(0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4), // 5^246
(0xb7118682dbb66a77, 0x3fbc8c33221dc2a1), // 5^247
(0xe4d5e82392a40515, 0xfabaf3feaa5334a), // 5^248
(0x8f05b1163ba6832d, 0x29cb4d87f2a7400e), // 5^249
(0xb2c71d5bca9023f8, 0x743e20e9ef511012), // 5^250
(0xdf78e4b2bd342cf6, 0x914da9246b255416), // 5^251
(0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e), // 5^252
(0xae9672aba3d0c320, 0xa184ac2473b529b1), // 5^253
(0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e), // 5^254
(0x8865899617fb1871, 0x7e2fa67c7a658892), // 5^255
(0xaa7eebfb9df9de8d, 0xddbb901b98feeab7), // 5^256
(0xd51ea6fa85785631, 0x552a74227f3ea565), // 5^257
(0x8533285c936b35de, 0xd53a88958f87275f), // 5^258
(0xa67ff273b8460356, 0x8a892abaf368f137), // 5^259
(0xd01fef10a657842c, 0x2d2b7569b0432d85), // 5^260
(0x8213f56a67f6b29b, 0x9c3b29620e29fc73), // 5^261
(0xa298f2c501f45f42, 0x8349f3ba91b47b8f), // 5^262
(0xcb3f2f7642717713, 0x241c70a936219a73), // 5^263
(0xfe0efb53d30dd4d7, 0xed238cd383aa0110), // 5^264
(0x9ec95d1463e8a506, 0xf4363804324a40aa), // 5^265
(0xc67bb4597ce2ce48, 0xb143c6053edcd0d5), // 5^266
(0xf81aa16fdc1b81da, 0xdd94b7868e94050a), // 5^267
(0x9b10a4e5e9913128, 0xca7cf2b4191c8326), // 5^268
(0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0), // 5^269
(0xf24a01a73cf2dccf, 0xbc633b39673c8cec), // 5^270
(0x976e41088617ca01, 0xd5be0503e085d813), // 5^271
(0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18), // 5^272
(0xec9c459d51852ba2, 0xddf8e7d60ed1219e), // 5^273
(0x93e1ab8252f33b45, 0xcabb90e5c942b503), // 5^274
(0xb8da1662e7b00a17, 0x3d6a751f3b936243), // 5^275
(0xe7109bfba19c0c9d, 0xcc512670a783ad4), // 5^276
(0x906a617d450187e2, 0x27fb2b80668b24c5), // 5^277
(0xb484f9dc9641e9da, 0xb1f9f660802dedf6), // 5^278
(0xe1a63853bbd26451, 0x5e7873f8a0396973), // 5^279
(0x8d07e33455637eb2, 0xdb0b487b6423e1e8), // 5^280
(0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62), // 5^281
(0xdc5c5301c56b75f7, 0x7641a140cc7810fb), // 5^282
(0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d), // 5^283
(0xac2820d9623bf429, 0x546345fa9fbdcd44), // 5^284
(0xd732290fbacaf133, 0xa97c177947ad4095), // 5^285
(0x867f59a9d4bed6c0, 0x49ed8eabcccc485d), // 5^286
(0xa81f301449ee8c70, 0x5c68f256bfff5a74), // 5^287
(0xd226fc195c6a2f8c, 0x73832eec6fff3111), // 5^288
(0x83585d8fd9c25db7, 0xc831fd53c5ff7eab), // 5^289
(0xa42e74f3d032f525, 0xba3e7ca8b77f5e55), // 5^290
(0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb), // 5^291
(0x80444b5e7aa7cf85, 0x7980d163cf5b81b3), // 5^292
(0xa0555e361951c366, 0xd7e105bcc332621f), // 5^293
(0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7), // 5^294
(0xfa856334878fc150, 0xb14f98f6f0feb951), // 5^295
(0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3), // 5^296
(0xc3b8358109e84f07, 0xa862f80ec4700c8), // 5^297
(0xf4a642e14c6262c8, 0xcd27bb612758c0fa), // 5^298
(0x98e7e9cccfbd7dbd, 0x8038d51cb897789c), // 5^299
(0xbf21e44003acdd2c, 0xe0470a63e6bd56c3), // 5^300
(0xeeea5d5004981478, 0x1858ccfce06cac74), // 5^301
(0x95527a5202df0ccb, 0xf37801e0c43ebc8), // 5^302
(0xbaa718e68396cffd, 0xd30560258f54e6ba), // 5^303
(0xe950df20247c83fd, 0x47c6b82ef32a2069), // 5^304
(0x91d28b7416cdd27e, 0x4cdc331d57fa5441), // 5^305
(0xb6472e511c81471d, 0xe0133fe4adf8e952), // 5^306
(0xe3d8f9e563a198e5, 0x58180fddd97723a6), // 5^307
(0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648), // 5^308
];
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
//! Pre-computed small value tables for writing float strings.
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
// Re-export all the feature-specific files.
#[cfg(feature = "power-of-two")]
pub use crate::table_binary::*;
pub use crate::table_decimal::*;
#[cfg(feature = "radix")]
pub use crate::table_radix::*;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,68 @@
#![cfg(any(feature = "compact", feature = "radix"))]
#![allow(dead_code)]
use lexical_parse_float::bellerophon::bellerophon;
use lexical_parse_float::float::{extended_to_float, ExtendedFloat80, RawFloat};
use lexical_parse_float::number::Number;
use lexical_util::format::STANDARD;
pub fn bellerophon_test<F: RawFloat, const FORMAT: u128>(
xmant: u64,
xexp: i32,
many_digits: bool,
ymant: u64,
yexp: i32,
) {
let num = Number {
exponent: xexp as i64,
mantissa: xmant,
is_negative: false,
many_digits,
integer: &[],
fraction: None,
};
let xfp = bellerophon::<F, FORMAT>(&num, false);
let yfp = ExtendedFloat80 {
mant: ymant,
exp: yexp,
};
// Given us useful error messages if the floats are valid.
if xfp.exp >= 0 && yfp.exp >= 0 {
assert!(
xfp == yfp,
"x != y, xfp={:?}, yfp={:?}, x={:?}, y={:?}",
xfp,
yfp,
extended_to_float::<F>(xfp),
extended_to_float::<F>(yfp)
);
} else {
assert_eq!(xfp, yfp);
}
}
pub fn compute_float32(q: i64, w: u64) -> (i32, u64) {
let num = Number {
exponent: q,
mantissa: w,
is_negative: false,
many_digits: false,
integer: &[],
fraction: None,
};
let fp = bellerophon::<f32, { STANDARD }>(&num, false);
(fp.exp, fp.mant)
}
pub fn compute_float64(q: i64, w: u64) -> (i32, u64) {
let num = Number {
exponent: q,
mantissa: w,
is_negative: false,
many_digits: false,
integer: &[],
fraction: None,
};
let fp = bellerophon::<f64, { STANDARD }>(&num, false);
(fp.exp, fp.mant)
}
@@ -0,0 +1,26 @@
#![cfg(feature = "radix")]
mod bellerophon;
use bellerophon::bellerophon_test;
use lexical_util::format::NumberFormatBuilder;
const BASE3: u128 = NumberFormatBuilder::from_radix(3);
#[test]
fn bellerophon_radix_test() {
// Checking the exact rounding of the digits close to 5e-324.
bellerophon_test::<f64, { BASE3 }>(5, -640, false, 4172256988254845, 10);
bellerophon_test::<f64, { BASE3 }>(2, -679, false, 0, 0);
bellerophon_test::<f64, { BASE3 }>(3, -679, false, 1, 0);
bellerophon_test::<f64, { BASE3 }>(6, -680, false, 0, 0);
bellerophon_test::<f64, { BASE3 }>(7, -680, false, 1, 0);
bellerophon_test::<f64, { BASE3 }>(20, -681, false, 0, 0);
bellerophon_test::<f64, { BASE3 }>(21, -681, false, 1, 0);
bellerophon_test::<f64, { BASE3 }>(61, -682, false, 0, 0);
bellerophon_test::<f64, { BASE3 }>(62, -682, false, 1, 0);
bellerophon_test::<f64, { BASE3 }>(184, -683, false, 0, 0);
bellerophon_test::<f64, { BASE3 }>(185, -683, false, 1, 0);
bellerophon_test::<f64, { BASE3 }>(554, -684, false, 0, 0);
bellerophon_test::<f64, { BASE3 }>(555, -684, false, 1, 0);
}
@@ -0,0 +1,250 @@
#![cfg(feature = "compact")]
mod bellerophon;
use bellerophon::{bellerophon_test, compute_float32, compute_float64};
use lexical_parse_float::shared::INVALID_FP;
use lexical_util::format::STANDARD;
#[test]
fn halfway_round_down_test() {
// Halfway, round-down tests
bellerophon_test::<f64, { STANDARD }>(9007199254740992, 0, false, 0, 1076);
bellerophon_test::<f64, { STANDARD }>(
9007199254740993,
0,
false,
9223372036854776832,
1065 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(9007199254740994, 0, false, 1, 1076);
bellerophon_test::<f64, { STANDARD }>(18014398509481984, 0, false, 0, 1077);
bellerophon_test::<f64, { STANDARD }>(
18014398509481986,
0,
false,
9223372036854776832,
1066 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(18014398509481988, 0, false, 1, 1077);
bellerophon_test::<f64, { STANDARD }>(9223372036854775808, 0, false, 0, 1086);
bellerophon_test::<f64, { STANDARD }>(
9223372036854776832,
0,
false,
9223372036854776832,
1075 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(9223372036854777856, 0, false, 1, 1086);
// Add a 0 but say we're truncated.
bellerophon_test::<f64, { STANDARD }>(9007199254740992000, -3, true, 0, 1076);
bellerophon_test::<f64, { STANDARD }>(
9007199254740993000,
-3,
true,
9223372036854776832,
1065 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(9007199254740994000, -3, true, 1, 1076);
}
#[test]
fn halfway_round_up_test() {
// Halfway, round-up tests
bellerophon_test::<f64, { STANDARD }>(9007199254740994, 0, false, 1, 1076);
bellerophon_test::<f64, { STANDARD }>(
9007199254740995,
0,
false,
9223372036854778880,
1065 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(9007199254740996, 0, false, 2, 1076);
bellerophon_test::<f64, { STANDARD }>(18014398509481988, 0, false, 1, 1077);
bellerophon_test::<f64, { STANDARD }>(
18014398509481990,
0,
false,
9223372036854778880,
1066 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(18014398509481992, 0, false, 2, 1077);
bellerophon_test::<f64, { STANDARD }>(9223372036854777856, 0, false, 1, 1086);
bellerophon_test::<f64, { STANDARD }>(
9223372036854778880,
0,
false,
9223372036854778880,
1075 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(9223372036854779904, 0, false, 2, 1086);
// Add a 0 but say we're truncated.
bellerophon_test::<f64, { STANDARD }>(9007199254740994000, -3, true, 1, 1076);
bellerophon_test::<f64, { STANDARD }>(
9007199254740994990,
-3,
true,
9223372036854778869,
1065 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(
9007199254740995000,
-3,
true,
9223372036854778879,
1065 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(
9007199254740995010,
-3,
true,
9223372036854778890,
1065 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(9007199254740995050, -3, true, 2, 1076);
bellerophon_test::<f64, { STANDARD }>(9007199254740996000, -3, true, 2, 1076);
}
#[test]
fn extremes_test() {
// Need to check we get proper results with rounding for near-infinity
// and near-zero and/or denormal floats.
bellerophon_test::<f64, { STANDARD }>(5, -324, false, 1, 0);
bellerophon_test::<f64, { STANDARD }>(10, -324, false, 2, 0);
// This is very close to 2.4703282292062327206e-342.
bellerophon_test::<f64, { STANDARD }>(
2470328229206232720,
-342,
false,
18446744073709551608,
-64 + INVALID_FP,
);
bellerophon_test::<f64, { STANDARD }>(
2470328229206232721,
-342,
false,
9223372036854775808,
-32831,
);
bellerophon_test::<f64, { STANDARD }>(
2470328229206232725,
-342,
false,
9223372036854775824,
-32831,
);
bellerophon_test::<f64, { STANDARD }>(2470328229206232726, -342, false, 1, 0);
bellerophon_test::<f64, { STANDARD }>(2470328229206232730, -342, false, 1, 0);
// Check very close to literal infinity.
// 17.976931348623155
// 1.797693134862315508561243283845062402343434371574593359244049e+308
// 1.797693134862315708145274237317043567980705675258449965989175e+308
bellerophon_test::<f64, { STANDARD }>(17976931348623155, 292, false, 4503599627370494, 2046);
bellerophon_test::<f64, { STANDARD }>(17976931348623156, 292, false, 4503599627370494, 2046);
bellerophon_test::<f64, { STANDARD }>(1797693134862315605, 290, false, 4503599627370494, 2046);
bellerophon_test::<f64, { STANDARD }>(1797693134862315607, 290, false, 4503599627370494, 2046);
bellerophon_test::<f64, { STANDARD }>(
1797693134862315608,
290,
false,
18446744073709548540,
-30733,
);
bellerophon_test::<f64, { STANDARD }>(
1797693134862315609,
290,
false,
18446744073709548550,
-30733,
);
bellerophon_test::<f64, { STANDARD }>(179769313486231561, 291, false, 4503599627370495, 2046);
bellerophon_test::<f64, { STANDARD }>(17976931348623157, 292, false, 4503599627370495, 2046);
// Check existing issues and underflow.
bellerophon_test::<f64, { STANDARD }>(2470328229206232726, -343, false, 0, 0);
bellerophon_test::<f64, { STANDARD }>(2470328229206232726, -342, false, 1, 0);
bellerophon_test::<f64, { STANDARD }>(1, -250, false, 1945308223406668, 192);
bellerophon_test::<f64, { STANDARD }>(1, -150, false, 2867420733609077, 524);
bellerophon_test::<f64, { STANDARD }>(1, -45, false, 1924152549665465, 873);
bellerophon_test::<f64, { STANDARD }>(1, -40, false, 400386103400348, 890);
bellerophon_test::<f64, { STANDARD }>(1, -20, false, 2142540351554083, 956);
bellerophon_test::<f64, { STANDARD }>(1, 0, false, 0, 1023);
bellerophon_test::<f64, { STANDARD }>(1, 20, false, 1599915997629504, 1089);
bellerophon_test::<f64, { STANDARD }>(1, 40, false, 3768206498159781, 1155);
bellerophon_test::<f64, { STANDARD }>(1, 150, false, 999684479948463, 1521);
bellerophon_test::<f64, { STANDARD }>(1, 250, false, 1786584717939204, 1853);
// Minimum positive normal float.
bellerophon_test::<f64, { STANDARD }>(22250738585072014, -324, false, 0, 1);
// Maximum positive subnormal float.
bellerophon_test::<f64, { STANDARD }>(2225073858507201, -323, false, 4503599627370495, 0);
// Next highest subnormal float.
bellerophon_test::<f64, { STANDARD }>(22250738585072004, -324, false, 4503599627370494, 0);
bellerophon_test::<f64, { STANDARD }>(22250738585072006, -324, false, 4503599627370494, 0);
bellerophon_test::<f64, { STANDARD }>(22250738585072007, -324, false, 4503599627370495, 0);
bellerophon_test::<f64, { STANDARD }>(222507385850720062, -325, false, 4503599627370494, 0);
bellerophon_test::<f64, { STANDARD }>(222507385850720063, -325, false, 4503599627370494, 0);
bellerophon_test::<f64, { STANDARD }>(222507385850720064, -325, false, 4503599627370494, 0);
bellerophon_test::<f64, { STANDARD }>(
2225073858507200641,
-326,
false,
18446744073709545462,
-32779,
);
bellerophon_test::<f64, { STANDARD }>(
2225073858507200642,
-326,
false,
18446744073709545472,
-32779,
);
bellerophon_test::<f64, { STANDARD }>(222507385850720065, -325, false, 4503599627370495, 0);
}
#[test]
fn compute_float_f32_test() {
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_float32(0, 16777216), (151, 0));
assert_eq!(compute_float32(0, 16777217), (111 + INVALID_FP, 9223372586610589696));
assert_eq!(compute_float32(0, 16777218), (151, 1));
assert_eq!(compute_float32(0, 16777219), (111 + INVALID_FP, 9223373686122217472));
assert_eq!(compute_float32(0, 16777220), (151, 2));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
assert_eq!(compute_float32(-10, 167772170000000000), (111 + INVALID_FP, 9223372586610589696));
assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
// Let's check the lines to see if anything is different in table...
assert_eq!(compute_float32(-10, 167772190000000000), (111 + INVALID_FP, 9223373686122217472));
assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
}
#[test]
fn compute_float_f64_test() {
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740993), (1065 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
assert_eq!(compute_float64(0, 9007199254740995), (1065 + INVALID_FP, 9223372036854778880));
assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
assert_eq!(compute_float64(0, 18014398509481986), (1066 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
assert_eq!(compute_float64(0, 18014398509481990), (1066 + INVALID_FP, 9223372036854778880));
assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
assert_eq!(compute_float64(-3, 9007199254740993000), (1065 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
assert_eq!(compute_float64(-3, 9007199254740995000), (1065 + INVALID_FP, 9223372036854778879));
assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
}
@@ -0,0 +1,63 @@
#![cfg(feature = "radix")]
mod stackvec;
use lexical_parse_float::bigint::{Bigfloat, Limb};
use lexical_parse_float::float::ExtendedFloat80;
use stackvec::vec_from_u32;
#[test]
fn simple_test() {
let x = Bigfloat::new();
assert_eq!(x.exp, 0);
let y = Bigfloat::from_float(ExtendedFloat80 {
mant: 1 << 63,
exp: -63,
});
assert_eq!(y.exp, -63);
let x = Bigfloat::from_u32(1);
assert_eq!(&*x.data, &[1]);
let mut x = Bigfloat::from_u64(1);
assert_eq!(&*x.data, &[1]);
x.pow(10, 10);
assert_eq!(&*x.data, &[9765625]);
assert_eq!(x.exp, 10);
x.shl_bits(1);
assert_eq!(&*x.data, &[19531250]);
assert_eq!(x.exp, 10);
x.shl_limbs(1);
assert_eq!(&*x.data, &[0, 19531250]);
assert_eq!(x.exp, 10);
assert_eq!(x.leading_zeros(), Limb::BITS - 25);
// y has a 0 for 32-bit limbs, no 0s for 64-bit limbs.
x *= &y;
let expected = if Limb::BITS == 32 {
vec_from_u32(&[0, 0, 0, 9765625])
} else {
vec_from_u32(&[0, 0, 0, 0, 9765625])
};
assert!(x.data == expected, "failed");
assert_eq!(x.exp, -53);
}
#[test]
fn leading_zeros_test() {
assert_eq!(Bigfloat::new().leading_zeros(), 0);
assert_eq!(Bigfloat::from_u32(0xFF).leading_zeros(), Limb::BITS - 8);
assert_eq!(Bigfloat::from_u64(0xFF00000000).leading_zeros(), 24);
assert_eq!(Bigfloat::from_u32(0xF).leading_zeros(), Limb::BITS - 4);
assert_eq!(Bigfloat::from_u64(0xF00000000).leading_zeros(), 28);
assert_eq!(Bigfloat::from_u32(0xF0).leading_zeros(), Limb::BITS - 8);
assert_eq!(Bigfloat::from_u64(0xF000000000).leading_zeros(), 24);
}
@@ -0,0 +1,26 @@
mod stackvec;
use lexical_parse_float::bigint::Bigint;
use stackvec::vec_from_u32;
#[test]
fn simple_test() {
let x = Bigint::new();
assert_eq!(x.hi64(), (0, false));
let x = Bigint::from_u32(1);
assert_eq!(&*x.data, &[1]);
let mut x = Bigint::from_u64(1);
assert_eq!(&*x.data, &[1]);
x.pow(10, 10);
let expected = vec_from_u32(&[1410065408, 2]);
assert!(x.data == expected, "failed");
assert_eq!(x.bit_length(), 34);
let y = Bigint::from_u64(5);
x *= &y;
let expected = vec_from_u32(&[2755359744, 11]);
assert!(x.data == expected, "failed");
}
@@ -0,0 +1,161 @@
#![cfg(feature = "power-of-two")]
use lexical_parse_float::binary::{binary, slow_binary};
use lexical_parse_float::number::Number;
use lexical_util::format::NumberFormatBuilder;
const BINARY: u128 = NumberFormatBuilder::from_radix(2);
const BASE4: u128 = NumberFormatBuilder::from_radix(4);
const OCTAL: u128 = NumberFormatBuilder::from_radix(8);
const HEX: u128 = NumberFormatBuilder::from_radix(16);
const BASE32: u128 = NumberFormatBuilder::from_radix(32);
fn compute_float32<const FORMAT: u128>(q: i64, w: u64, many_digits: bool) -> (i32, u64) {
let num = Number {
exponent: q,
mantissa: w,
is_negative: false,
many_digits,
integer: &[],
fraction: None,
};
let fp = binary::<f32, FORMAT>(&num, false);
(fp.exp, fp.mant)
}
fn compute_float64<const FORMAT: u128>(q: i64, w: u64, many_digits: bool) -> (i32, u64) {
let num = Number {
exponent: q,
mantissa: w,
is_negative: false,
many_digits,
integer: &[],
fraction: None,
};
let fp = binary::<f64, FORMAT>(&num, false);
(fp.exp, fp.mant)
}
#[test]
fn computef32_test() {
// Halfway, round-down tests
assert_eq!(compute_float32::<BINARY>(0, 16777216, false), (151, 0));
assert_eq!(compute_float32::<BINARY>(0, 16777217, false), (151, 0));
assert_eq!(compute_float32::<BINARY>(0, 16777218, false), (151, 1));
assert_eq!(compute_float32::<BINARY>(0, 33554432, false), (152, 0));
assert_eq!(compute_float32::<BINARY>(0, 33554434, false), (152, 0));
assert_eq!(compute_float32::<BINARY>(0, 33554436, false), (152, 1));
}
#[test]
fn halfway_round_down_test() {
// Halfway, round-down tests
assert_eq!(compute_float64::<BINARY>(0, 9007199254740992, false), (1076, 0));
assert_eq!(compute_float64::<BINARY>(0, 9007199254740993, false), (1076, 0));
assert_eq!(compute_float64::<BINARY>(0, 9007199254740994, false), (1076, 1));
assert_eq!(compute_float64::<BINARY>(0, 18014398509481984, false), (1077, 0));
assert_eq!(compute_float64::<BINARY>(0, 18014398509481986, false), (1077, 0));
assert_eq!(compute_float64::<BINARY>(0, 18014398509481988, false), (1077, 1));
assert_eq!(compute_float64::<BINARY>(0, 9223372036854775808, false), (1086, 0));
assert_eq!(compute_float64::<BINARY>(0, 9223372036854776832, false), (1086, 0));
assert_eq!(compute_float64::<BINARY>(0, 9223372036854777856, false), (1086, 1));
// Add a 0 but say we're truncated.
assert_eq!(compute_float64::<BINARY>(-10, 9223372036854775808, true), (1076, 0));
assert_eq!(
compute_float64::<BINARY>(-10, 9223372036854776832, true),
(-31703, 9223372036854776832)
);
assert_eq!(compute_float64::<BINARY>(-10, 9223372036854777856, true), (1076, 1));
// Check other bases.
assert_eq!(compute_float64::<BASE4>(-2, 144115188075855872, false), (1076, 0));
assert_eq!(compute_float64::<BASE4>(-2, 144115188075855888, false), (1076, 0));
assert_eq!(compute_float64::<BASE4>(-2, 144115188075855904, false), (1076, 1));
assert_eq!(compute_float64::<OCTAL>(-2, 576460752303423488, false), (1076, 0));
assert_eq!(compute_float64::<OCTAL>(-2, 576460752303423552, false), (1076, 0));
assert_eq!(compute_float64::<OCTAL>(-2, 576460752303423616, false), (1076, 1));
assert_eq!(compute_float64::<HEX>(-1, 144115188075855872, false), (1076, 0));
assert_eq!(compute_float64::<HEX>(-1, 144115188075855888, false), (1076, 0));
assert_eq!(compute_float64::<HEX>(-1, 144115188075855904, false), (1076, 1));
assert_eq!(compute_float64::<BASE32>(-1, 288230376151711744, false), (1076, 0));
assert_eq!(compute_float64::<BASE32>(-1, 288230376151711776, false), (1076, 0));
assert_eq!(compute_float64::<BASE32>(-1, 288230376151711808, false), (1076, 1));
}
#[test]
fn test_halfway_round_up() {
// Halfway, round-up tests
assert_eq!(compute_float64::<BINARY>(0, 9007199254740994, false), (1076, 1));
assert_eq!(compute_float64::<BINARY>(0, 9007199254740995, false), (1076, 2));
assert_eq!(compute_float64::<BINARY>(0, 9007199254740996, false), (1076, 2));
assert_eq!(compute_float64::<BINARY>(0, 18014398509481988, false), (1077, 1));
assert_eq!(compute_float64::<BINARY>(0, 18014398509481990, false), (1077, 2));
assert_eq!(compute_float64::<BINARY>(0, 18014398509481992, false), (1077, 2));
assert_eq!(compute_float64::<BINARY>(0, 9223372036854777856, false), (1086, 1));
assert_eq!(compute_float64::<BINARY>(0, 9223372036854778880, false), (1086, 2));
assert_eq!(compute_float64::<BINARY>(0, 9223372036854779904, false), (1086, 2));
// Add a 0 but say we're truncated.
assert_eq!(compute_float64::<BINARY>(-10, 9223372036854777856, true), (1076, 1));
assert_eq!(compute_float64::<BINARY>(-10, 9223372036854778879, true), (1076, 1));
assert_eq!(compute_float64::<BINARY>(-10, 9223372036854778880, true), (1076, 2));
assert_eq!(compute_float64::<BINARY>(-10, 9223372036854779904, true), (1076, 2));
// Check other bases.
assert_eq!(compute_float64::<BASE4>(-2, 144115188075855904, false), (1076, 1));
assert_eq!(compute_float64::<BASE4>(-2, 144115188075855920, false), (1076, 2));
assert_eq!(compute_float64::<BASE4>(-2, 144115188075855936, false), (1076, 2));
assert_eq!(compute_float64::<OCTAL>(-2, 576460752303423616, false), (1076, 1));
assert_eq!(compute_float64::<OCTAL>(-2, 576460752303423680, false), (1076, 2));
assert_eq!(compute_float64::<OCTAL>(-2, 576460752303423744, false), (1076, 2));
assert_eq!(compute_float64::<HEX>(-1, 144115188075855904, false), (1076, 1));
assert_eq!(compute_float64::<HEX>(-1, 144115188075855920, false), (1076, 2));
assert_eq!(compute_float64::<HEX>(-1, 144115188075855936, false), (1076, 2));
assert_eq!(compute_float64::<BASE32>(-1, 288230376151711808, false), (1076, 1));
assert_eq!(compute_float64::<BASE32>(-1, 288230376151711840, false), (1076, 2));
assert_eq!(compute_float64::<BASE32>(-1, 288230376151711872, false), (1076, 2));
}
fn compute_float64_slow<const FORMAT: u128>(
integer: &[u8],
fraction: Option<&[u8]>,
exponent: i64,
) -> (i32, u64) {
let num = Number {
exponent,
mantissa: 0,
is_negative: false,
many_digits: false,
integer,
fraction,
};
let fp = slow_binary::<f64, FORMAT>(num);
(fp.exp, fp.mant)
}
#[test]
fn test_slow() {
let i = b"100000000000000000000000000000000000000000000000000001";
let f = b"0000000000000";
assert_eq!(compute_float64_slow::<BINARY>(i, Some(f), -10), (1076, 0));
let i = b"100000000000000000000000000000000000000000000000000001";
let f = b"000000000000000000001";
assert_eq!(compute_float64_slow::<BINARY>(i, Some(f), -10), (1076, 1));
let i = b"100000000000000000000000000000000000000000000000000001";
let f = b"000000000000010000000";
assert_eq!(compute_float64_slow::<BINARY>(i, Some(f), -10), (1076, 1));
}
@@ -0,0 +1,165 @@
use lexical_parse_float::float::{self, RawFloat};
use lexical_parse_float::limits::ExactFloat;
use lexical_util::num::Float;
#[test]
fn exponent_fast_path_test() {
assert_eq!(f32::min_exponent_fast_path(10), -10);
assert_eq!(f32::max_exponent_fast_path(10), 10);
assert_eq!(f32::max_exponent_disguised_fast_path(10), 17);
assert_eq!(f64::min_exponent_fast_path(10), -22);
assert_eq!(f64::max_exponent_fast_path(10), 22);
assert_eq!(f64::max_exponent_disguised_fast_path(10), 37);
}
fn slow_f32_power(exponent: usize, radix: u32) -> f32 {
let mut value: f32 = 1.0;
for _ in 0..exponent {
value *= radix as f32;
}
value
}
fn slow_f64_power(exponent: usize, radix: u32) -> f64 {
let mut value: f64 = 1.0;
for _ in 0..exponent {
value *= radix as f64;
}
value
}
fn pow_fast_path(radix: u32) {
for exponent in 0..f32::exponent_limit(radix).1 + 1 {
let exponent = exponent as usize;
let actual = f32::pow_fast_path(exponent, radix);
assert_eq!(actual, slow_f32_power(exponent, radix));
}
for exponent in 0..f64::exponent_limit(radix).1 + 1 {
let exponent = exponent as usize;
let actual = f64::pow_fast_path(exponent, radix);
assert_eq!(actual, slow_f64_power(exponent, radix));
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn pow_fast_path_test() {
pow_fast_path(10);
if cfg!(feature = "power-of-two") {
pow_fast_path(2);
pow_fast_path(4);
pow_fast_path(8);
pow_fast_path(16);
pow_fast_path(32);
}
if cfg!(feature = "radix") {
pow_fast_path(3);
pow_fast_path(5);
pow_fast_path(6);
pow_fast_path(7);
pow_fast_path(9);
pow_fast_path(11);
pow_fast_path(12);
pow_fast_path(13);
pow_fast_path(14);
pow_fast_path(15);
pow_fast_path(17);
pow_fast_path(18);
pow_fast_path(19);
pow_fast_path(20);
pow_fast_path(21);
pow_fast_path(22);
pow_fast_path(23);
pow_fast_path(24);
pow_fast_path(25);
pow_fast_path(26);
pow_fast_path(27);
pow_fast_path(28);
pow_fast_path(29);
pow_fast_path(30);
pow_fast_path(31);
pow_fast_path(33);
pow_fast_path(34);
pow_fast_path(35);
pow_fast_path(36);
}
}
fn slow_int_power(exponent: usize, radix: u32) -> u64 {
let mut value: u64 = 1;
for _ in 0..exponent {
value *= radix as u64;
}
value
}
fn int_pow_fast_path(radix: u32) {
for exponent in 0..f64::mantissa_limit(radix) {
let exponent = exponent as usize;
let actual = f64::int_pow_fast_path(exponent, radix);
assert_eq!(actual, slow_int_power(exponent, radix));
}
}
#[test]
fn int_pow_fast_path_test() {
int_pow_fast_path(10);
if cfg!(feature = "power-of-two") {
int_pow_fast_path(2);
int_pow_fast_path(4);
int_pow_fast_path(8);
int_pow_fast_path(16);
int_pow_fast_path(32);
}
if cfg!(feature = "radix") {
int_pow_fast_path(3);
int_pow_fast_path(5);
int_pow_fast_path(6);
int_pow_fast_path(7);
int_pow_fast_path(9);
int_pow_fast_path(11);
int_pow_fast_path(12);
int_pow_fast_path(13);
int_pow_fast_path(14);
int_pow_fast_path(15);
int_pow_fast_path(17);
int_pow_fast_path(18);
int_pow_fast_path(19);
int_pow_fast_path(20);
int_pow_fast_path(21);
int_pow_fast_path(22);
int_pow_fast_path(23);
int_pow_fast_path(24);
int_pow_fast_path(25);
int_pow_fast_path(26);
int_pow_fast_path(27);
int_pow_fast_path(28);
int_pow_fast_path(29);
int_pow_fast_path(30);
int_pow_fast_path(31);
int_pow_fast_path(33);
int_pow_fast_path(34);
int_pow_fast_path(35);
int_pow_fast_path(36);
}
}
fn extended_to_float<F: RawFloat>(mantissa: u64, exponent: i32, expected: F) {
let fp = float::ExtendedFloat80 {
mant: mantissa,
exp: exponent,
};
assert_eq!(float::extended_to_float::<F>(fp), expected);
}
#[test]
fn extended_to_float_test() {
let max_mant = (1 << f64::MANTISSA_SIZE) - 1;
let max_exp = f64::INFINITE_POWER - 1;
extended_to_float::<f64>(0, 0, 0.0);
extended_to_float::<f64>(1, 0, 5e-324);
extended_to_float::<f64>(max_mant, max_exp, f64::MAX);
extended_to_float::<f64>(0, 1076, 9007199254740992.0);
extended_to_float::<f64>(1, 1076, 9007199254740994.0);
}
@@ -0,0 +1,435 @@
#![cfg(feature = "format")]
use core::num;
use lexical_parse_float::{
Error,
FromLexical,
FromLexicalWithOptions,
NumberFormatBuilder,
Options,
};
use lexical_util::format::STANDARD;
#[test]
fn issue_96_test() {
const OPTS: Options = Options::new();
const NO_CONSECUTIVE: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(true)
.internal_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(false)
.build_strict();
const CONSECUTIVE: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(true)
.internal_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(true)
.build_strict();
const NO_LEADING: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(false)
.internal_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(true)
.build_strict();
let result = f64::from_lexical(b"_-1234");
assert_eq!(result, Err(Error::InvalidDigit(0)));
let result = f64::from_lexical_with_options::<NO_CONSECUTIVE>(b"_-1234", &OPTS);
assert_eq!(result, Err(Error::InvalidDigit(1)));
let result = f64::from_lexical_with_options::<NO_LEADING>(b"^-1234", &OPTS);
assert_eq!(result, Err(Error::InvalidDigit(0)));
// NOTE: This uis correct, since it's "trailing"
let result = f64::from_lexical_with_options::<NO_LEADING>(b"_-1234", &OPTS);
assert_eq!(result, Err(Error::InvalidDigit(1)));
let result = f64::from_lexical_with_options::<NO_LEADING>(b"_1234", &OPTS);
assert_eq!(result, Err(Error::InvalidDigit(0)));
let result = f64::from_lexical_with_options::<NO_LEADING>(b"X1234", &OPTS);
assert_eq!(result, Err(Error::InvalidDigit(0)));
let result = f64::from_lexical_with_options::<NO_CONSECUTIVE>(b"__1__234__", &OPTS);
assert_eq!(result, Err(Error::InvalidDigit(0)));
let result = f64::from_lexical_with_options::<CONSECUTIVE>(b"__1__234__", &OPTS);
assert_eq!(result, Ok(1234f64));
}
#[test]
fn issue_96_i_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.internal_digit_separator(true)
.consecutive_digit_separator(false)
.build_strict();
let result = f64::from_lexical_partial_with_options::<FMT>(b"", &OPTS);
assert_eq!(result, Err(Error::Empty(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1__1_23", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1", &OPTS);
assert_eq!(result, Ok((11f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1_23", &OPTS);
assert_eq!(result, Ok((1123f64, 6)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1__23", &OPTS);
assert_eq!(result, Ok((11f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1_23_", &OPTS);
assert_eq!(result, Ok((1123f64, 6)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1_23.", &OPTS);
assert_eq!(result, Ok((1123f64, 7)));
}
#[test]
fn issue_96_l_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(true)
.consecutive_digit_separator(false)
.build_strict();
let result = f64::from_lexical_partial_with_options::<FMT>(b"", &OPTS);
assert_eq!(result, Err(Error::Empty(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_", &OPTS);
assert_eq!(result, Err(Error::Empty(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_", &OPTS);
assert_eq!(result, Err(Error::Empty(2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_23", &OPTS);
assert_eq!(result, Ok((1f64, 2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_1_23", &OPTS);
assert_eq!(result, Ok((1f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1__1_23", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_+1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
}
#[test]
fn issue_96_t_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.trailing_digit_separator(true)
.consecutive_digit_separator(false)
.build_strict();
let result = f64::from_lexical_partial_with_options::<FMT>(b"", &OPTS);
assert_eq!(result, Err(Error::Empty(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_", &OPTS);
assert_eq!(result, Err(Error::Empty(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_", &OPTS);
assert_eq!(result, Err(Error::Empty(2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1__1_23", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_+1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123_", &OPTS);
assert_eq!(result, Ok((123f64, 5)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123__", &OPTS);
assert_eq!(result, Ok((123f64, 4)));
}
#[test]
fn issue_96_il_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.internal_digit_separator(true)
.leading_digit_separator(true)
.consecutive_digit_separator(false)
.build_strict();
let result = f64::from_lexical_partial_with_options::<FMT>(b"", &OPTS);
assert_eq!(result, Err(Error::Empty(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_", &OPTS);
assert_eq!(result, Err(Error::Empty(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_", &OPTS);
assert_eq!(result, Err(Error::Empty(2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_23", &OPTS);
assert_eq!(result, Ok((123f64, 5)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_1_23", &OPTS);
assert_eq!(result, Ok((123f64, 6)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1__1_23", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1", &OPTS);
assert_eq!(result, Ok((11f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1_", &OPTS);
assert_eq!(result, Ok((11f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_+1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123_", &OPTS);
assert_eq!(result, Ok((123f64, 4)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123__", &OPTS);
assert_eq!(result, Ok((123f64, 4)));
}
#[test]
fn issue_96_it_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.internal_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(false)
.build_strict();
let result = f64::from_lexical_partial_with_options::<FMT>(b"", &OPTS);
assert_eq!(result, Err(Error::Empty(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_", &OPTS);
assert_eq!(result, Err(Error::Empty(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_", &OPTS);
assert_eq!(result, Err(Error::Empty(2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(0)));
let result: Result<(f64, usize), Error> =
f64::from_lexical_partial_with_options::<FMT>(b"+_1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1__1_23", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1", &OPTS);
assert_eq!(result, Ok((11f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1_", &OPTS);
assert_eq!(result, Ok((11f64, 4)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_+1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123_", &OPTS);
assert_eq!(result, Ok((123f64, 5)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123__", &OPTS);
assert_eq!(result, Ok((123f64, 4)));
}
#[test]
fn issue_96_lt_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(false)
.build_strict();
let result = f64::from_lexical_partial_with_options::<FMT>(b"", &OPTS);
assert_eq!(result, Err(Error::Empty(0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_", &OPTS);
assert_eq!(result, Err(Error::Empty(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_", &OPTS);
assert_eq!(result, Err(Error::Empty(2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_23", &OPTS);
assert_eq!(result, Ok((1f64, 2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_1_23", &OPTS);
assert_eq!(result, Ok((1f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1__1_23", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1_", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_11_", &OPTS);
assert_eq!(result, Ok((11f64, 4)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_+1_23", &OPTS);
assert_eq!(result, Err(Error::EmptyMantissa(1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123_", &OPTS);
assert_eq!(result, Ok((123f64, 5)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123__", &OPTS);
assert_eq!(result, Ok((123f64, 4)));
}
#[test]
fn issue_96_no_required_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(false)
.required_digits(false)
.build_strict();
let result = f64::from_lexical_partial_with_options::<FMT>(b"", &OPTS);
assert_eq!(result, Ok((0f64, 0)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_", &OPTS);
assert_eq!(result, Ok((0f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_", &OPTS);
assert_eq!(result, Ok((0f64, 2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_1_23", &OPTS);
assert_eq!(result, Ok((1f64, 2)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+_1_23", &OPTS);
assert_eq!(result, Ok((1f64, 3)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1__1_23", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"1_1_", &OPTS);
assert_eq!(result, Ok((1f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_11_", &OPTS);
assert_eq!(result, Ok((11f64, 4)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"_+1_23", &OPTS);
assert_eq!(result, Ok((0f64, 1)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123_", &OPTS);
assert_eq!(result, Ok((123f64, 5)));
let result = f64::from_lexical_partial_with_options::<FMT>(b"+123__", &OPTS);
assert_eq!(result, Ok((123f64, 4)));
}
#[test]
fn issue_96_rounding_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(true)
.internal_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(true)
.build_strict();
let input = b"0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002225073858507200889024586876085859887650423112240959465493524802562440009228235695178775888803759155264230978095043431208587738715835729182199302029437922422355981982750124204178896957131179108226104397197960400045489739193807919893608152561311337614984204327175103362739154978273159414382813627511383860409424946494228631669542910508020181592664213499660651780309507591305871984642390606863710200510872328278467884363194451586613504122347901479236958520832159762106637540161373658304419360371477835530668283453563400507407304013560296804637591858316312422452159926254649430083685186171942241764645513713542013221703137049658321015465406803539741790602258950302350193751977303094576317321085250729930508976158251915";
let result = f32::from_lexical_partial_with_options::<STANDARD>(input, &OPTS);
assert_eq!(result, Ok((0f32, input.len())));
let result = f32::from_lexical_partial_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok((0f32, input.len())));
let result = f64::from_lexical_partial_with_options::<STANDARD>(input, &OPTS);
assert_eq!(result, Ok((2.225073858507201e-308f64, input.len())));
let result = f64::from_lexical_partial_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok((2.225073858507201e-308f64, input.len())));
let input = b"_0e+___00";
let result = f32::from_lexical_partial_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok((0f32, input.len())));
let result = f32::from_lexical_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok(0f32));
let result = f64::from_lexical_partial_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok((0f64, input.len())));
let result = f64::from_lexical_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok(0f64));
let input = b"323081493377685546875e-297";
let result = f64::from_lexical_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok(3.2308149337768557e-277));
let input = b"32308_1493_3776_8554_6875e-297";
let result = f64::from_lexical_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok(3.2308149337768557e-277));
}
#[test]
fn issue_96_wuff_test() {
const OPTS: Options = Options::new();
const FMT: u128 = NumberFormatBuilder::new()
.digit_separator(num::NonZeroU8::new(b'_'))
.leading_digit_separator(true)
.internal_digit_separator(true)
.trailing_digit_separator(true)
.consecutive_digit_separator(true)
.build_strict();
let input = b"0.000061094760894775390625";
let result = f32::from_lexical_partial_with_options::<STANDARD>(input, &OPTS);
assert_eq!(result, Ok((6.109476e-5f32, input.len())));
let result = f64::from_lexical_partial_with_options::<STANDARD>(input, &OPTS);
assert_eq!(result, Ok((6.109476089477539e-5, input.len())));
let input = b"0_.0000610_9476_0894775390_625";
let result = f32::from_lexical_partial_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok((6.109476e-5f32, input.len())));
let result = f64::from_lexical_partial_with_options::<FMT>(input, &OPTS);
assert_eq!(result, Ok((6.109476089477539e-5, input.len())));
}
@@ -0,0 +1,67 @@
#![cfg(all(feature = "power-of-two", feature = "format"))]
use std::assert_eq;
use lexical_parse_float::FromLexicalWithOptions;
use lexical_parse_float::NumberFormatBuilder;
use lexical_parse_float::Options;
use lexical_util::error::Error;
#[test]
fn issue_98_test() {
const DECIMAL_FORMAT: u128 = NumberFormatBuilder::new()
.required_digits(true)
.no_positive_mantissa_sign(false)
.no_special(true)
.no_integer_leading_zeros(true)
.no_float_leading_zeros(true)
.build_strict();
let result = f64::from_lexical_with_options::<DECIMAL_FORMAT>(b"1.1.0", &Options::new());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::InvalidDigit(3));
assert_eq!(
f64::from_lexical_partial_with_options::<DECIMAL_FORMAT>(b"1.1.0", &Options::new()),
Ok((1.1f64, 3))
);
assert_eq!(
f64::from_lexical_with_options::<DECIMAL_FORMAT>(b"1.1", &Options::new()),
Ok(1.1f64)
);
assert_eq!(
f64::from_lexical_partial_with_options::<DECIMAL_FORMAT>(b"1.1", &Options::new()),
Ok((1.1f64, 3))
);
let result = f64::from_lexical_with_options::<DECIMAL_FORMAT>(b"0.1.0", &Options::new());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::InvalidDigit(3));
assert_eq!(
f64::from_lexical_partial_with_options::<DECIMAL_FORMAT>(b"0.1.0", &Options::new()),
Ok((0.1f64, 3))
);
assert_eq!(
f64::from_lexical_with_options::<DECIMAL_FORMAT>(b"0.1", &Options::new()),
Ok(0.1f64)
);
assert_eq!(
f64::from_lexical_partial_with_options::<DECIMAL_FORMAT>(b"0.1", &Options::new()),
Ok((0.1f64, 3))
);
let result = f64::from_lexical_with_options::<DECIMAL_FORMAT>(b"01.1.0", &Options::new());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0));
let result = f64::from_lexical_with_options::<DECIMAL_FORMAT>(b"00.1", &Options::new());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0));
assert_eq!(
f64::from_lexical_partial_with_options::<DECIMAL_FORMAT>(b"10.1", &Options::new()),
Ok((10.1, 4))
);
assert_eq!(
f64::from_lexical_partial_with_options::<DECIMAL_FORMAT>(b"11.1", &Options::new()),
Ok((11.1, 4))
);
}
@@ -0,0 +1,309 @@
//! These tests are adapted from the Rust core library's unittests.
#![cfg(not(feature = "compact"))]
use lexical_parse_float::lemire;
use lexical_parse_float::shared::INVALID_FP;
fn compute_error32(q: i64, w: u64) -> (i32, u64) {
let fp = lemire::compute_error::<f32>(q, w);
(fp.exp, fp.mant)
}
fn compute_error64(q: i64, w: u64) -> (i32, u64) {
let fp = lemire::compute_error::<f64>(q, w);
(fp.exp, fp.mant)
}
fn compute_error_scaled32(q: i64, w: u64, lz: i32) -> (i32, u64) {
let fp = lemire::compute_error_scaled::<f32>(q, w, lz);
(fp.exp, fp.mant)
}
fn compute_error_scaled64(q: i64, w: u64, lz: i32) -> (i32, u64) {
let fp = lemire::compute_error_scaled::<f64>(q, w, lz);
(fp.exp, fp.mant)
}
fn compute_float32(q: i64, w: u64) -> (i32, u64) {
let fp = lemire::compute_float::<f32>(q, w, false);
(fp.exp, fp.mant)
}
fn compute_float64(q: i64, w: u64) -> (i32, u64) {
let fp = lemire::compute_float::<f64>(q, w, false);
(fp.exp, fp.mant)
}
#[test]
fn compute_error32_test() {
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_error32(0, 16777216), (111 + INVALID_FP, 9223372036854775808));
assert_eq!(compute_error32(0, 16777217), (111 + INVALID_FP, 9223372586610589696));
assert_eq!(compute_error32(0, 16777218), (111 + INVALID_FP, 9223373136366403584));
assert_eq!(compute_error32(0, 16777219), (111 + INVALID_FP, 9223373686122217472));
assert_eq!(compute_error32(0, 16777220), (111 + INVALID_FP, 9223374235878031360));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_error32(-10, 167772160000000000), (111 + INVALID_FP, 9223372036854775808));
assert_eq!(compute_error32(-10, 167772170000000000), (111 + INVALID_FP, 9223372586610589696));
assert_eq!(compute_error32(-10, 167772180000000000), (111 + INVALID_FP, 9223373136366403584));
// Let's check the lines to see if anything is different in table...
assert_eq!(compute_error32(-10, 167772190000000000), (111 + INVALID_FP, 9223373686122217472));
assert_eq!(compute_error32(-10, 167772200000000000), (111 + INVALID_FP, 9223374235878031360));
}
#[test]
fn compute_error64_test() {
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_error64(0, 9007199254740992), (1065 + INVALID_FP, 9223372036854775808));
assert_eq!(compute_error64(0, 9007199254740993), (1065 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_error64(0, 9007199254740994), (1065 + INVALID_FP, 9223372036854777856));
assert_eq!(compute_error64(0, 9007199254740995), (1065 + INVALID_FP, 9223372036854778880));
assert_eq!(compute_error64(0, 9007199254740996), (1065 + INVALID_FP, 9223372036854779904));
assert_eq!(compute_error64(0, 18014398509481984), (1066 + INVALID_FP, 9223372036854775808));
assert_eq!(compute_error64(0, 18014398509481986), (1066 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_error64(0, 18014398509481988), (1066 + INVALID_FP, 9223372036854777856));
assert_eq!(compute_error64(0, 18014398509481990), (1066 + INVALID_FP, 9223372036854778880));
assert_eq!(compute_error64(0, 18014398509481992), (1066 + INVALID_FP, 9223372036854779904));
// Test a much closer set of examples.
assert_eq!(compute_error64(0, 9007199254740991), (1064 + INVALID_FP, 18446744073709549568));
assert_eq!(compute_error64(0, 9223372036854776831), (1075 + INVALID_FP, 9223372036854776830));
assert_eq!(compute_error64(0, 9223372036854776832), (1075 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_error64(0, 9223372036854776833), (1075 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_error64(-42, 9123456727292927), (925 + INVALID_FP, 13021432563531497894));
assert_eq!(compute_error64(-43, 91234567272929275), (925 + INVALID_FP, 13021432563531498606));
assert_eq!(compute_error64(-42, 9123456727292928), (925 + INVALID_FP, 13021432563531499320));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_error64(-3, 9007199254740992000), (1065 + INVALID_FP, 9223372036854775808));
assert_eq!(compute_error64(-3, 9007199254740993000), (1065 + INVALID_FP, 9223372036854776832));
assert_eq!(compute_error64(-3, 9007199254740994000), (1065 + INVALID_FP, 9223372036854777856));
assert_eq!(compute_error64(-3, 9007199254740995000), (1065 + INVALID_FP, 9223372036854778880));
assert_eq!(compute_error64(-3, 9007199254740996000), (1065 + INVALID_FP, 9223372036854779904));
// Test from errors in atof.
assert_eq!(compute_error64(-18, 1000000178813934326), (1012 + INVALID_FP, 9223373686122217470));
// Check edge-cases from previous errors.
assert_eq!(
compute_error64(-342, 2470328229206232720),
(-64 + INVALID_FP, 18446744073709551608)
);
}
#[test]
fn compute_error_scaled32_test() {
// These are the same examples above, just using pre-computed scaled values.
// These test near-halfway cases for single-precision floats.
assert_eq!(
compute_error_scaled32(0, 4611686018427387904, 39),
(111 + INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled32(0, 4611686293305294848, 39),
(111 + INVALID_FP, 9223372586610589696)
);
assert_eq!(
compute_error_scaled32(0, 4611686568183201792, 39),
(111 + INVALID_FP, 9223373136366403584)
);
assert_eq!(
compute_error_scaled32(0, 4611686843061108736, 39),
(111 + INVALID_FP, 9223373686122217472)
);
assert_eq!(
compute_error_scaled32(0, 4611687117939015680, 39),
(111 + INVALID_FP, 9223374235878031360)
);
assert_eq!(
compute_error_scaled32(-10, 9223372036854775808, 6),
(111 + INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled32(-10, 9223372586610589696, 6),
(111 + INVALID_FP, 9223372586610589696)
);
assert_eq!(
compute_error_scaled32(-10, 9223373136366403584, 6),
(111 + INVALID_FP, 9223373136366403584)
);
assert_eq!(
compute_error_scaled32(-10, 9223373686122217472, 6),
(111 + INVALID_FP, 9223373686122217472)
);
assert_eq!(
compute_error_scaled32(-10, 9223374235878031360, 6),
(111 + INVALID_FP, 9223374235878031360)
);
}
#[test]
fn compute_error_scaled64_test() {
// These are the same examples above, just using pre-computed scaled values.
// These test near-halfway cases for double-precision floats.
assert_eq!(
compute_error_scaled64(0, 4611686018427387904, 10),
(1065 + INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 10),
(1065 + INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388928, 10),
(1065 + INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389440, 10),
(1065 + INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389952, 10),
(1065 + INVALID_FP, 9223372036854779904)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427387904, 9),
(1066 + INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 9),
(1066 + INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388928, 9),
(1066 + INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389440, 9),
(1066 + INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427389952, 9),
(1066 + INVALID_FP, 9223372036854779904)
);
// Test a much closer set of examples.
assert_eq!(
compute_error_scaled64(0, 9223372036854774784, 11),
(1064 + INVALID_FP, 18446744073709549568)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388415, 0),
(1075 + INVALID_FP, 9223372036854776830)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 0),
(1075 + INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(0, 4611686018427388416, 0),
(1075 + INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(-42, 6510716281765748947, 10),
(925 + INVALID_FP, 13021432563531497894)
);
assert_eq!(
compute_error_scaled64(-43, 6510716281765749303, 7),
(925 + INVALID_FP, 13021432563531498606)
);
assert_eq!(
compute_error_scaled64(-42, 6510716281765749660, 10),
(925 + INVALID_FP, 13021432563531499320)
);
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(
compute_error_scaled64(-3, 9223372036854775808, 1),
(1065 + INVALID_FP, 9223372036854775808)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854776832, 1),
(1065 + INVALID_FP, 9223372036854776832)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854777856, 1),
(1065 + INVALID_FP, 9223372036854777856)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854778880, 1),
(1065 + INVALID_FP, 9223372036854778880)
);
assert_eq!(
compute_error_scaled64(-3, 9223372036854779904, 1),
(1065 + INVALID_FP, 9223372036854779904)
);
// Test from errors in atof.
assert_eq!(
compute_error_scaled64(-18, 9223373686122217470, 4),
(1012 + INVALID_FP, 9223373686122217470)
);
// Check edge-cases from previous errors.
assert_eq!(
compute_error_scaled64(-342, 9223372036854775804, 2),
(-64 + INVALID_FP, 18446744073709551608)
);
}
#[test]
fn compute_float_f32_rounding() {
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_float32(0, 16777216), (151, 0));
assert_eq!(compute_float32(0, 16777217), (151, 0));
assert_eq!(compute_float32(0, 16777218), (151, 1));
assert_eq!(compute_float32(0, 16777219), (151, 2));
assert_eq!(compute_float32(0, 16777220), (151, 2));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
assert_eq!(compute_float32(-10, 167772170000000000), (151, 0));
assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
// Let's check the lines to see if anything is different in table...
assert_eq!(compute_float32(-10, 167772190000000000), (151, 2));
assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
}
#[test]
fn compute_float_f64_rounding() {
// Also need to check halfway cases **inside** that exponent range.
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740993), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
assert_eq!(compute_float64(0, 9007199254740995), (1076, 2));
assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
assert_eq!(compute_float64(0, 18014398509481986), (1077, 0));
assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
assert_eq!(compute_float64(0, 18014398509481990), (1077, 2));
assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
// Test a much closer set of examples.
assert_eq!(compute_float64(0, 9007199254740991), (1075, 4503599627370495));
assert_eq!(compute_float64(0, 9223372036854776831), (1086, 0));
assert_eq!(compute_float64(0, 9223372036854776832), (1086, 0));
assert_eq!(compute_float64(0, 9223372036854776833), (1086, 1));
assert_eq!(compute_float64(-42, 9123456727292927), (936, 1854521741541368));
assert_eq!(compute_float64(-43, 91234567272929275), (936, 1854521741541369));
assert_eq!(compute_float64(-42, 9123456727292928), (936, 1854521741541369));
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0));
assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2));
assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
}
@@ -0,0 +1,292 @@
#![cfg(all(not(feature = "std"), feature = "compact"))]
// These are adapted from libm, a port of musl libc's libm to Rust.
// libm can be found online [here](https://github.com/rust-lang/libm),
// and is similarly licensed under an Apache2.0/MIT license
use core::f64;
use lexical_parse_float::libm;
#[test]
fn fabsf_sanity_test() {
assert_eq!(libm::fabsf(-1.0), 1.0);
assert_eq!(libm::fabsf(2.8), 2.8);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs
#[test]
fn fabsf_spec_test() {
assert!(libm::fabsf(f32::NAN).is_nan());
for f in [0.0, -0.0].iter().copied() {
assert_eq!(libm::fabsf(f), 0.0);
}
for f in [f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
assert_eq!(libm::fabsf(f), f32::INFINITY);
}
}
#[test]
fn sqrtf_sanity_test() {
assert_eq!(libm::sqrtf(100.0), 10.0);
assert_eq!(libm::sqrtf(4.0), 2.0);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt
#[test]
fn sqrtf_spec_test() {
// Not Asserted: FE_INVALID exception is raised if argument is negative.
assert!(libm::sqrtf(-1.0).is_nan());
assert!(libm::sqrtf(f32::NAN).is_nan());
for f in [0.0, -0.0, f32::INFINITY].iter().copied() {
assert_eq!(libm::sqrtf(f), f);
}
}
const POS_ZERO: &[f64] = &[0.0];
const NEG_ZERO: &[f64] = &[-0.0];
const POS_ONE: &[f64] = &[1.0];
const NEG_ONE: &[f64] = &[-1.0];
const POS_FLOATS: &[f64] = &[99.0 / 70.0, f64::consts::E, f64::consts::PI];
const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -f64::consts::E, -f64::consts::PI];
const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), f64::MIN_POSITIVE, f64::EPSILON];
const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -f64::MIN_POSITIVE, -f64::EPSILON];
const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, f64::MAX];
const NEG_EVENS: &[f64] = &[f64::MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0];
const POS_ODDS: &[f64] = &[3.0, 7.0];
const NEG_ODDS: &[f64] = &[-7.0, -3.0];
const NANS: &[f64] = &[f64::NAN];
const POS_INF: &[f64] = &[f64::INFINITY];
const NEG_INF: &[f64] = &[f64::NEG_INFINITY];
const ALL: &[&[f64]] = &[
POS_ZERO,
NEG_ZERO,
NANS,
NEG_SMALL_FLOATS,
POS_SMALL_FLOATS,
NEG_FLOATS,
POS_FLOATS,
NEG_EVENS,
POS_EVENS,
NEG_ODDS,
POS_ODDS,
NEG_INF,
POS_INF,
NEG_ONE,
POS_ONE,
];
const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF];
const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF];
fn powd(base: f64, exponent: f64, expected: f64) {
let res = libm::powd(base, exponent);
assert!(
if expected.is_nan() {
res.is_nan()
} else {
libm::powd(base, exponent) == expected
},
"{} ** {} was {} instead of {}",
base,
exponent,
res,
expected
);
}
fn powd_test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) {
sets.iter().for_each(|s| s.iter().for_each(|val| powd(*val, exponent, expected)));
}
fn powd_test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) {
sets.iter().for_each(|s| s.iter().for_each(|val| powd(base, *val, expected)));
}
fn powd_test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) {
sets.iter().for_each(|s| {
s.iter().for_each(|val| {
let exp = expected(*val);
let res = computed(*val);
assert!(
if exp.is_nan() {
res.is_nan()
} else {
exp == res
},
"test for {} was {} instead of {}",
val,
res,
exp
);
})
});
}
#[test]
fn powd_zero_as_exponent() {
powd_test_sets_as_base(ALL, 0.0, 1.0);
powd_test_sets_as_base(ALL, -0.0, 1.0);
}
#[test]
fn powd_one_as_base() {
powd_test_sets_as_exponent(1.0, ALL, 1.0);
}
#[test]
fn powd_nan_inputs() {
// NAN as the base:
// (NAN ^ anything *but 0* should be NAN)
powd_test_sets_as_exponent(f64::NAN, &ALL[2..], f64::NAN);
// NAN as the exponent:
// (anything *but 1* ^ NAN should be NAN)
powd_test_sets_as_base(&ALL[..(ALL.len() - 2)], f64::NAN, f64::NAN);
}
#[test]
fn powd_infinity_as_base() {
// Positive Infinity as the base:
// (+Infinity ^ positive anything but 0 and NAN should be +Infinity)
powd_test_sets_as_exponent(f64::INFINITY, &POS[1..], f64::INFINITY);
// (+Infinity ^ negative anything except 0 and NAN should be 0.0)
powd_test_sets_as_exponent(f64::INFINITY, &NEG[1..], 0.0);
// Negative Infinity as the base:
// (-Infinity ^ positive odd ints should be -Infinity)
powd_test_sets_as_exponent(f64::NEG_INFINITY, &[POS_ODDS], f64::NEG_INFINITY);
// (-Infinity ^ anything but odd ints should be == -0 ^ (-anything))
// We can lump in pos/neg odd ints here because they don't seem to
// cause panics (div by zero) in release mode (I think).
powd_test_sets(ALL, &|v: f64| libm::powd(f64::NEG_INFINITY, v), &|v: f64| libm::powd(-0.0, -v));
}
#[test]
fn infinity_as_exponent() {
// Positive/Negative base greater than 1:
// (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the
// base)
powd_test_sets_as_base(&ALL[5..(ALL.len() - 2)], f64::INFINITY, f64::INFINITY);
// (pos/neg > 1 ^ -Infinity should be 0.0)
powd_test_sets_as_base(&ALL[5..ALL.len() - 2], f64::NEG_INFINITY, 0.0);
// Positive/Negative base less than 1:
let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS];
// (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base)
powd_test_sets_as_base(base_below_one, f64::INFINITY, 0.0);
// (pos/neg < 1 ^ -Infinity should be Infinity)
powd_test_sets_as_base(base_below_one, f64::NEG_INFINITY, f64::INFINITY);
// Positive/Negative 1 as the base:
// (pos/neg 1 ^ Infinity should be 1)
powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::INFINITY, 1.0);
// (pos/neg 1 ^ -Infinity should be 1)
powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::NEG_INFINITY, 1.0);
}
#[test]
fn powd_zero_as_base() {
// Positive Zero as the base:
// (+0 ^ anything positive but 0 and NAN should be +0)
powd_test_sets_as_exponent(0.0, &POS[1..], 0.0);
// (+0 ^ anything negative but 0 and NAN should be Infinity)
// (this should panic because we're dividing by zero)
powd_test_sets_as_exponent(0.0, &NEG[1..], f64::INFINITY);
// Negative Zero as the base:
// (-0 ^ anything positive but 0, NAN, and odd ints should be +0)
powd_test_sets_as_exponent(-0.0, &POS[3..], 0.0);
// (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity)
// (should panic because of divide by zero)
powd_test_sets_as_exponent(-0.0, &NEG[3..], f64::INFINITY);
// (-0 ^ positive odd ints should be -0)
powd_test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0);
// (-0 ^ negative odd ints should be -Infinity)
// (should panic because of divide by zero)
powd_test_sets_as_exponent(-0.0, &[NEG_ODDS], f64::NEG_INFINITY);
}
#[test]
fn special_cases() {
// One as the exponent:
// (anything ^ 1 should be anything - i.e. the base)
powd_test_sets(ALL, &|v: f64| libm::powd(v, 1.0), &|v: f64| v);
// Negative One as the exponent:
// (anything ^ -1 should be 1/anything)
powd_test_sets(ALL, &|v: f64| libm::powd(v, -1.0), &|v: f64| 1.0 / v);
// Factoring -1 out:
// (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^
// integer))
[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS].iter().for_each(|int_set| {
int_set.iter().for_each(|int| {
powd_test_sets(ALL, &|v: f64| libm::powd(-v, *int), &|v: f64| {
libm::powd(-1.0, *int) * libm::powd(v, *int)
});
})
});
// Negative base (imaginary results):
// (-anything except 0 and Infinity ^ non-integer should be NAN)
NEG[1..(NEG.len() - 1)].iter().for_each(|set| {
set.iter().for_each(|val| {
powd_test_sets(&ALL[3..7], &|v: f64| libm::powd(*val, v), &|_| f64::NAN);
})
});
}
#[test]
fn normal_cases() {
assert_eq!(libm::powd(2.0, 20.0), (1 << 20) as f64);
assert_eq!(libm::powd(-1.0, 9.0), -1.0);
assert!(libm::powd(-1.0, 2.2).is_nan());
assert!(libm::powd(-1.0, -1.14).is_nan());
}
#[test]
fn fabsd_sanity_test() {
assert_eq!(libm::fabsd(-1.0), 1.0);
assert_eq!(libm::fabsd(2.8), 2.8);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs
#[test]
fn fabsd_spec_test() {
assert!(libm::fabsd(f64::NAN).is_nan());
for f in [0.0, -0.0].iter().copied() {
assert_eq!(libm::fabsd(f), 0.0);
}
for f in [f64::INFINITY, f64::NEG_INFINITY].iter().copied() {
assert_eq!(libm::fabsd(f), f64::INFINITY);
}
}
#[test]
fn sqrtd_sanity_test() {
assert_eq!(libm::sqrtd(100.0), 10.0);
assert_eq!(libm::sqrtd(4.0), 2.0);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt
#[test]
fn sqrtd_spec_test() {
// Not Asserted: FE_INVALID exception is raised if argument is negative.
assert!(libm::sqrtd(-1.0).is_nan());
assert!(libm::sqrtd(f64::NAN).is_nan());
for f in [0.0, -0.0, f64::INFINITY].iter().copied() {
assert_eq!(libm::sqrtd(f), f);
}
}
@@ -0,0 +1,27 @@
use lexical_parse_float::limits::{self, ExactFloat, MaxDigits};
#[test]
fn mantissa_limit_test() {
assert_eq!(f32::mantissa_limit(10), 7);
assert_eq!(f64::mantissa_limit(10), 15);
}
#[test]
fn exponent_limit_test() {
assert_eq!(f32::exponent_limit(10), (-10, 10));
assert_eq!(f64::exponent_limit(10), (-22, 22));
}
#[test]
fn power_limit_test() {
assert_eq!(limits::u32_power_limit(5), 13);
assert_eq!(limits::u32_power_limit(10), 9);
assert_eq!(limits::u64_power_limit(5), 27);
assert_eq!(limits::u64_power_limit(10), 19);
}
#[test]
fn max_digit_test() {
assert_eq!(f32::max_digits(10), Some(114));
assert_eq!(f64::max_digits(10), Some(769));
}
@@ -0,0 +1,16 @@
use lexical_parse_float::mask;
#[test]
fn lower_n_mask_test() {
assert_eq!(mask::lower_n_mask(2), 0b11);
}
#[test]
fn lower_n_halfway_test() {
assert_eq!(mask::lower_n_halfway(2), 0b10);
}
#[test]
fn nth_bit_test() {
assert_eq!(mask::nth_bit(2), 0b100);
}
@@ -0,0 +1,95 @@
use lexical_parse_float::number::Number;
use lexical_util::format::STANDARD;
#[test]
fn is_fast_path_test() {
let mut number = Number {
exponent: -4,
mantissa: 12345,
is_negative: false,
many_digits: false,
integer: &[],
fraction: None,
};
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), true);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), true);
number.exponent = -15;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), true);
number.exponent = -25;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), false);
number.exponent = 25;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), true);
number.exponent = 36;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), true);
number.exponent = 38;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), false);
number.mantissa = 1 << 25;
number.exponent = 0;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), true);
number.mantissa = 1 << 54;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), false);
number.mantissa = 1 << 52;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), true);
number.many_digits = true;
assert_eq!(number.is_fast_path::<f32, { STANDARD }>(), false);
assert_eq!(number.is_fast_path::<f64, { STANDARD }>(), false);
}
#[test]
fn try_fast_path_test() {
let mut number = Number {
exponent: -4,
mantissa: 12345,
is_negative: false,
many_digits: false,
integer: &[],
fraction: None,
};
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), Some(1.2345));
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), Some(1.2345));
number.exponent = -10;
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), Some(1.2345e-6));
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), Some(1.2345e-6));
number.exponent = -20;
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), None);
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), Some(1.2345e-16));
number.exponent = -25;
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), None);
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), None);
number.exponent = 12;
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), Some(1.2345e16));
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), Some(1.2345e16));
number.exponent = 25;
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), None);
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), Some(1.2345e29));
number.exponent = 32;
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), None);
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), Some(1.2345e36));
number.exponent = 36;
assert_eq!(number.try_fast_path::<f32, { STANDARD }>(), None);
assert_eq!(number.try_fast_path::<f64, { STANDARD }>(), None);
}
@@ -0,0 +1,135 @@
use lexical_parse_float::options::{Options, OptionsBuilder};
#[test]
fn invalid_exponent_test() {
let mut builder = OptionsBuilder::default();
builder = builder.exponent(b'\x00');
assert!(!builder.is_valid());
builder = builder.exponent(b'\x7f');
assert!(!builder.is_valid());
assert!(builder.build().is_err());
builder = builder.exponent(b'^');
assert!(builder.is_valid());
assert!(builder.build().is_ok());
}
#[test]
fn invalid_decimal_point_test() {
let mut builder = OptionsBuilder::default();
builder = builder.decimal_point(b'\x00');
assert!(!builder.is_valid());
builder = builder.decimal_point(b'\x7f');
assert!(!builder.is_valid());
assert!(builder.build().is_err());
builder = builder.decimal_point(b',');
assert!(builder.is_valid());
assert!(builder.build().is_ok());
}
#[test]
fn invalid_nan_test() {
let mut builder = OptionsBuilder::default();
builder = builder.nan_string(Some(b"naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan"));
assert!(!builder.is_valid());
builder = builder.nan_string(Some(b"inf"));
assert!(!builder.is_valid());
builder = builder.nan_string(Some(b"na00n"));
assert!(!builder.is_valid());
assert!(builder.build().is_err());
builder = builder.nan_string(Some(b"nan"));
assert!(builder.is_valid());
assert!(builder.build().is_ok());
builder = builder.nan_string(None);
assert!(builder.is_valid());
}
#[test]
fn invalid_inf_test() {
let mut builder = OptionsBuilder::default();
builder = builder.inf_string(Some(b"innnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnf"));
assert!(!builder.is_valid());
builder = builder.inf_string(Some(b"nan"));
assert!(!builder.is_valid());
builder = builder.inf_string(Some(b"in00f"));
assert!(!builder.is_valid());
assert!(builder.build().is_err());
builder = builder.inf_string(Some(b"i"));
assert!(builder.is_valid());
builder = builder.inf_string(Some(b"inf"));
assert!(builder.is_valid());
assert!(builder.build().is_ok());
builder = builder.inf_string(None);
assert!(builder.is_valid());
builder = builder.infinity_string(None);
assert!(builder.is_valid());
}
#[test]
fn invalid_infinity_test() {
let mut builder = OptionsBuilder::default();
builder =
builder.infinity_string(Some(b"innnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnf"));
assert!(!builder.is_valid());
builder = builder.infinity_string(Some(b"nan"));
assert!(!builder.is_valid());
builder = builder.infinity_string(Some(b"i"));
assert!(!builder.is_valid());
builder = builder.inf_string(Some(b"infi000nity"));
assert!(!builder.is_valid());
assert!(builder.build().is_err());
builder = builder.inf_string(Some(b"i"));
assert!(builder.is_valid());
builder = builder.infinity_string(Some(b"infinity"));
assert!(builder.is_valid());
assert!(builder.build().is_ok());
builder = builder.infinity_string(None);
assert!(!builder.is_valid());
builder = builder.inf_string(None);
assert!(builder.is_valid());
}
#[test]
fn builder_test() {
let mut builder = OptionsBuilder::default();
builder = builder.lossy(true);
builder = builder.exponent(b'^');
builder = builder.decimal_point(b',');
builder = builder.nan_string(Some(b"nan"));
builder = builder.inf_string(Some(b"Infinity"));
builder = builder.infinity_string(Some(b"Infiniiiiiity"));
assert_eq!(builder.get_lossy(), true);
assert_eq!(builder.get_exponent(), b'^');
assert_eq!(builder.get_decimal_point(), b',');
assert_eq!(builder.get_nan_string(), Some("nan".as_bytes()));
assert_eq!(builder.get_inf_string(), Some("Infinity".as_bytes()));
assert_eq!(builder.get_infinity_string(), Some("Infiniiiiiity".as_bytes()));
assert!(builder.is_valid());
assert_eq!(builder.build(), Ok(builder.build_unchecked()));
}
#[test]
#[allow(deprecated)]
fn options_test() {
let mut opts = Options::new();
opts.set_lossy(true);
opts.set_exponent(b'^');
opts.set_decimal_point(b',');
opts.set_nan_string(Some(b"nan"));
opts.set_inf_string(Some(b"Infinity"));
opts.set_infinity_string(Some(b"Infiniiiiiity"));
assert_eq!(opts.lossy(), true);
assert_eq!(opts.exponent(), b'^');
assert_eq!(opts.decimal_point(), b',');
assert_eq!(opts.nan_string(), Some("nan".as_bytes()));
assert_eq!(opts.inf_string(), Some("Infinity".as_bytes()));
assert_eq!(opts.infinity_string(), Some("Infiniiiiiity".as_bytes()));
assert!(opts.is_valid());
assert_eq!(Options::builder(), OptionsBuilder::new());
assert_eq!(opts.rebuild().build(), Ok(opts));
}
@@ -0,0 +1,267 @@
use lexical_parse_float::options::Options;
use lexical_parse_float::parse;
use lexical_util::format::STANDARD;
use lexical_util::iterator::AsBytes;
use lexical_util::step::u64_step;
#[test]
fn parse_complete_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let string = b"1.2345e10";
let result = parse::parse_complete::<f64, FORMAT>(string, &OPTIONS);
assert_eq!(result, Ok(1.2345e10));
let string = b"1.2345e";
let result = parse::parse_complete::<f64, FORMAT>(string, &OPTIONS);
assert!(result.is_err());
let string = b"1.2345 ";
let result = parse::parse_complete::<f64, FORMAT>(string, &OPTIONS);
assert!(result.is_err());
}
#[test]
fn fast_path_complete_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let string = b"1.2345e10";
let result = parse::fast_path_complete::<f64, FORMAT>(string, &OPTIONS);
assert_eq!(result, Ok(1.2345e10));
let string = b"1.2345e";
let result = parse::fast_path_complete::<f64, FORMAT>(string, &OPTIONS);
assert!(result.is_err());
let string = b"1.2345 ";
let result = parse::fast_path_complete::<f64, FORMAT>(string, &OPTIONS);
assert!(result.is_err());
}
#[test]
fn parse_partial_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let string = b"1.2345e10";
let result = parse::parse_partial::<f64, FORMAT>(string, &OPTIONS);
assert_eq!(result, Ok((1.2345e10, 9)));
let string = b"1.2345e";
let result = parse::parse_partial::<f64, FORMAT>(string, &OPTIONS);
assert!(result.is_err());
let string = b"1.2345 ";
let result = parse::parse_partial::<f64, FORMAT>(string, &OPTIONS);
assert_eq!(result, Ok((1.2345, 6)));
}
#[test]
fn fast_path_partial_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let string = b"1.2345e10";
let result = parse::fast_path_partial::<f64, FORMAT>(string, &OPTIONS);
assert_eq!(result, Ok((1.2345e10, 9)));
let string = b"1.2345e";
let result = parse::fast_path_partial::<f64, FORMAT>(string, &OPTIONS);
assert!(result.is_err());
let string = b"1.2345 ";
let result = parse::fast_path_partial::<f64, FORMAT>(string, &OPTIONS);
assert_eq!(result, Ok((1.2345, 6)));
}
#[test]
fn parse_number_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let string = b"1.2345e10";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_complete_number(byte, false, &OPTIONS);
assert!(result.is_ok());
let num = result.unwrap();
assert_eq!(num.mantissa, 12345);
assert_eq!(num.exponent, 6);
assert_eq!(num.many_digits, false);
let string = b"1.2345e";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_complete_number(byte, false, &OPTIONS);
assert!(result.is_err());
let string = b"1.2345 ";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_complete_number(byte, false, &OPTIONS);
assert!(result.is_err());
}
#[test]
fn parse_partial_number_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let string = b"1.2345e10";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_partial_number(byte, false, &OPTIONS);
assert!(result.is_ok());
let (num, count) = result.unwrap();
assert_eq!(num.mantissa, 12345);
assert_eq!(num.exponent, 6);
assert_eq!(num.many_digits, false);
assert_eq!(count, 9);
let string = b"1.2345e";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_partial_number(byte, false, &OPTIONS);
assert!(result.is_err());
let string = b"1.2345 ";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_partial_number(byte, false, &OPTIONS);
assert!(result.is_ok());
let (num, count) = result.unwrap();
assert_eq!(num.mantissa, 12345);
assert_eq!(num.exponent, -4);
assert_eq!(num.many_digits, false);
assert_eq!(count, 6);
// Leading zeros
let string = b"00000000000000000000001.2345 ";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_partial_number(byte, false, &OPTIONS);
assert!(result.is_ok());
let (num, count) = result.unwrap();
assert_eq!(num.mantissa, 12345);
assert_eq!(num.exponent, -4);
assert_eq!(num.many_digits, false);
assert_eq!(count, 28);
// Leading zeros
let string = b"0.00000000000000000000012345 ";
let byte = string.bytes::<{ FORMAT }>();
let result = parse::parse_partial_number(byte, false, &OPTIONS);
assert!(result.is_ok());
let (num, count) = result.unwrap();
assert_eq!(num.mantissa, 12345);
assert_eq!(num.exponent, -26);
assert_eq!(num.many_digits, false);
assert_eq!(count, 28);
}
#[test]
fn parse_digits_test() {
const FORMAT: u128 = STANDARD;
let mut mantissa: u64 = 0;
let digits = b"1234567890123456789012345";
let mut byte = digits.bytes::<{ FORMAT }>();
parse::parse_digits(byte.integer_iter(), 10, |digit| {
mantissa = mantissa.wrapping_mul(10).wrapping_add(digit as _);
});
assert_eq!(mantissa, 1096246371337559929);
}
#[test]
#[cfg(not(feature = "compact"))]
fn parse_8digits_test() {
const FORMAT: u128 = STANDARD;
let mut mantissa: u64 = 0;
let digits = b"1234567890123456789012345";
let mut byte = digits.bytes::<{ FORMAT }>();
parse::parse_8digits::<_, FORMAT>(byte.integer_iter(), &mut mantissa);
// We don't check for overflow.
assert_eq!(mantissa, 11177671081359486962);
}
#[test]
fn parse_u64_digits_test() {
const FORMAT: u128 = STANDARD;
let mut mantissa: u64 = 0;
let mut step = u64_step(10);
let digits = b"1234567890123456789012345";
let mut byte = digits.bytes::<{ FORMAT }>();
parse::parse_u64_digits::<_, FORMAT>(byte.integer_iter(), &mut mantissa, &mut step);
assert_eq!(mantissa, 1234567890123456789);
assert_eq!(step, 0);
let mut mantissa: u64 = 0;
let mut step = u64_step(10);
let digits = b"1234567890123456789";
let mut byte = digits.bytes::<{ FORMAT }>();
parse::parse_u64_digits::<_, FORMAT>(byte.integer_iter(), &mut mantissa, &mut step);
assert_eq!(mantissa, 1234567890123456789);
assert_eq!(step, 0);
}
#[test]
fn is_special_eq_test() {
const FORMAT: u128 = STANDARD;
let digits = b"NaN";
let byte = digits.bytes::<{ FORMAT }>();
assert_eq!(parse::is_special_eq::<FORMAT>(byte.clone(), b"nan"), 3);
let byte = digits.bytes::<{ FORMAT }>();
assert_eq!(parse::is_special_eq::<FORMAT>(byte.clone(), b"NaN"), 3);
let byte = digits.bytes::<{ FORMAT }>();
assert_eq!(parse::is_special_eq::<FORMAT>(byte.clone(), b"inf"), 0);
}
#[test]
fn parse_positive_special_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let digits = b"NaN";
let byte = digits.bytes::<{ FORMAT }>();
let result = parse::parse_positive_special::<f64, FORMAT>(byte, &OPTIONS).unwrap();
assert_eq!(result.1, 3);
assert!(f64::is_nan(result.0));
let digits = b"NaN1";
let byte = digits.bytes::<{ FORMAT }>();
let result = parse::parse_positive_special::<f64, FORMAT>(byte, &OPTIONS).unwrap();
assert_eq!(result.1, 3);
assert!(f64::is_nan(result.0));
let digits = b"inf";
let byte = digits.bytes::<{ FORMAT }>();
let result = parse::parse_positive_special::<f64, FORMAT>(byte, &OPTIONS).unwrap();
assert_eq!(result.1, 3);
assert!(f64::is_infinite(result.0));
let digits = b"in";
let byte = digits.bytes::<{ FORMAT }>();
let result = parse::parse_positive_special::<f64, FORMAT>(byte, &OPTIONS);
assert_eq!(result, None);
}
#[test]
fn parse_partial_special_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let digits = b"NaN";
let byte = digits.bytes::<{ FORMAT }>();
let result = parse::parse_partial_special::<f64, FORMAT>(byte, true, &OPTIONS).unwrap();
assert_eq!(result.1, 3);
assert!(f64::is_nan(result.0));
assert!(f64::is_sign_negative(result.0));
}
#[test]
fn parse_parse_special_test() {
const FORMAT: u128 = STANDARD;
const OPTIONS: Options = Options::new();
let digits = b"NaN";
let byte = digits.bytes::<{ FORMAT }>();
let result = parse::parse_special::<f64, FORMAT>(byte, true, &OPTIONS).unwrap();
assert!(f64::is_nan(result));
assert!(f64::is_sign_negative(result));
let digits = b"NaN1";
let byte = digits.bytes::<{ FORMAT }>();
let result = parse::parse_special::<f64, FORMAT>(byte, true, &OPTIONS);
assert_eq!(result, None);
}
@@ -0,0 +1,110 @@
use lexical_parse_float::float::ExtendedFloat80;
use lexical_parse_float::shared;
#[cfg(feature = "power-of-two")]
use lexical_util::format::NumberFormatBuilder;
#[test]
fn calculate_shift_test() {
assert_eq!(shared::calculate_shift::<f64>(-63), 64);
assert_eq!(shared::calculate_shift::<f64>(-15), 16);
assert_eq!(shared::calculate_shift::<f64>(-8), 11);
assert_eq!(shared::calculate_shift::<f64>(0), 11);
assert_eq!(shared::calculate_shift::<f64>(50), 11);
}
#[test]
#[cfg(feature = "power-of-two")]
fn calculate_power2_test() {
const BASE4: u128 = NumberFormatBuilder::from_radix(4);
assert_eq!(shared::calculate_power2::<f64, BASE4>(-63, 5), 944);
assert_eq!(shared::calculate_power2::<f64, BASE4>(-15, 5), 1040);
assert_eq!(shared::calculate_power2::<f64, BASE4>(-8, 0), 1059);
assert_eq!(shared::calculate_power2::<f64, BASE4>(-8, 5), 1054);
assert_eq!(shared::calculate_power2::<f64, BASE4>(0, 5), 1070);
assert_eq!(shared::calculate_power2::<f64, BASE4>(50, 5), 1170);
}
#[test]
fn log2_test() {
assert_eq!(shared::log2(2), 1);
assert_eq!(shared::log2(4), 2);
assert_eq!(shared::log2(10), 1);
}
#[test]
fn starts_with_test() {
assert_eq!(shared::starts_with(b"NaN".iter(), b"nAN".iter()), false);
assert_eq!(shared::starts_with(b"nAN".iter(), b"nAN".iter()), true);
assert_eq!(shared::starts_with(b"nAN1".iter(), b"nAN".iter()), true);
assert_eq!(shared::starts_with(b"nAN1".iter(), b"nAN12".iter()), false);
}
#[test]
fn starts_with_uncased_test() {
assert_eq!(shared::starts_with_uncased(b"NaN".iter(), b"nAN".iter()), true);
assert_eq!(shared::starts_with_uncased(b"nAN".iter(), b"nAN".iter()), true);
assert_eq!(shared::starts_with_uncased(b"nAN1".iter(), b"nAN".iter()), true);
assert_eq!(shared::starts_with_uncased(b"nAN1".iter(), b"nAN12".iter()), false);
}
#[test]
fn round_test() {
let mut fp = ExtendedFloat80 {
mant: 9223372036854776832,
exp: -10,
};
shared::round::<f64, _>(&mut fp, |f, s| {
f.mant >>= s;
f.exp += s;
});
assert_eq!(fp.mant, 0);
assert_eq!(fp.exp, 1);
let mut fp = ExtendedFloat80 {
mant: 9223372036854776832,
exp: -10,
};
shared::round::<f64, _>(&mut fp, |f, s| {
f.mant >>= s;
f.exp += s;
// Round-up.
f.mant += 1;
});
assert_eq!(fp.mant, 1);
assert_eq!(fp.exp, 1);
// Round-down
let mut fp = ExtendedFloat80 {
mant: 9223372036854776832,
exp: -10,
};
shared::round::<f64, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| {
is_above || (is_odd && is_halfway)
});
});
assert_eq!(fp.mant, 0);
assert_eq!(fp.exp, 1);
// Round up
let mut fp = ExtendedFloat80 {
mant: 9223372036854778880,
exp: -10,
};
shared::round::<f64, _>(&mut fp, |f, s| {
shared::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| {
is_above || (is_odd && is_halfway)
});
});
assert_eq!(fp.mant, 2);
assert_eq!(fp.exp, 1);
// Round down
let mut fp = ExtendedFloat80 {
mant: 9223372036854778880,
exp: -10,
};
shared::round::<f64, _>(&mut fp, shared::round_down);
assert_eq!(fp.mant, 1);
assert_eq!(fp.exp, 1);
}
@@ -0,0 +1,695 @@
mod stackvec;
#[cfg(feature = "radix")]
use core::cmp;
#[cfg(feature = "radix")]
use lexical_parse_float::bigint::Bigfloat;
use lexical_parse_float::bigint::Bigint;
use lexical_parse_float::float::{ExtendedFloat80, RawFloat};
use lexical_parse_float::limits::MaxDigits;
use lexical_parse_float::number::Number;
use lexical_parse_float::slow;
use lexical_util::format::STANDARD;
use stackvec::vec_from_u32;
fn b<F: RawFloat>(float: F) -> (u64, i32) {
let fp = slow::b(float);
(fp.mant, fp.exp)
}
fn bh<F: RawFloat>(float: F) -> (u64, i32) {
let fp = slow::bh(float);
(fp.mant, fp.exp)
}
#[test]
fn b_test() {
assert_eq!(b(1e-45_f32), (1, -149));
assert_eq!(b(5e-324_f64), (1, -1074));
assert_eq!(b(1e-323_f64), (2, -1074));
assert_eq!(b(2e-323_f64), (4, -1074));
assert_eq!(b(3e-323_f64), (6, -1074));
assert_eq!(b(4e-323_f64), (8, -1074));
assert_eq!(b(5e-323_f64), (10, -1074));
assert_eq!(b(6e-323_f64), (12, -1074));
assert_eq!(b(7e-323_f64), (14, -1074));
assert_eq!(b(8e-323_f64), (16, -1074));
assert_eq!(b(9e-323_f64), (18, -1074));
assert_eq!(b(1_f32), (8388608, -23));
assert_eq!(b(1_f64), (4503599627370496, -52));
assert_eq!(b(1e38_f32), (9860761, 103));
assert_eq!(b(1e308_f64), (5010420900022432, 971));
}
#[test]
fn bh_test() {
assert_eq!(bh(1e-45_f32), (3, -150));
assert_eq!(bh(5e-324_f64), (3, -1075));
assert_eq!(bh(1_f32), (16777217, -24));
assert_eq!(bh(1_f64), (9007199254740993, -53));
assert_eq!(bh(1e38_f32), (19721523, 102));
assert_eq!(bh(1e308_f64), (10020841800044865, 970));
}
#[test]
fn slow_radix_test() {
const FORMAT: u128 = STANDARD;
// 5e-324, round-down.
let mut num = Number {
mantissa: 2470328229206232720,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"2",
fraction: Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999"),
};
let fp = ExtendedFloat80 {
mant: 1 << 63,
exp: -63,
};
let result = slow::slow_radix::<f64, FORMAT>(num.clone(), fp);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, round-up.
num.fraction = Some(b"47032822920623272088284396434110686182529901307162382212792841250337753635104375932649918180817996189898282347722858865463328355177969898199387398005390939063150356595155702263922908583924491051844359318028499365361525003193704576782492193656236698636584807570015857692699037063119282795585513329278343384093519780155312465972635795746227664652728272200563740064854999770965994704540208281662262378573934507363390079677619305775067401763246736009689513405355374585166611342237666786041621596804619144672918403005300575308490487653917113865916462395249126236538818796362393732804238910186723484976682350898633885879256283027559956575244555072551893136908362547791869486679949683240497058210285131854513962138377228261454376934125320985913276672363281251");
let result = slow::slow_radix::<f64, FORMAT>(num.clone(), fp);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 0);
// 8.98846567431158e+307
let mut num = Number {
mantissa: 8988465674311580536,
exponent: 289,
is_negative: false,
many_digits: true,
integer: b"8",
fraction: Some(b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032"),
};
let fp = ExtendedFloat80 {
mant: 9223372036854776832,
exp: 2035,
};
let result = slow::slow_radix::<f64, FORMAT>(num.clone(), fp);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 2046);
// 8.988465674311582e+307
num.fraction = Some(b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296474151486978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080321");
let result = slow::slow_radix::<f64, FORMAT>(num.clone(), fp);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 2046);
}
#[test]
fn digit_comp_test() {
const FORMAT: u128 = STANDARD;
let max_digits = f64::max_digits(10).unwrap();
// 5e-324, round-down.
let num = Number {
mantissa: 2470328229206232720,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"2",
fraction: Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999"),
};
let fp = ExtendedFloat80 {
mant: 1 << 63,
exp: -63,
};
let result = slow::digit_comp::<f64, FORMAT>(num.clone(), fp, -324, max_digits);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 1e-323, round-up.
let num = Number {
mantissa: 7410984687618698162,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"7",
fraction: Some(b"4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375"),
};
let fp = ExtendedFloat80 {
mant: 1 << 63,
exp: -62,
};
let result = slow::digit_comp::<f64, FORMAT>(num.clone(), fp, -324, max_digits);
assert_eq!(result.mant, 2);
assert_eq!(result.exp, 0);
// 8.98846567431158e+307
let mut num = Number {
mantissa: 8988465674311580536,
exponent: 289,
is_negative: false,
many_digits: true,
integer: b"8",
fraction: Some(b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032"),
};
let fp = ExtendedFloat80 {
mant: 9223372036854776832,
exp: 2035,
};
let result = slow::digit_comp::<f64, FORMAT>(num.clone(), fp, 307, max_digits);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 2046);
// 8.988465674311582e+307
num.fraction = Some(b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296474151486978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080321");
let result = slow::digit_comp::<f64, FORMAT>(num.clone(), fp, 307, max_digits);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 2046);
}
#[test]
fn positive_digit_comp_test() {
const FORMAT: u128 = STANDARD;
// 8.98846567431158e+307
let bigmant = Bigint {
data: vec_from_u32(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1024, 2147483648,
]),
};
let exponent = 307 + 1 - 308;
let result = slow::positive_digit_comp::<f64, FORMAT>(bigmant, exponent);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 2046);
// 8.988465674311582e+307
let bigmant = Bigint {
data: vec_from_u32(&[
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1024, 2147483648,
]),
};
let exponent = 307 + 1 - 308;
let result = slow::positive_digit_comp::<f64, FORMAT>(bigmant, exponent);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 2046);
}
#[test]
fn negative_digit_comp_test() {
const FORMAT: u128 = STANDARD;
// 5e-324, below halfway, round-down to 0.0.
let bigmant = Bigint {
data: vec_from_u32(&[
1727738439, 330069557, 3509095598, 686205316, 156923684, 750687444, 2688855918,
28211928, 1887482096, 3222998811, 913348873, 1652282845, 1600735541, 1664240266,
84454144, 1487769792, 1855966778, 2832488299, 507030148, 1410055467, 2513359584,
3453963205, 779237894, 3456088326, 3671009895, 3094451696, 1250165638, 2682979794,
357925323, 1713890438, 3271046672, 3485897285, 3934710962, 1813530592, 199705026,
976390839, 2805488572, 2194288220, 2094065006, 2592523639, 3798974617, 586957244,
1409218821, 3442050171, 3789534764, 1380190380, 2055222457, 3535299831, 429482276,
389342206, 133558576, 721875297, 3013586570, 540178306, 2389746866, 2313334501,
422440635, 1288499129, 864978311, 842263325, 3016323856, 2282442263, 1440906063,
3931458696, 3511314276, 1884879882, 946366824, 4260548261, 1073379659, 1732329252,
3828972211, 1915607049, 3665440937, 1844358779, 3735281178, 2646335050, 1457460927,
2940016422, 1051,
]),
};
let fp = ExtendedFloat80 {
mant: 1 << 63,
exp: -63,
};
let exponent = -324 + 1 - 755;
let result = slow::negative_digit_comp::<f64, FORMAT>(bigmant, fp, exponent);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, halfway, round-down to 0.0.
let bigmant = Bigint {
data: vec_from_u32(&[
2084786877, 507136210, 2666388819, 3110242527, 3178432722, 541916566, 208847286,
3092404665, 83491860, 2893735989, 3973758097, 2600107496, 147629623, 1754010897,
4226332273, 2587058081, 942453804, 88731834, 1319061990, 173208747, 1982493283,
3808794987, 3874839738, 1854586992, 3508364323, 2021729080, 1899625710, 2420749567,
816401711, 3059730605, 1570934109, 3138812023, 1756281367, 3205859133, 2985201975,
1014588672, 3799556578, 577719905, 4052248225, 3649019757, 398935965, 56421532,
976366795, 1876047791, 3147705595, 4025764546, 1097271882, 1910500779, 2397021233,
1340419138, 2753207595, 3067328524, 2210626776, 1280440432, 3940874757, 4172726578,
1035509558, 1062145421, 1465448826, 2990139501, 1785427751, 2093931515, 4055890033,
3388365687, 2245484242, 3609657408, 3527114516, 1013577862, 2389075196, 426934091,
3237939346, 1071362463, 4070999470, 250952461, 2280067948, 1097862995, 2226250520,
221983348, 1,
]),
};
let exponent = -324 + 1 - 752;
let result = slow::negative_digit_comp::<f64, FORMAT>(bigmant, fp, exponent);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, above halfway, round-up to 5e-324.
let bigmant = Bigint {
data: vec_from_u32(&[
3667999587, 776394808, 894084415, 1037654204, 1719556155, 1124198371, 2088472861,
859275578, 834918607, 3167556114, 1082875312, 231271193, 1476296236, 360239786,
3608617070, 100777043, 834603454, 887318342, 305718012, 1732087473, 2645063646,
3728211506, 93691724, 1366000745, 723904866, 3037421624, 1816387920, 2732659194,
3869049819, 532534979, 2824439209, 1323349161, 382944493, 1993820262, 4082215981,
1555952134, 3635827414, 1482231762, 1867776587, 2130459211, 3989359658, 564215320,
1173733358, 1580608728, 1412284882, 1602939803, 2382784237, 1925138608, 2495375854,
519289497, 1762272177, 608514174, 631431287, 4214469733, 754041908, 3072560125,
1765160997, 2031519620, 1769586374, 4131591237, 674408332, 3759445970, 1904194670,
3818885807, 980005947, 1736835717, 911406800, 1545844036, 2415915482, 4269340915,
2314622388, 2123690045, 2055289038, 2509524619, 1325843000, 2388695363, 787668722,
2219833485, 10,
]),
};
let exponent = -324 + 1 - 753;
let result = slow::negative_digit_comp::<f64, FORMAT>(bigmant, fp, exponent);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 0);
// 1e-323, below halfway, round-down to 5e-324.
let bigmant = Bigint {
data: vec_from_u32(&[
888248023, 990208672, 1937352202, 2058615950, 470771052, 2252062332, 3771600458,
84635785, 1367478992, 1079061842, 2740046621, 661881239, 507239328, 697753503,
253362433, 168342080, 1272933039, 4202497602, 1521090445, 4230166401, 3245111456,
1771955024, 2337713684, 1778330386, 2423095095, 693420498, 3750496916, 3753972086,
1073775970, 846704018, 1223205425, 1867757265, 3214198296, 1145624482, 599115079,
2929172517, 4121498420, 2287897365, 1987227723, 3482603622, 2806989260, 1760871734,
4227656463, 1736215921, 2778669702, 4140571142, 1870700075, 2015964902, 1288446830,
1168026618, 400675728, 2165625891, 450825118, 1620534920, 2874273302, 2645036208,
1267321906, 3865497387, 2594934933, 2526789975, 459036976, 2552359495, 27750894,
3204441497, 1944008238, 1359672352, 2839100473, 4191710191, 3220138979, 902020460,
2896982042, 1451853853, 2406388220, 1238109043, 2615908943, 3644037856, 77415486,
230114675, 3155,
]),
};
let fp = ExtendedFloat80 {
mant: 1 << 63,
exp: -62,
};
let exponent = -324 + 1 - 755;
let result = slow::negative_digit_comp::<f64, FORMAT>(bigmant, fp, exponent);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 0);
// 1e-323, halfway, round-up to 1e-323.
let bigmant = Bigint {
data: vec_from_u32(&[
1959393335, 1521408631, 3704199161, 740792990, 945363576, 1625749700, 626541858,
687279403, 250475582, 91273375, 3331339701, 3505355194, 442888870, 967065395,
4089062228, 3466206949, 2827361413, 266195502, 3957185970, 519626241, 1652512553,
2836450370, 3034584624, 1268793682, 1935158378, 1770219946, 1403909835, 2967281406,
2449205134, 589257223, 417835033, 826501478, 973876807, 1027642808, 365671335,
3043766018, 2808735142, 1733159717, 3566810083, 2357124681, 1196807897, 169264596,
2929100385, 1333176077, 853182194, 3487359048, 3291815648, 1436535041, 2896096404,
4021257415, 3964655489, 612050981, 2336913034, 3841321297, 3232689679, 3928245144,
3106528676, 3186436263, 101379182, 380483912, 1061315959, 1986827250, 3577735508,
1575162471, 2441485432, 2239037633, 1991408958, 3040733588, 2872258292, 1280802274,
1123883446, 3214087391, 3623063818, 752857385, 2545236548, 3293588986, 2383784264,
665950045, 3,
]),
};
let exponent = -324 + 1 - 752;
let result = slow::negative_digit_comp::<f64, FORMAT>(bigmant, fp, exponent);
assert_eq!(result.mant, 2);
assert_eq!(result.exp, 0);
// 1e-323, above halfway, round-up to 1e-323.
let bigmant = Bigint {
data: vec_from_u32(&[
2414064167, 2329184426, 2682253245, 3112962612, 863701169, 3372595114, 1970451287,
2577826735, 2504755821, 912733750, 3248625938, 693813579, 133921412, 1080719359,
2235916618, 302331131, 2503810362, 2661955026, 917154036, 901295123, 3640223643,
2594699927, 281075174, 4098002235, 2171714598, 522330280, 1154196466, 3903010287,
3017214866, 1597604939, 4178350331, 3970047484, 1148833479, 1686493490, 3656713352,
372889108, 2317547651, 151727992, 1308362466, 2096410338, 3378144383, 1692645962,
3521200074, 446858888, 4236854647, 513852113, 2853385416, 1480448529, 3191160267,
1557868492, 991849235, 1825542523, 1894293861, 4053474607, 2262125726, 627745783,
1000515697, 1799591565, 1013791827, 3804839120, 2023224998, 2688403318, 1417616716,
2866722830, 2940017843, 915539855, 2734220401, 342564812, 2952779151, 4218088154,
2648899870, 2076102840, 1870899819, 3233606562, 3977529001, 2871118793, 2363006167,
2364533159, 31,
]),
};
let exponent = -324 + 1 - 753;
let result = slow::negative_digit_comp::<f64, FORMAT>(bigmant, fp, exponent);
assert_eq!(result.mant, 2);
assert_eq!(result.exp, 0);
}
#[test]
fn parse_mantissa_test() {
const FORMAT: u128 = STANDARD;
let max_digits = f64::max_digits(10).unwrap();
// Large number of digits.
let mut num = Number {
mantissa: 2470328229206232720,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"2",
fraction: Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999"),
};
let (bigmant, count) = slow::parse_mantissa::<FORMAT>(num.clone(), max_digits);
let expected = vec_from_u32::<100>(&[
1727738439, 330069557, 3509095598, 686205316, 156923684, 750687444, 2688855918, 28211928,
1887482096, 3222998811, 913348873, 1652282845, 1600735541, 1664240266, 84454144,
1487769792, 1855966778, 2832488299, 507030148, 1410055467, 2513359584, 3453963205,
779237894, 3456088326, 3671009895, 3094451696, 1250165638, 2682979794, 357925323,
1713890438, 3271046672, 3485897285, 3934710962, 1813530592, 199705026, 976390839,
2805488572, 2194288220, 2094065006, 2592523639, 3798974617, 586957244, 1409218821,
3442050171, 3789534764, 1380190380, 2055222457, 3535299831, 429482276, 389342206,
133558576, 721875297, 3013586570, 540178306, 2389746866, 2313334501, 422440635, 1288499129,
864978311, 842263325, 3016323856, 2282442263, 1440906063, 3931458696, 3511314276,
1884879882, 946366824, 4260548261, 1073379659, 1732329252, 3828972211, 1915607049,
3665440937, 1844358779, 3735281178, 2646335050, 1457460927, 2940016422, 1051,
]);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, 755);
// Leading zeros
num.integer = b"0000000002";
let (bigmant, count) = slow::parse_mantissa::<FORMAT>(num.clone(), max_digits);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, 755);
// Truncation.
let mut num = Number {
mantissa: 7410984687618698162,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"7",
fraction: Some(b"4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375332669816033062329967789262837"),
};
let (bigmant, count) = slow::parse_mantissa::<FORMAT>(num.clone(), max_digits);
let expected = vec_from_u32::<100>(&[
983641521, 2202462645, 4170685875, 1591772364, 529830014, 803977727, 126733331, 1695971390,
4089590927, 1532849076, 2705586665, 4046282448, 4076195232, 3230469892, 3059053929,
79035789, 744229654, 2026438108, 3570486781, 2818088662, 3485839733, 3653138023,
2857937689, 602717004, 3689362390, 283607819, 1783392475, 2053068939, 1888214698,
550023429, 296880187, 1046779059, 1285361259, 84614934, 1627922685, 2023868765, 1987523901,
743493573, 3897769089, 2210613570, 2261081349, 3015057659, 3949711644, 3346092916,
2433639051, 36411806, 1050442, 269209477, 2649742673, 1494221829, 2763524503, 2514491481,
2325312415, 1741242814, 2479923579, 1098250122, 2416211509, 3612906464, 403420662,
3663250314, 1993722098, 365907183, 4270226312, 3962131185, 432952495, 2963635838,
2996289227, 3200289391, 2753231690, 2780286109, 884373163, 1418533204, 3382415762,
499541562, 3369625401, 3421327641, 3526770155, 3109983188, 1157439767, 734593155,
]);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, max_digits + 1);
// No fraction digits.
num.integer = b"74109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375332669816033062329967789262837";
num.fraction = None;
let (bigmant, count) = slow::parse_mantissa::<FORMAT>(num.clone(), max_digits);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, max_digits + 1);
// Multiple of step (check we add our temporary correctly).
num.integer = b"7410984687618698162648531893023320585475897039214871466383785237510132609053131277979497545424539885696948470431685765963899850655339096945981621940161728171894510697854671067917687257517734731555330779540854980960845750095811137303474765809687100959097544227100475730780971111893578483867565399878350301522805593404659373979179073872386829939581848166016912201945649993128979841136206248449867871357218035220901702390328579173252022052897402080290685402160661237554998340267130003581248647904138574340187552090159017259254714629617513415977493871857473787096164563890871811984127167305601704549300470526959016576377688490826798697257336652176556794107250876433756084600398490497214911746308553955635418864151316847843631308023759629577398300170898437533266981";
num.fraction = None;
let (bigmant, count) = slow::parse_mantissa::<FORMAT>(num.clone(), max_digits);
let expected = vec_from_u32::<100>(&[
617018405, 396211401, 2130402383, 3812547827, 4263683770, 3918012496, 1787721490,
2493014694, 435464626, 3720854431, 2928509507, 2677932436, 369049650, 3606588290,
231237141, 2231172875, 3358152367, 95217925, 2777810007, 1016185079, 596681915, 2331711780,
593487272, 4212730845, 339602972, 4097829793, 262427536, 4182115035, 3414687403,
3711518952, 4168896929, 483727327, 1657080031, 2785588628, 1009114769, 482126749,
485376744, 1123705337, 3225501941, 2939050108, 1338451005, 2104263947, 3425461126,
1834224928, 4061025704, 792093815, 2707019125, 3610271203, 4254101529, 1026215278,
4117890107, 1748110416, 2535111606, 80965120, 3823822115, 2354910057, 590658512,
2682089507, 159300272, 1776569442, 3382166479, 3222978591, 540586210, 934713382,
2014123057, 1455555790, 4119131465, 3685912982, 3019947291, 3437891678, 2660105801,
2605860762, 394373515, 4177081532, 1616198650, 1580399082, 2017617452, 3327697130,
315505357,
]);
assert_eq!(&*bigmant.data, &*expected);
assert_eq!(count, 760);
}
#[test]
#[cfg(feature = "radix")]
fn byte_comp_test() {
const FORMAT: u128 = STANDARD;
// 5e-324
let mut num = Number {
mantissa: 2470328229206232720,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"2",
fraction: Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999"),
};
let fp = ExtendedFloat80 {
mant: 1 << 63,
exp: -63,
};
let result = slow::byte_comp::<f64, FORMAT>(num.clone(), fp, -324);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, equal, round-down
num.fraction = Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125");
let result = slow::byte_comp::<f64, FORMAT>(num.clone(), fp, -324);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, equal, round-down, many 0s
num.fraction = Some(b"47032822920623272088284396434110686182529901307162382212792841250337753635104375932649918180817996189898282347722858865463328355177969898199387398005390939063150356595155702263922908583924491051844359318028499365361525003193704576782492193656236698636584807570015857692699037063119282795585513329278343384093519780155312465972635795746227664652728272200563740064854999770965994704540208281662262378573934507363390079677619305775067401763246736009689513405355374585166611342237666786041621596804619144672918403005300575308490487653917113865916462395249126236538818796362393732804238910186723484976682350898633885879256283027559956575244555072551893136908362547791869486679949683240497058210285131854513962138377228261454376934125320985913276672363281250000000");
let result = slow::byte_comp::<f64, FORMAT>(num.clone(), fp, -324);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 0);
// 5e-324, above, round-up
num.fraction = Some(b"47032822920623272088284396434110686182529901307162382212792841250337753635104375932649918180817996189898282347722858865463328355177969898199387398005390939063150356595155702263922908583924491051844359318028499365361525003193704576782492193656236698636584807570015857692699037063119282795585513329278343384093519780155312465972635795746227664652728272200563740064854999770965994704540208281662262378573934507363390079677619305775067401763246736009689513405355374585166611342237666786041621596804619144672918403005300575308490487653917113865916462395249126236538818796362393732804238910186723484976682350898633885879256283027559956575244555072551893136908362547791869486679949683240497058210285131854513962138377228261454376934125320985913276672363281251");
let result = slow::byte_comp::<f64, FORMAT>(num.clone(), fp, -324);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 0);
// 8.98846567431158e+307
let mut num = Number {
mantissa: 8988465674311580536,
exponent: 289,
is_negative: false,
many_digits: true,
integer: b"8",
fraction: Some(b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032"),
};
let fp = ExtendedFloat80 {
mant: 9223372036854776832,
exp: 960 + 1075,
};
let result = slow::byte_comp::<f64, FORMAT>(num.clone(), fp, 307);
assert_eq!(result.mant, 0);
assert_eq!(result.exp, 2046);
// 8.988465674311582e+307
num.fraction = Some(b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296474151486978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080321");
let result = slow::byte_comp::<f64, FORMAT>(num.clone(), fp, 307);
assert_eq!(result.mant, 1);
assert_eq!(result.exp, 2046);
}
#[test]
#[cfg(feature = "radix")]
fn compare_bytes_test() {
const FORMAT: u128 = STANDARD;
// 2^-1074
let num = Bigfloat {
data: vec_from_u32(&[
1725370368, 1252154597, 1017462556, 675087593, 2805901938, 1401824593, 1124332496,
2380663002, 1612846757, 4128923878, 1492915356, 437569744, 2975325085, 3331531962,
3367627909, 730662168, 2699172281, 1440714968, 2778340312, 690527038, 1297115354,
763425880, 1453089653, 331561842,
]),
exp: 312,
};
let den = Bigfloat {
data: vec_from_u32(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728,
]),
exp: 312,
};
// Below halfway
let mut number = Number {
mantissa: 2470328229206232720,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"2",
fraction: Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999"),
};
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Less
);
// Exactly halfway.
number.fraction = Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Equal
);
// Above halfway.
number.fraction = Some(b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125001");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Greater
);
// 2*2^-1074
let num = Bigfloat {
data: vec_from_u32(&[
881143808, 3756463792, 3052387668, 2025262779, 4122738518, 4205473780, 3372997488,
2847021710, 543572976, 3796837043, 183778774, 1312709233, 336040663, 1404661296,
1512949137, 2191986506, 3802549547, 27177609, 4040053641, 2071581115, 3891346062,
2290277640, 64301663, 994685527,
]),
exp: 312,
};
let den = Bigfloat {
data: vec_from_u32(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728,
]),
exp: 312,
};
// Below halfway
let mut number = Number {
mantissa: 7410984687618698162,
exponent: -342,
is_negative: false,
many_digits: true,
integer: b"7",
fraction: Some(b"4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999"),
};
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Less
);
// Exactly halfway.
number.fraction = Some(b"4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Equal
);
// Above halfway.
number.fraction = Some(b"4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Greater
);
// 4503599627370496*2^971
let num = Bigfloat {
data: vec_from_u32(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1024, 2147483648,
]),
exp: 288,
};
let den = Bigfloat {
data: vec_from_u32(&[
1978138624, 2671552565, 2938166866, 3588566204, 1860064291, 2104472219, 2014975858,
2797301608, 462262832, 318515330, 1101517094, 1738264167, 3721375114, 414401884,
1406861075, 3053102637, 387329537, 2051556775, 1867945454, 3717689914, 1434550525,
1446648206, 238915486,
]),
exp: 288,
};
// Below halfway
let mut number = Number {
mantissa: 8988465674311580536,
exponent: 289,
is_negative: false,
many_digits: true,
integer: b"8",
fraction: Some(b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708031999"),
};
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Less
);
// Exactly halfway.
number.fraction = Some(b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Equal
);
// Above halfway.
number.fraction = Some(b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429648741514697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032001");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Greater
);
// Ensure leading and trailing zeros are ignored.
// Below halfway
number.integer = b"000008";
number.fraction = Some(b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296474151486978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080319990000");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Less
);
// Exactly halfway.
number.integer = b"000008";
number.fraction = Some(b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296474151486978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080320000");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Equal
);
// Above halfway.
number.integer = b"000008";
number.fraction = Some(b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296487415146978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080320010000");
assert_eq!(
slow::compare_bytes::<FORMAT>(number.clone(), num.clone(), den.clone()),
cmp::Ordering::Greater
);
}
#[test]
fn scientific_exponent_test() {
let mut number = Number {
exponent: -4,
mantissa: 12345,
is_negative: false,
many_digits: false,
integer: &[],
fraction: None,
};
assert_eq!(slow::scientific_exponent::<{ STANDARD }>(&number), 0);
number.exponent = -15;
assert_eq!(slow::scientific_exponent::<{ STANDARD }>(&number), -11);
number.mantissa = 1234567890123456;
assert_eq!(slow::scientific_exponent::<{ STANDARD }>(&number), 0);
}
#[test]
#[cfg(feature = "radix")]
fn integral_binary_factor_test() {
const TABLE: [u32; 35] = [
0, 2, 0, 3, 3, 3, 0, 4, 4, 4, 4, 4, 4, 4, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
0, 6, 6, 6, 6,
];
for (index, radix) in (2..37).enumerate() {
assert_eq!(slow::integral_binary_factor(radix), TABLE[index]);
}
}
@@ -0,0 +1,28 @@
use lexical_parse_float::bigint::{Limb, StackVec};
pub fn vec_from_u32<const SIZE: usize>(x: &[u32]) -> StackVec<SIZE> {
let mut vec = StackVec::<SIZE>::new();
#[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))]
{
for &xi in x {
vec.try_push(xi as Limb).unwrap();
}
}
#[cfg(all(target_pointer_width = "64", not(target_arch = "sparc")))]
{
for xi in x.chunks(2) {
match xi.len() {
1 => vec.try_push(xi[0] as Limb).unwrap(),
2 => {
let xi0 = xi[0] as Limb;
let xi1 = xi[1] as Limb;
vec.try_push((xi1 << 32) | xi0).unwrap()
},
_ => unreachable!(),
}
}
}
vec
}
@@ -0,0 +1,472 @@
mod stackvec;
use core::cmp;
use lexical_parse_float::bigint::{self, Limb, StackVec};
use stackvec::vec_from_u32;
const SIZE: usize = 50;
type VecType = StackVec<SIZE>;
#[test]
fn simple_test() {
// Test the simple properties of the stack vector.
let mut x = VecType::from_u32(1);
assert_eq!(x.len(), 1);
assert_eq!(x.is_empty(), false);
assert_eq!(x.capacity(), SIZE);
x.try_push(5).unwrap();
assert_eq!(x.len(), 2);
assert_eq!(x.pop(), Some(5));
assert_eq!(x.len(), 1);
assert_eq!(&*x, &[1]);
x.try_extend(&[2, 3, 4]).unwrap();
assert_eq!(x.len(), 4);
assert_eq!(&*x, &[1, 2, 3, 4]);
x.try_resize(6, 0).unwrap();
assert_eq!(x.len(), 6);
assert_eq!(&*x, &[1, 2, 3, 4, 0, 0]);
x.try_resize(0, 0).unwrap();
assert_eq!(x.len(), 0);
assert_eq!(x.is_empty(), true);
let x = VecType::try_from(&[5, 1]).unwrap();
assert_eq!(x.len(), 2);
assert_eq!(x.is_empty(), false);
assert_eq!(x.hi16(), (0x8000, true));
if Limb::BITS == 32 {
assert_eq!(x.hi32(), (0x80000002, true));
assert_eq!(x.hi64(), (0x8000000280000000, false));
} else {
assert_eq!(x.hi32(), (0x80000000, true));
assert_eq!(x.hi64(), (0x8000000000000002, true));
}
let rview = x.rview();
assert_eq!(x[0], 5);
assert_eq!(x[1], 1);
assert_eq!(rview[0], 1);
assert_eq!(rview[1], 5);
assert_eq!(rview.get(1), Some(&5));
assert_eq!(rview.get(2), None);
assert_eq!(VecType::from_u16(u16::MAX).hi16(), (u16::MAX, false));
assert_eq!(VecType::from_u32(u32::MAX).hi32(), (u32::MAX, false));
assert_eq!(VecType::from_u64(u64::MAX).hi64(), (u64::MAX, false));
}
#[test]
fn bounds_test() {
type ShortVec = StackVec<2>;
let mut x = ShortVec::from_u32(1);
assert_eq!(x.try_push(2), Some(()));
assert_eq!(x.try_push(5), None);
assert_eq!(x.try_resize(0, 0), Some(()));
assert_eq!(x.try_resize(3, 0), None);
}
#[test]
fn hi32_test() {
assert_eq!(VecType::from_u16(0xA).hi32(), (0xA0000000, false));
assert_eq!(VecType::from_u32(0xAB).hi32(), (0xAB000000, false));
assert_eq!(VecType::from_u64(0xAB00000000).hi32(), (0xAB000000, false));
assert_eq!(VecType::from_u64(0xA23456789A).hi32(), (0xA2345678, true));
}
#[test]
fn hi64_test() {
assert_eq!(VecType::from_u16(0xA).hi64(), (0xA000000000000000, false));
assert_eq!(VecType::from_u32(0xAB).hi64(), (0xAB00000000000000, false));
assert_eq!(VecType::from_u64(0xAB00000000).hi64(), (0xAB00000000000000, false));
assert_eq!(VecType::from_u64(0xA23456789A).hi64(), (0xA23456789A000000, false));
}
#[test]
fn cmp_test() {
// Simple
let x = VecType::from_u32(1);
let y = VecType::from_u32(2);
assert_eq!(x.partial_cmp(&x), Some(cmp::Ordering::Equal));
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Less);
// Check asymmetric
let x = VecType::try_from(&[5, 1]).unwrap();
let y = VecType::from_u32(2);
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Greater);
// Check when we use reverse ordering properly.
let x = VecType::try_from(&[5, 1, 9]).unwrap();
let y = VecType::try_from(&[6, 2, 8]).unwrap();
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Greater);
// Complex scenario, check it properly uses reverse ordering.
let x = VecType::try_from(&[0, 1, 9]).unwrap();
let y = VecType::try_from(&[4294967295, 0, 9]).unwrap();
assert_eq!(x.cmp(&x), cmp::Ordering::Equal);
assert_eq!(x.cmp(&y), cmp::Ordering::Greater);
}
#[test]
fn math_test() {
let mut x = VecType::try_from(&[0, 1, 9]).unwrap();
assert_eq!(x.is_normalized(), true);
x.try_push(0).unwrap();
assert_eq!(&*x, &[0, 1, 9, 0]);
assert_eq!(x.is_normalized(), false);
x.normalize();
assert_eq!(&*x, &[0, 1, 9]);
assert_eq!(x.is_normalized(), true);
x.add_small(1);
assert_eq!(&*x, &[1, 1, 9]);
x.add_small(Limb::MAX);
assert_eq!(&*x, &[0, 2, 9]);
x.mul_small(3);
assert_eq!(&*x, &[0, 6, 27]);
x.mul_small(Limb::MAX);
let expected: VecType = if Limb::BITS == 32 {
vec_from_u32(&[0, 4294967290, 4294967274, 26])
} else {
vec_from_u32(&[0, 0, 4294967290, 4294967295, 4294967274, 4294967295, 26])
};
assert_eq!(&*x, &*expected);
#[cfg(feature = "radix")]
{
let mut x: VecType = vec_from_u32(&[0, 0, 0, 536870912]);
let y: VecType = vec_from_u32(&[3358091099, 2770363594, 2782716766, 217327764]);
assert_eq!(x.quorem(&y), 2);
let expected: VecType = vec_from_u32(&[1873752394, 3049207402, 3024501058, 102215382]);
assert_eq!(&*x, &*expected);
}
let mut x = VecType::from_u32(0xFFFFFFFF);
let y = VecType::from_u32(5);
x *= &y;
let expected: VecType = vec_from_u32(&[0xFFFFFFFB, 0x4]);
assert_eq!(&*x, &*expected);
// Test with carry
let mut x = VecType::from_u32(1);
assert_eq!(&*x, &[1]);
x.add_small(Limb::MAX);
assert_eq!(&*x, &[0, 1]);
}
#[test]
fn hi_test() {
assert_eq!(unsafe { bigint::nonzero(&[0, 0, 0], 0) }, false);
assert_eq!(unsafe { bigint::nonzero(&[1, 0, 0], 0) }, true);
assert_eq!(bigint::u32_to_hi16_1(1), (0x8000, false));
assert_eq!(bigint::u32_to_hi16_2(1, 4), (0x8000, true));
assert_eq!(bigint::u32_to_hi32_1(1), (0x80000000, false));
assert_eq!(bigint::u32_to_hi32_2(1, 4), (0x80000002, false));
assert_eq!(bigint::u32_to_hi64_1(1), (0x8000000000000000, false));
assert_eq!(bigint::u32_to_hi64_2(1, 4), (0x8000000200000000, false));
assert_eq!(bigint::u32_to_hi64_2(1, 5), (0x8000000280000000, false));
assert_eq!(bigint::u32_to_hi64_3(1, 5, 4), (0x8000000280000002, false));
assert_eq!(bigint::u32_to_hi64_3(1, 5, 5), (0x8000000280000002, true));
assert_eq!(bigint::u64_to_hi16_1(1), (0x8000, false));
assert_eq!(bigint::u64_to_hi16_2(1, 4), (0x8000, true));
assert_eq!(bigint::u64_to_hi32_1(1), (0x80000000, false));
assert_eq!(bigint::u64_to_hi32_2(1, 4), (0x80000000, true));
assert_eq!(bigint::u64_to_hi64_1(1), (0x8000000000000000, false));
assert_eq!(bigint::u64_to_hi64_2(1, 4), (0x8000000000000002, false));
assert_eq!(bigint::u64_to_hi64_2(1, 5), (0x8000000000000002, true));
}
#[test]
fn scalar_add_test() {
assert_eq!(bigint::scalar_add(5, 5), (10, false));
assert_eq!(bigint::scalar_add(Limb::MAX, 1), (0, true));
}
#[test]
fn scalar_mul_test() {
assert_eq!(bigint::scalar_mul(5, 5, 0), (25, 0));
assert_eq!(bigint::scalar_mul(5, 5, 1), (26, 0));
assert_eq!(bigint::scalar_mul(Limb::MAX, 2, 0), (Limb::MAX - 1, 1));
}
#[test]
fn small_add_test() {
let mut x = VecType::from_u32(4294967295);
bigint::small_add(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 1]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u32(5);
bigint::small_add(&mut x, 7);
let expected = VecType::from_u32(12);
assert_eq!(&*x, &*expected);
// Single carry, internal overflow
let mut x = VecType::from_u64(0x80000000FFFFFFFF);
bigint::small_add(&mut x, 7);
let expected: VecType = vec_from_u32(&[6, 0x80000001]);
assert_eq!(&*x, &*expected);
// Double carry, overflow
let mut x = VecType::from_u64(0xFFFFFFFFFFFFFFFF);
bigint::small_add(&mut x, 7);
let expected: VecType = vec_from_u32(&[6, 0, 1]);
assert_eq!(&*x, &*expected);
}
#[test]
fn small_mul_test() {
// No overflow check, 1-int.
let mut x = VecType::from_u32(5);
bigint::small_mul(&mut x, 7);
let expected = VecType::from_u32(35);
assert_eq!(&*x, &*expected);
// No overflow check, 2-ints.
let mut x = VecType::from_u64(0x4000000040000);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[0x00140000, 0x140000]);
assert_eq!(&*x, &*expected);
// Overflow, 1 carry.
let mut x = VecType::from_u32(0x33333334);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 1]);
assert_eq!(&*x, &*expected);
// Overflow, 1 carry, internal.
let mut x = VecType::from_u64(0x133333334);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 6]);
assert_eq!(&*x, &*expected);
// Overflow, 2 carries.
let mut x = VecType::from_u64(0x3333333333333334);
bigint::small_mul(&mut x, 5);
let expected: VecType = vec_from_u32(&[4, 0, 1]);
assert_eq!(&*x, &*expected);
}
#[test]
fn pow_test() {
let mut x = VecType::from_u32(1);
bigint::pow(&mut x, 5, 2);
let expected = VecType::from_u32(25);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u32(1);
bigint::pow(&mut x, 5, 15);
let expected: VecType = vec_from_u32(&[452807053, 7]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u32(1);
bigint::pow(&mut x, 5, 16);
let expected: VecType = vec_from_u32(&[2264035265, 35]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u32(1);
bigint::pow(&mut x, 5, 17);
let expected: VecType = vec_from_u32(&[2730241733, 177]);
assert_eq!(&*x, &*expected);
let mut x = VecType::from_u32(1);
bigint::pow(&mut x, 5, 302);
let expected: VecType = vec_from_u32(&[
2443090281, 2149694430, 2297493928, 1584384001, 1279504719, 1930002239, 3312868939,
3735173465, 3523274756, 2025818732, 1641675015, 2431239749, 4292780461, 3719612855,
4174476133, 3296847770, 2677357556, 638848153, 2198928114, 3285049351, 2159526706,
626302612,
]);
assert_eq!(&*x, &*expected);
}
#[test]
fn large_add_test() {
// Overflow, both single values
let mut x = VecType::from_u32(4294967295);
let y = VecType::from_u32(5);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[4, 1]);
assert_eq!(&*x, &*expected);
// No overflow, single value
let mut x = VecType::from_u32(5);
let y = VecType::from_u32(7);
bigint::large_add(&mut x, &y);
let expected = VecType::from_u32(12);
assert_eq!(&*x, &*expected);
// Single carry, internal overflow
let mut x = VecType::from_u64(0x80000000FFFFFFFF);
let y = VecType::from_u32(7);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[6, 0x80000001]);
assert_eq!(&*x, &*expected);
// 1st overflows, 2nd doesn't.
let mut x = VecType::from_u64(0x7FFFFFFFFFFFFFFF);
let y = VecType::from_u64(0x7FFFFFFFFFFFFFFF);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[0xFFFFFFFE, 0xFFFFFFFF]);
assert_eq!(&*x, &*expected);
// Both overflow.
let mut x = VecType::from_u64(0x8FFFFFFFFFFFFFFF);
let y = VecType::from_u64(0x7FFFFFFFFFFFFFFF);
bigint::large_add(&mut x, &y);
let expected: VecType = vec_from_u32(&[0xFFFFFFFE, 0x0FFFFFFF, 1]);
assert_eq!(&*x, &*expected);
}
#[test]
fn large_mul_test() {
// Test by empty
let mut x = VecType::from_u32(0xFFFFFFFF);
let y = VecType::new();
bigint::large_mul(&mut x, &y);
let expected = VecType::new();
assert_eq!(&*x, &*expected);
// Simple case
let mut x = VecType::from_u32(0xFFFFFFFF);
let y = VecType::from_u32(5);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[0xFFFFFFFB, 0x4]);
assert_eq!(&*x, &*expected);
// Large u32, but still just as easy.
let mut x = VecType::from_u32(0xFFFFFFFF);
let y = VecType::from_u32(0xFFFFFFFE);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[0x2, 0xFFFFFFFD]);
assert_eq!(&*x, &*expected);
// Let's multiply two large values together.
let mut x: VecType = vec_from_u32(&[0xFFFFFFFE, 0x0FFFFFFF, 1]);
let y: VecType = vec_from_u32(&[0x99999999, 0x99999999, 0xCCCD9999, 0xCCCC]);
bigint::large_mul(&mut x, &y);
let expected: VecType =
vec_from_u32(&[0xCCCCCCCE, 0x5CCCCCCC, 0x9997FFFF, 0x33319999, 0x999A7333, 0xD999]);
assert_eq!(&*x, &*expected);
}
#[test]
fn very_large_mul_test() {
// Test cases triggered to that would normally use `karatsuba_mul`.
// Karatsuba multiplication was ripped out, however, these are useful
// test cases.
let mut x: VecType = vec_from_u32(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
let y: VecType = vec_from_u32(&[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[
4, 13, 28, 50, 80, 119, 168, 228, 300, 385, 484, 598, 728, 875, 1040, 1224, 1340, 1435,
1508, 1558, 1584, 1585, 1560, 1508, 1428, 1319, 1180, 1010, 808, 573, 304,
]);
assert_eq!(&*x, &*expected);
// Test cases triggered to that would normally use `karatsuba_uneven_mul`.
let mut x: VecType = vec_from_u32(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
let y: VecType = vec_from_u32(&[
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
]);
bigint::large_mul(&mut x, &y);
let expected: VecType = vec_from_u32(&[
4, 13, 28, 50, 80, 119, 168, 228, 300, 385, 484, 598, 728, 875, 1040, 1224, 1360, 1496,
1632, 1768, 1904, 2040, 2176, 2312, 2448, 2584, 2720, 2856, 2992, 3128, 3264, 3400, 3536,
3672, 3770, 3829, 3848, 3826, 3762, 3655, 3504, 3308, 3066, 2777, 2440, 2054, 1618, 1131,
592,
]);
assert_eq!(&*x, &*expected);
}
#[test]
#[cfg(feature = "radix")]
fn quorem_test() {
let mut x: VecType = vec_from_u32(&[0, 0, 0, 536870912]);
let y: VecType = vec_from_u32(&[3358091099, 2770363594, 2782716766, 217327764]);
assert_eq!(bigint::large_quorem(&mut x, &y), 2);
let expected: VecType = vec_from_u32(&[1873752394, 3049207402, 3024501058, 102215382]);
assert_eq!(&*x, &*expected);
}
#[test]
fn bit_length_test() {
let x: VecType = vec_from_u32(&[0, 0, 0, 1]);
assert_eq!(bigint::bit_length(&x), 97);
let x: VecType = vec_from_u32(&[0, 0, 0, 3]);
assert_eq!(bigint::bit_length(&x), 98);
let x = VecType::from_u32(1 << 31);
assert_eq!(bigint::bit_length(&x), 32);
}
#[test]
fn shl_bits_test() {
let mut x = VecType::from_u32(0xD2210408);
bigint::shl_bits(&mut x, 5);
let expected: VecType = vec_from_u32(&[0x44208100, 0x1A]);
assert_eq!(&*x, &*expected);
}
#[test]
fn shl_limbs_test() {
let mut x = VecType::from_u32(0xD2210408);
bigint::shl_limbs(&mut x, 2);
let expected: VecType = if Limb::BITS == 32 {
vec_from_u32(&[0, 0, 0xD2210408])
} else {
vec_from_u32(&[0, 0, 0, 0, 0xD2210408])
};
assert_eq!(&*x, &*expected);
}
#[test]
fn shl_test() {
// Pattern generated via `''.join(["1" +"0"*i for i in range(20)])`
let mut x = VecType::from_u32(0xD2210408);
bigint::shl(&mut x, 5).unwrap();
let expected: VecType = vec_from_u32(&[0x44208100, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 32).unwrap();
let expected: VecType = vec_from_u32(&[0, 0x44208100, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 27).unwrap();
let expected: VecType = vec_from_u32(&[0, 0, 0xD2210408]);
assert_eq!(&*x, &*expected);
// 96-bits of previous pattern
let mut x: VecType = vec_from_u32(&[0x20020010, 0x8040100, 0xD2210408]);
bigint::shl(&mut x, 5).unwrap();
let expected: VecType = vec_from_u32(&[0x400200, 0x802004, 0x44208101, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 32).unwrap();
let expected: VecType = vec_from_u32(&[0, 0x400200, 0x802004, 0x44208101, 0x1A]);
assert_eq!(&*x, &*expected);
bigint::shl(&mut x, 27).unwrap();
let expected: VecType = vec_from_u32(&[0, 0, 0x20020010, 0x8040100, 0xD2210408]);
assert_eq!(&*x, &*expected);
}
#[test]
fn split_radix_test() {
assert_eq!(bigint::split_radix(10), (5, 1));
if cfg!(feature = "radix") {
assert_eq!(bigint::split_radix(2), (0, 1));
assert_eq!(bigint::split_radix(4), (0, 2));
assert_eq!(bigint::split_radix(8), (0, 3));
assert_eq!(bigint::split_radix(16), (0, 4));
assert_eq!(bigint::split_radix(32), (0, 5));
assert_eq!(bigint::split_radix(14), (7, 1));
}
}