Import 'ryu' crate version 1.0.5 am: ce81bb62a2

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/ryu/+/1414669

Change-Id: Icf121ab040c6b7247e9589255f31a5f381b8e4f3
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..5e45238
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "9edbbd939fa80fb364d0a1ae65ff05efbc2ac894"
+  }
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..ea91ef1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,43 @@
+name: CI
+
+on:
+  push:
+  pull_request:
+  schedule: [cron: "40 1 * * *"]
+
+jobs:
+  test:
+    name: Rust ${{matrix.rust}}
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        rust: [nightly, beta, stable]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{matrix.rust}}
+      - run: cargo test
+      - run: cargo build --tests --features no-panic --release
+        if: matrix.rust == 'nightly'
+
+  msrv:
+    name: Rust 1.31.0
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@1.31.0
+      - run: cargo build
+      - run: cargo build --features small
+
+  miri:
+    name: Miri
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@nightly
+        with:
+          components: miri
+      - run: cargo miri setup
+      - run: cargo miri test
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2588c33
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/target
+/upstream
+**/*.rs.bk
+Cargo.lock
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..bbb0114
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,13 @@
+// This file is generated by cargo2android.py --run --device --dependencies.
+
+rust_library {
+    name: "libryu",
+    host_supported: true,
+    crate_name: "ryu",
+    srcs: ["src/lib.rs"],
+    edition: "2018",
+    flags: [
+        "--cfg integer128",
+        "--cfg maybe_uninit",
+    ],
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3da4fcd
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,38 @@
+# 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 believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "ryu"
+version = "1.0.5"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+description = "Fast floating point to string conversion"
+documentation = "https://docs.rs/ryu"
+readme = "README.md"
+license = "Apache-2.0 OR BSL-1.0"
+repository = "https://github.com/dtolnay/ryu"
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+[dependencies.no-panic]
+version = "0.1"
+optional = true
+[dev-dependencies.num_cpus]
+version = "1.8"
+
+[dev-dependencies.rand]
+version = "0.7"
+
+[dev-dependencies.rand_xorshift]
+version = "0.2"
+
+[features]
+small = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..ad0ba2c
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,28 @@
+[package]
+name = "ryu"
+version = "1.0.5" # don't forget to update html_root_url
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+license = "Apache-2.0 OR BSL-1.0"
+description = "Fast floating point to string conversion"
+repository = "https://github.com/dtolnay/ryu"
+documentation = "https://docs.rs/ryu"
+readme = "README.md"
+edition = "2018"
+
+[features]
+# Use smaller lookup tables. Instead of storing every required power of
+# 5, only store every 26th entry, and compute intermediate values with a
+# multiplication. This reduces the lookup table size by about 10x (only
+# one case, and only f64) at the cost of some performance.
+small = []
+
+[dependencies]
+no-panic = { version = "0.1", optional = true }
+
+[dev-dependencies]
+num_cpus = "1.8"
+rand = "0.7"
+rand_xorshift = "0.2"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d682d4d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,227 @@
+                              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.
+
+------------------
+
+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.
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE-APACHE
@@ -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.
diff --git a/LICENSE-BOOST b/LICENSE-BOOST
new file mode 100644
index 0000000..36b7cd9
--- /dev/null
+++ b/LICENSE-BOOST
@@ -0,0 +1,23 @@
+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.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..81e8be1
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,18 @@
+name: "ryu"
+description:
+    "Fast floating point to string conversion"
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://crates.io/crates/ryu"
+  }
+  url {
+    type: ARCHIVE
+    value: "https://static.crates.io/crates/ryu/ryu-1.0.5.crate"
+  }
+  version: "1.0.5"
+  last_upgrade_date { year: 2020 month: 8 day: 21 }
+  license_type: NOTICE
+}
+
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..46fc303
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bf718ed
--- /dev/null
+++ b/README.md
@@ -0,0 +1,114 @@
+# Ryū
+
+[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/ryu-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/ryu)
+[<img alt="crates.io" src="https://img.shields.io/crates/v/ryu.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/ryu)
+[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-ryu-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/ryu)
+[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/ryu/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/ryu/actions?query=branch%3Amaster)
+
+Pure Rust implementation of Ryū, an algorithm to quickly convert floating point
+numbers to decimal strings.
+
+The PLDI'18 paper [*Ryū: fast float-to-string conversion*][paper] by Ulf Adams
+includes a complete correctness proof of the algorithm. The paper is available
+under the creative commons CC-BY-SA license.
+
+This Rust implementation is a line-by-line port of Ulf Adams' implementation in
+C, [https://github.com/ulfjack/ryu][upstream].
+
+*Requirements: this crate supports any compiler version back to rustc 1.31; it
+uses nothing from the Rust standard library so is usable from no_std crates.*
+
+[paper]: https://dl.acm.org/citation.cfm?id=3192369
+[upstream]: https://github.com/ulfjack/ryu/tree/1c413e127f8d02afd12eb6259bc80163722f1385
+
+```toml
+[dependencies]
+ryu = "1.0"
+```
+
+## Example
+
+```rust
+fn main() {
+    let mut buffer = ryu::Buffer::new();
+    let printed = buffer.format(1.234);
+    assert_eq!(printed, "1.234");
+}
+```
+
+## Performance
+
+You can run upstream's benchmarks with:
+
+```console
+$ git clone https://github.com/ulfjack/ryu c-ryu
+$ cd c-ryu
+$ bazel run -c opt //ryu/benchmark
+```
+
+And the same benchmark against our implementation with:
+
+```console
+$ git clone https://github.com/dtolnay/ryu rust-ryu
+$ cd rust-ryu
+$ cargo run --example upstream_benchmark --release
+```
+
+These benchmarks measure the average time to print a 32-bit float and average
+time to print a 64-bit float, where the inputs are distributed as uniform random
+bit patterns 32 and 64 bits wide.
+
+The upstream C code, the unsafe direct Rust port, and the safe pretty Rust API
+all perform the same, taking around 21 nanoseconds to format a 32-bit float and
+31 nanoseconds to format a 64-bit float.
+
+There is also a Rust-specific benchmark comparing this implementation to the
+standard library which you can run with:
+
+```console
+$ cargo bench
+```
+
+The benchmark shows Ryū approximately 4-10x faster than the standard library
+across a range of f32 and f64 inputs. Measurements are in nanoseconds per
+iteration; smaller is better.
+
+| type=f32 | 0.0  | 0.1234 | 2.718281828459045 | f32::MAX |
+|:--------:|:----:|:------:|:-----------------:|:--------:|
+| RYU      | 3ns  | 28ns   | 23ns              | 22ns     |
+| STD      | 40ns | 106ns  | 128ns             | 110ns    |
+
+| type=f64 | 0.0  | 0.1234 | 2.718281828459045 | f64::MAX |
+|:--------:|:----:|:------:|:-----------------:|:--------:|
+| RYU      | 3ns  | 50ns   | 35ns              | 32ns     |
+| STD      | 39ns | 105ns  | 128ns             | 202ns    |
+
+## Formatting
+
+This library tends to produce more human-readable output than the standard
+library's to\_string, which never uses scientific notation. Here are two
+examples:
+
+- *ryu:* 1.23e40, *std:* 12300000000000000000000000000000000000000
+- *ryu:* 1.23e-40, *std:* 0.000000000000000000000000000000000000000123
+
+Both libraries print short decimals such as 0.0000123 without scientific
+notation.
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-BOOST">Boost Software License 1.0</a> at your
+option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/benches/bench.rs b/benches/bench.rs
new file mode 100644
index 0000000..8da2cac
--- /dev/null
+++ b/benches/bench.rs
@@ -0,0 +1,57 @@
+// cargo bench
+
+#![feature(test)]
+
+extern crate test;
+
+use std::io::Write;
+use std::{f32, f64};
+use test::{black_box, Bencher};
+
+macro_rules! benches {
+    ($($name:ident($value:expr),)*) => {
+        mod bench_ryu {
+            use super::*;
+            $(
+                #[bench]
+                fn $name(b: &mut Bencher) {
+                    let mut buf = ryu::Buffer::new();
+
+                    b.iter(move || {
+                        let value = black_box($value);
+                        let formatted = buf.format_finite(value);
+                        black_box(formatted);
+                    });
+                }
+            )*
+        }
+
+        mod bench_std_fmt {
+            use super::*;
+            $(
+                #[bench]
+                fn $name(b: &mut Bencher) {
+                    let mut buf = Vec::with_capacity(20);
+
+                    b.iter(|| {
+                        buf.clear();
+                        let value = black_box($value);
+                        write!(&mut buf, "{}", value).unwrap();
+                        black_box(buf.as_slice());
+                    });
+                }
+            )*
+        }
+    };
+}
+
+benches! {
+    bench_0_f64(0f64),
+    bench_short_f64(0.1234f64),
+    bench_e_f64(2.718281828459045f64),
+    bench_max_f64(f64::MAX),
+    bench_0_f32(0f32),
+    bench_short_f32(0.1234f32),
+    bench_e_f32(2.718281828459045f32),
+    bench_max_f32(f32::MAX),
+}
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..f48477a
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,40 @@
+use std::env;
+use std::process::Command;
+use std::str::{self, FromStr};
+
+// The rustc-cfg strings below are *not* public API. Please let us know by
+// opening a GitHub issue if your build environment requires some way to enable
+// these cfgs other than by executing our build script.
+fn main() {
+    let minor = match rustc_minor_version() {
+        Some(minor) => minor,
+        None => return,
+    };
+
+    let target = env::var("TARGET").unwrap();
+    let emscripten = target == "asmjs-unknown-emscripten" || target == "wasm32-unknown-emscripten";
+
+    // 128-bit integers disabled on Emscripten targets as Emscripten doesn't
+    // currently support integers larger than 64 bits.
+    if !emscripten {
+        println!("cargo:rustc-cfg=integer128");
+    }
+
+    // MaybeUninit<T> stabilized in Rust 1.36:
+    // https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html
+    if minor >= 36 {
+        println!("cargo:rustc-cfg=maybe_uninit");
+    }
+}
+
+fn rustc_minor_version() -> Option<u32> {
+    let rustc = env::var_os("RUSTC")?;
+    let output = Command::new(rustc).arg("--version").output().ok()?;
+    let version = str::from_utf8(&output.stdout).ok()?;
+    let mut pieces = version.split('.');
+    if pieces.next() != Some("rustc 1") {
+        return None;
+    }
+    let next = pieces.next()?;
+    u32::from_str(next).ok()
+}
diff --git a/examples/upstream_benchmark.rs b/examples/upstream_benchmark.rs
new file mode 100644
index 0000000..437855b
--- /dev/null
+++ b/examples/upstream_benchmark.rs
@@ -0,0 +1,85 @@
+// cargo run --example upstream_benchmark --release
+
+use rand::{Rng, SeedableRng};
+
+const SAMPLES: usize = 10000;
+const ITERATIONS: usize = 1000;
+
+struct MeanAndVariance {
+    n: i64,
+    mean: f64,
+    m2: f64,
+}
+
+impl MeanAndVariance {
+    fn new() -> Self {
+        MeanAndVariance {
+            n: 0,
+            mean: 0.0,
+            m2: 0.0,
+        }
+    }
+
+    fn update(&mut self, x: f64) {
+        self.n += 1;
+        let d = x - self.mean;
+        self.mean += d / self.n as f64;
+        let d2 = x - self.mean;
+        self.m2 += d * d2;
+    }
+
+    fn variance(&self) -> f64 {
+        self.m2 / (self.n - 1) as f64
+    }
+
+    fn stddev(&self) -> f64 {
+        self.variance().sqrt()
+    }
+}
+
+macro_rules! benchmark {
+    ($name:ident, $ty:ident) => {
+        fn $name() -> usize {
+            let mut rng = rand_xorshift::XorShiftRng::from_seed([123u8; 16]);
+            let mut mv = MeanAndVariance::new();
+            let mut throwaway = 0;
+            for _ in 0..SAMPLES {
+                let f = loop {
+                    let f = $ty::from_bits(rng.gen());
+                    if f.is_finite() {
+                        break f;
+                    }
+                };
+
+                let t1 = std::time::SystemTime::now();
+                for _ in 0..ITERATIONS {
+                    throwaway += ryu::Buffer::new().format_finite(f).len();
+                }
+                let duration = t1.elapsed().unwrap();
+                let nanos = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64;
+                mv.update(nanos as f64 / ITERATIONS as f64);
+            }
+            println!(
+                "{:12} {:8.3} {:8.3}",
+                concat!(stringify!($name), ":"),
+                mv.mean,
+                mv.stddev(),
+            );
+            throwaway
+        }
+    };
+}
+
+benchmark!(pretty32, f32);
+benchmark!(pretty64, f64);
+
+fn main() {
+    println!("{:>20}{:>9}", "Average", "Stddev");
+    let mut throwaway = 0;
+    throwaway += pretty32();
+    throwaway += pretty64();
+    if std::env::var_os("ryu-benchmark").is_some() {
+        // Prevent the compiler from optimizing the code away.
+        println!("{}", throwaway);
+    }
+}
diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs
new file mode 100644
index 0000000..df21fe0
--- /dev/null
+++ b/src/buffer/mod.rs
@@ -0,0 +1,180 @@
+use crate::raw;
+#[cfg(maybe_uninit)]
+use core::mem::MaybeUninit;
+use core::{mem, slice, str};
+#[cfg(feature = "no-panic")]
+use no_panic::no_panic;
+
+const NAN: &'static str = "NaN";
+const INFINITY: &'static str = "inf";
+const NEG_INFINITY: &'static str = "-inf";
+
+/// Safe API for formatting floating point numbers to text.
+///
+/// ## Example
+///
+/// ```
+/// let mut buffer = ryu::Buffer::new();
+/// let printed = buffer.format_finite(1.234);
+/// assert_eq!(printed, "1.234");
+/// ```
+pub struct Buffer {
+    #[cfg(maybe_uninit)]
+    bytes: [MaybeUninit<u8>; 24],
+    #[cfg(not(maybe_uninit))]
+    bytes: [u8; 24],
+}
+
+impl Buffer {
+    /// This is a cheap operation; you don't need to worry about reusing buffers
+    /// for efficiency.
+    #[inline]
+    #[cfg_attr(feature = "no-panic", no_panic)]
+    pub fn new() -> Self {
+        // assume_init is safe here, since this is an array of MaybeUninit, which does not need
+        // to be initialized.
+        #[cfg(maybe_uninit)]
+        let bytes = [MaybeUninit::<u8>::uninit(); 24];
+        #[cfg(not(maybe_uninit))]
+        let bytes = unsafe { mem::uninitialized() };
+
+        Buffer { bytes: bytes }
+    }
+
+    /// Print a floating point number into this buffer and return a reference to
+    /// its string representation within the buffer.
+    ///
+    /// # Special cases
+    ///
+    /// This function formats NaN as the string "NaN", positive infinity as
+    /// "inf", and negative infinity as "-inf" to match std::fmt.
+    ///
+    /// If your input is known to be finite, you may get better performance by
+    /// calling the `format_finite` method instead of `format` to avoid the
+    /// checks for special cases.
+    #[cfg_attr(feature = "no-panic", inline)]
+    #[cfg_attr(feature = "no-panic", no_panic)]
+    pub fn format<F: Float>(&mut self, f: F) -> &str {
+        if f.is_nonfinite() {
+            f.format_nonfinite()
+        } else {
+            self.format_finite(f)
+        }
+    }
+
+    /// Print a floating point number into this buffer and return a reference to
+    /// its string representation within the buffer.
+    ///
+    /// # Special cases
+    ///
+    /// This function **does not** check for NaN or infinity. If the input
+    /// number is not a finite float, the printed representation will be some
+    /// correctly formatted but unspecified numerical value.
+    ///
+    /// Please check [`is_finite`] yourself before calling this function, or
+    /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
+    ///
+    /// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
+    /// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
+    /// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
+    #[inline]
+    #[cfg_attr(feature = "no-panic", no_panic)]
+    pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
+        unsafe {
+            let n = f.write_to_ryu_buffer(self.bytes.as_mut_ptr() as *mut u8);
+            debug_assert!(n <= self.bytes.len());
+            let slice = slice::from_raw_parts(self.bytes.as_ptr() as *const u8, n);
+            str::from_utf8_unchecked(slice)
+        }
+    }
+}
+
+impl Copy for Buffer {}
+
+impl Clone for Buffer {
+    #[inline]
+    fn clone(&self) -> Self {
+        Buffer::new()
+    }
+}
+
+impl Default for Buffer {
+    #[inline]
+    #[cfg_attr(feature = "no-panic", no_panic)]
+    fn default() -> Self {
+        Buffer::new()
+    }
+}
+
+/// A floating point number, f32 or f64, that can be written into a
+/// [`ryu::Buffer`][Buffer].
+///
+/// This trait is sealed and cannot be implemented for types outside of the
+/// `ryu` crate.
+pub trait Float: Sealed {}
+impl Float for f32 {}
+impl Float for f64 {}
+
+pub trait Sealed: Copy {
+    fn is_nonfinite(self) -> bool;
+    fn format_nonfinite(self) -> &'static str;
+    unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
+}
+
+impl Sealed for f32 {
+    #[inline]
+    fn is_nonfinite(self) -> bool {
+        const EXP_MASK: u32 = 0x7f800000;
+        let bits = unsafe { mem::transmute::<f32, u32>(self) };
+        bits & EXP_MASK == EXP_MASK
+    }
+
+    #[cold]
+    #[cfg_attr(feature = "no-panic", inline)]
+    fn format_nonfinite(self) -> &'static str {
+        const MANTISSA_MASK: u32 = 0x007fffff;
+        const SIGN_MASK: u32 = 0x80000000;
+        let bits = unsafe { mem::transmute::<f32, u32>(self) };
+        if bits & MANTISSA_MASK != 0 {
+            NAN
+        } else if bits & SIGN_MASK != 0 {
+            NEG_INFINITY
+        } else {
+            INFINITY
+        }
+    }
+
+    #[inline]
+    unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
+        raw::format32(self, result)
+    }
+}
+
+impl Sealed for f64 {
+    #[inline]
+    fn is_nonfinite(self) -> bool {
+        const EXP_MASK: u64 = 0x7ff0000000000000;
+        let bits = unsafe { mem::transmute::<f64, u64>(self) };
+        bits & EXP_MASK == EXP_MASK
+    }
+
+    #[cold]
+    #[cfg_attr(feature = "no-panic", inline)]
+    fn format_nonfinite(self) -> &'static str {
+        const MANTISSA_MASK: u64 = 0x000fffffffffffff;
+        const SIGN_MASK: u64 = 0x8000000000000000;
+        let bits = unsafe { mem::transmute::<f64, u64>(self) };
+        if bits & MANTISSA_MASK != 0 {
+            NAN
+        } else if bits & SIGN_MASK != 0 {
+            NEG_INFINITY
+        } else {
+            INFINITY
+        }
+    }
+
+    #[inline]
+    unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
+        raw::format64(self, result)
+    }
+}
diff --git a/src/common.rs b/src/common.rs
new file mode 100644
index 0000000..9613036
--- /dev/null
+++ b/src/common.rs
@@ -0,0 +1,95 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+// Returns the number of decimal digits in v, which must not contain more than 9
+// digits.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn decimal_length9(v: u32) -> u32 {
+    // Function precondition: v is not a 10-digit number.
+    // (f2s: 9 digits are sufficient for round-tripping.)
+    debug_assert!(v < 1000000000);
+
+    if v >= 100000000 {
+        9
+    } else if v >= 10000000 {
+        8
+    } else if v >= 1000000 {
+        7
+    } else if v >= 100000 {
+        6
+    } else if v >= 10000 {
+        5
+    } else if v >= 1000 {
+        4
+    } else if v >= 100 {
+        3
+    } else if v >= 10 {
+        2
+    } else {
+        1
+    }
+}
+
+// Returns e == 0 ? 1 : [log_2(5^e)]; requires 0 <= e <= 3528.
+#[cfg_attr(feature = "no-panic", inline)]
+#[allow(dead_code)]
+pub fn log2_pow5(e: i32) -> i32 /* or u32 -> u32 */ {
+    // This approximation works up to the point that the multiplication
+    // overflows at e = 3529. If the multiplication were done in 64 bits, it
+    // would fail at 5^4004 which is just greater than 2^9297.
+    debug_assert!(e >= 0);
+    debug_assert!(e <= 3528);
+    ((e as u32 * 1217359) >> 19) as i32
+}
+
+// Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn pow5bits(e: i32) -> i32 /* or u32 -> u32 */ {
+    // This approximation works up to the point that the multiplication
+    // overflows at e = 3529. If the multiplication were done in 64 bits, it
+    // would fail at 5^4004 which is just greater than 2^9297.
+    debug_assert!(e >= 0);
+    debug_assert!(e <= 3528);
+    (((e as u32 * 1217359) >> 19) + 1) as i32
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+#[allow(dead_code)]
+pub fn ceil_log2_pow5(e: i32) -> i32 /* or u32 -> u32 */ {
+    log2_pow5(e) + 1
+}
+
+// Returns floor(log_10(2^e)); requires 0 <= e <= 1650.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn log10_pow2(e: i32) -> u32 /* or u32 -> u32 */ {
+    // The first value this approximation fails for is 2^1651 which is just greater than 10^297.
+    debug_assert!(e >= 0);
+    debug_assert!(e <= 1650);
+    (e as u32 * 78913) >> 18
+}
+
+// Returns floor(log_10(5^e)); requires 0 <= e <= 2620.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn log10_pow5(e: i32) -> u32 /* or u32 -> u32 */ {
+    // The first value this approximation fails for is 5^2621 which is just greater than 10^1832.
+    debug_assert!(e >= 0);
+    debug_assert!(e <= 2620);
+    (e as u32 * 732923) >> 20
+}
diff --git a/src/d2s.rs b/src/d2s.rs
new file mode 100644
index 0000000..862fd5f
--- /dev/null
+++ b/src/d2s.rs
@@ -0,0 +1,344 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+use crate::common::*;
+#[cfg(not(feature = "small"))]
+pub use crate::d2s_full_table::*;
+use crate::d2s_intrinsics::*;
+#[cfg(feature = "small")]
+pub use crate::d2s_small_table::*;
+#[cfg(not(maybe_uninit))]
+use core::mem;
+#[cfg(maybe_uninit)]
+use core::mem::MaybeUninit;
+
+pub const DOUBLE_MANTISSA_BITS: u32 = 52;
+pub const DOUBLE_EXPONENT_BITS: u32 = 11;
+pub const DOUBLE_BIAS: i32 = 1023;
+pub const DOUBLE_POW5_INV_BITCOUNT: i32 = 125;
+pub const DOUBLE_POW5_BITCOUNT: i32 = 125;
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn decimal_length17(v: u64) -> u32 {
+    // This is slightly faster than a loop.
+    // The average output length is 16.38 digits, so we check high-to-low.
+    // Function precondition: v is not an 18, 19, or 20-digit number.
+    // (17 digits are sufficient for round-tripping.)
+    debug_assert!(v < 100000000000000000);
+
+    if v >= 10000000000000000 {
+        17
+    } else if v >= 1000000000000000 {
+        16
+    } else if v >= 100000000000000 {
+        15
+    } else if v >= 10000000000000 {
+        14
+    } else if v >= 1000000000000 {
+        13
+    } else if v >= 100000000000 {
+        12
+    } else if v >= 10000000000 {
+        11
+    } else if v >= 1000000000 {
+        10
+    } else if v >= 100000000 {
+        9
+    } else if v >= 10000000 {
+        8
+    } else if v >= 1000000 {
+        7
+    } else if v >= 100000 {
+        6
+    } else if v >= 10000 {
+        5
+    } else if v >= 1000 {
+        4
+    } else if v >= 100 {
+        3
+    } else if v >= 10 {
+        2
+    } else {
+        1
+    }
+}
+
+// A floating decimal representing m * 10^e.
+pub struct FloatingDecimal64 {
+    pub mantissa: u64,
+    // Decimal exponent's range is -324 to 308
+    // inclusive, and can fit in i16 if needed.
+    pub exponent: i32,
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 {
+    let (e2, m2) = if ieee_exponent == 0 {
+        (
+            // We subtract 2 so that the bounds computation has 2 additional bits.
+            1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
+            ieee_mantissa,
+        )
+    } else {
+        (
+            ieee_exponent as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
+            (1u64 << DOUBLE_MANTISSA_BITS) | ieee_mantissa,
+        )
+    };
+    let even = (m2 & 1) == 0;
+    let accept_bounds = even;
+
+    // Step 2: Determine the interval of valid decimal representations.
+    let mv = 4 * m2;
+    // Implicit bool -> int conversion. True is 1, false is 0.
+    let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32;
+    // We would compute mp and mm like this:
+    // uint64_t mp = 4 * m2 + 2;
+    // uint64_t mm = mv - 1 - mm_shift;
+
+    // Step 3: Convert to a decimal power base using 128-bit arithmetic.
+    let mut vr: u64;
+    let mut vp: u64;
+    let mut vm: u64;
+    #[cfg(not(maybe_uninit))]
+    {
+        vp = unsafe { mem::uninitialized() };
+        vm = unsafe { mem::uninitialized() };
+    }
+    #[cfg(maybe_uninit)]
+    let mut vp_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
+    #[cfg(maybe_uninit)]
+    let mut vm_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
+    let e10: i32;
+    let mut vm_is_trailing_zeros = false;
+    let mut vr_is_trailing_zeros = false;
+    if e2 >= 0 {
+        // I tried special-casing q == 0, but there was no effect on performance.
+        // This expression is slightly faster than max(0, log10_pow2(e2) - 1).
+        let q = log10_pow2(e2) - (e2 > 3) as u32;
+        e10 = q as i32;
+        let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1;
+        let i = -e2 + q as i32 + k;
+        vr = unsafe {
+            mul_shift_all_64(
+                m2,
+                #[cfg(feature = "small")]
+                &compute_inv_pow5(q),
+                #[cfg(not(feature = "small"))]
+                {
+                    debug_assert!(q < DOUBLE_POW5_INV_SPLIT.len() as u32);
+                    DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize)
+                },
+                i as u32,
+                #[cfg(maybe_uninit)]
+                {
+                    vp_uninit.as_mut_ptr()
+                },
+                #[cfg(not(maybe_uninit))]
+                {
+                    &mut vp
+                },
+                #[cfg(maybe_uninit)]
+                {
+                    vm_uninit.as_mut_ptr()
+                },
+                #[cfg(not(maybe_uninit))]
+                {
+                    &mut vm
+                },
+                mm_shift,
+            )
+        };
+        #[cfg(maybe_uninit)]
+        {
+            vp = unsafe { vp_uninit.assume_init() };
+            vm = unsafe { vm_uninit.assume_init() };
+        }
+        if q <= 21 {
+            // This should use q <= 22, but I think 21 is also safe. Smaller values
+            // may still be safe, but it's more difficult to reason about them.
+            // Only one of mp, mv, and mm can be a multiple of 5, if any.
+            let mv_mod5 = (mv as u32).wrapping_sub(5u32.wrapping_mul(div5(mv) as u32));
+            if mv_mod5 == 0 {
+                vr_is_trailing_zeros = multiple_of_power_of_5(mv, q);
+            } else if accept_bounds {
+                // Same as min(e2 + (~mm & 1), pow5_factor(mm)) >= q
+                // <=> e2 + (~mm & 1) >= q && pow5_factor(mm) >= q
+                // <=> true && pow5_factor(mm) >= q, since e2 >= q.
+                vm_is_trailing_zeros = multiple_of_power_of_5(mv - 1 - mm_shift as u64, q);
+            } else {
+                // Same as min(e2 + 1, pow5_factor(mp)) >= q.
+                vp -= multiple_of_power_of_5(mv + 2, q) as u64;
+            }
+        }
+    } else {
+        // This expression is slightly faster than max(0, log10_pow5(-e2) - 1).
+        let q = log10_pow5(-e2) - (-e2 > 1) as u32;
+        e10 = q as i32 + e2;
+        let i = -e2 - q as i32;
+        let k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
+        let j = q as i32 - k;
+        vr = unsafe {
+            mul_shift_all_64(
+                m2,
+                #[cfg(feature = "small")]
+                &compute_pow5(i as u32),
+                #[cfg(not(feature = "small"))]
+                {
+                    debug_assert!(i < DOUBLE_POW5_SPLIT.len() as i32);
+                    DOUBLE_POW5_SPLIT.get_unchecked(i as usize)
+                },
+                j as u32,
+                #[cfg(maybe_uninit)]
+                {
+                    vp_uninit.as_mut_ptr()
+                },
+                #[cfg(not(maybe_uninit))]
+                {
+                    &mut vp
+                },
+                #[cfg(maybe_uninit)]
+                {
+                    vm_uninit.as_mut_ptr()
+                },
+                #[cfg(not(maybe_uninit))]
+                {
+                    &mut vm
+                },
+                mm_shift,
+            )
+        };
+        #[cfg(maybe_uninit)]
+        {
+            vp = unsafe { vp_uninit.assume_init() };
+            vm = unsafe { vm_uninit.assume_init() };
+        }
+        if q <= 1 {
+            // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
+            // mv = 4 * m2, so it always has at least two trailing 0 bits.
+            vr_is_trailing_zeros = true;
+            if accept_bounds {
+                // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
+                vm_is_trailing_zeros = mm_shift == 1;
+            } else {
+                // mp = mv + 2, so it always has at least one trailing 0 bit.
+                vp -= 1;
+            }
+        } else if q < 63 {
+            // TODO(ulfjack): Use a tighter bound here.
+            // We want to know if the full product has at least q trailing zeros.
+            // We need to compute min(p2(mv), p5(mv) - e2) >= q
+            // <=> p2(mv) >= q && p5(mv) - e2 >= q
+            // <=> p2(mv) >= q (because -e2 >= q)
+            vr_is_trailing_zeros = multiple_of_power_of_2(mv, q);
+        }
+    }
+
+    // Step 4: Find the shortest decimal representation in the interval of valid representations.
+    let mut removed = 0i32;
+    let mut last_removed_digit = 0u8;
+    // On average, we remove ~2 digits.
+    let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
+        // General case, which happens rarely (~0.7%).
+        loop {
+            let vp_div10 = div10(vp);
+            let vm_div10 = div10(vm);
+            if vp_div10 <= vm_div10 {
+                break;
+            }
+            let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
+            let vr_div10 = div10(vr);
+            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
+            vm_is_trailing_zeros &= vm_mod10 == 0;
+            vr_is_trailing_zeros &= last_removed_digit == 0;
+            last_removed_digit = vr_mod10 as u8;
+            vr = vr_div10;
+            vp = vp_div10;
+            vm = vm_div10;
+            removed += 1;
+        }
+        if vm_is_trailing_zeros {
+            loop {
+                let vm_div10 = div10(vm);
+                let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
+                if vm_mod10 != 0 {
+                    break;
+                }
+                let vp_div10 = div10(vp);
+                let vr_div10 = div10(vr);
+                let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
+                vr_is_trailing_zeros &= last_removed_digit == 0;
+                last_removed_digit = vr_mod10 as u8;
+                vr = vr_div10;
+                vp = vp_div10;
+                vm = vm_div10;
+                removed += 1;
+            }
+        }
+        if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
+            // Round even if the exact number is .....50..0.
+            last_removed_digit = 4;
+        }
+        // We need to take vr + 1 if vr is outside bounds or we need to round up.
+        vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
+            as u64
+    } else {
+        // Specialized for the common case (~99.3%). Percentages below are relative to this.
+        let mut round_up = false;
+        let vp_div100 = div100(vp);
+        let vm_div100 = div100(vm);
+        // Optimization: remove two digits at a time (~86.2%).
+        if vp_div100 > vm_div100 {
+            let vr_div100 = div100(vr);
+            let vr_mod100 = (vr as u32).wrapping_sub(100u32.wrapping_mul(vr_div100 as u32));
+            round_up = vr_mod100 >= 50;
+            vr = vr_div100;
+            vp = vp_div100;
+            vm = vm_div100;
+            removed += 2;
+        }
+        // Loop iterations below (approximately), without optimization above:
+        // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%
+        // Loop iterations below (approximately), with optimization above:
+        // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%
+        loop {
+            let vp_div10 = div10(vp);
+            let vm_div10 = div10(vm);
+            if vp_div10 <= vm_div10 {
+                break;
+            }
+            let vr_div10 = div10(vr);
+            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
+            round_up = vr_mod10 >= 5;
+            vr = vr_div10;
+            vp = vp_div10;
+            vm = vm_div10;
+            removed += 1;
+        }
+        // We need to take vr + 1 if vr is outside bounds or we need to round up.
+        vr + (vr == vm || round_up) as u64
+    };
+    let exp = e10 + removed;
+
+    FloatingDecimal64 {
+        exponent: exp,
+        mantissa: output,
+    }
+}
diff --git a/src/d2s_full_table.rs b/src/d2s_full_table.rs
new file mode 100644
index 0000000..7534ddd
--- /dev/null
+++ b/src/d2s_full_table.rs
@@ -0,0 +1,696 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+const DOUBLE_POW5_INV_TABLE_SIZE: usize = 342;
+const DOUBLE_POW5_TABLE_SIZE: usize = 326;
+
+pub static DOUBLE_POW5_INV_SPLIT: [(u64, u64); DOUBLE_POW5_INV_TABLE_SIZE] = [
+    (1, 2305843009213693952),
+    (11068046444225730970, 1844674407370955161),
+    (5165088340638674453, 1475739525896764129),
+    (7821419487252849886, 1180591620717411303),
+    (8824922364862649494, 1888946593147858085),
+    (7059937891890119595, 1511157274518286468),
+    (13026647942995916322, 1208925819614629174),
+    (9774590264567735146, 1934281311383406679),
+    (11509021026396098440, 1547425049106725343),
+    (16585914450600699399, 1237940039285380274),
+    (15469416676735388068, 1980704062856608439),
+    (16064882156130220778, 1584563250285286751),
+    (9162556910162266299, 1267650600228229401),
+    (7281393426775805432, 2028240960365167042),
+    (16893161185646375315, 1622592768292133633),
+    (2446482504291369283, 1298074214633706907),
+    (7603720821608101175, 2076918743413931051),
+    (2393627842544570617, 1661534994731144841),
+    (16672297533003297786, 1329227995784915872),
+    (11918280793837635165, 2126764793255865396),
+    (5845275820328197809, 1701411834604692317),
+    (15744267100488289217, 1361129467683753853),
+    (3054734472329800808, 2177807148294006166),
+    (17201182836831481939, 1742245718635204932),
+    (6382248639981364905, 1393796574908163946),
+    (2832900194486363201, 2230074519853062314),
+    (5955668970331000884, 1784059615882449851),
+    (1075186361522890384, 1427247692705959881),
+    (12788344622662355584, 2283596308329535809),
+    (13920024512871794791, 1826877046663628647),
+    (3757321980813615186, 1461501637330902918),
+    (10384555214134712795, 1169201309864722334),
+    (5547241898389809503, 1870722095783555735),
+    (4437793518711847602, 1496577676626844588),
+    (10928932444453298728, 1197262141301475670),
+    (17486291911125277965, 1915619426082361072),
+    (6610335899416401726, 1532495540865888858),
+    (12666966349016942027, 1225996432692711086),
+    (12888448528943286597, 1961594292308337738),
+    (17689456452638449924, 1569275433846670190),
+    (14151565162110759939, 1255420347077336152),
+    (7885109000409574610, 2008672555323737844),
+    (9997436015069570011, 1606938044258990275),
+    (7997948812055656009, 1285550435407192220),
+    (12796718099289049614, 2056880696651507552),
+    (2858676849947419045, 1645504557321206042),
+    (13354987924183666206, 1316403645856964833),
+    (17678631863951955605, 2106245833371143733),
+    (3074859046935833515, 1684996666696914987),
+    (13527933681774397782, 1347997333357531989),
+    (10576647446613305481, 2156795733372051183),
+    (15840015586774465031, 1725436586697640946),
+    (8982663654677661702, 1380349269358112757),
+    (18061610662226169046, 2208558830972980411),
+    (10759939715039024913, 1766847064778384329),
+    (12297300586773130254, 1413477651822707463),
+    (15986332124095098083, 2261564242916331941),
+    (9099716884534168143, 1809251394333065553),
+    (14658471137111155161, 1447401115466452442),
+    (4348079280205103483, 1157920892373161954),
+    (14335624477811986218, 1852673427797059126),
+    (7779150767507678651, 1482138742237647301),
+    (2533971799264232598, 1185710993790117841),
+    (15122401323048503126, 1897137590064188545),
+    (12097921058438802501, 1517710072051350836),
+    (5988988032009131678, 1214168057641080669),
+    (16961078480698431330, 1942668892225729070),
+    (13568862784558745064, 1554135113780583256),
+    (7165741412905085728, 1243308091024466605),
+    (11465186260648137165, 1989292945639146568),
+    (16550846638002330379, 1591434356511317254),
+    (16930026125143774626, 1273147485209053803),
+    (4951948911778577463, 2037035976334486086),
+    (272210314680951647, 1629628781067588869),
+    (3907117066486671641, 1303703024854071095),
+    (6251387306378674625, 2085924839766513752),
+    (16069156289328670670, 1668739871813211001),
+    (9165976216721026213, 1334991897450568801),
+    (7286864317269821294, 2135987035920910082),
+    (16897537898041588005, 1708789628736728065),
+    (13518030318433270404, 1367031702989382452),
+    (6871453250525591353, 2187250724783011924),
+    (9186511415162383406, 1749800579826409539),
+    (11038557946871817048, 1399840463861127631),
+    (10282995085511086630, 2239744742177804210),
+    (8226396068408869304, 1791795793742243368),
+    (13959814484210916090, 1433436634993794694),
+    (11267656730511734774, 2293498615990071511),
+    (5324776569667477496, 1834798892792057209),
+    (7949170070475892320, 1467839114233645767),
+    (17427382500606444826, 1174271291386916613),
+    (5747719112518849781, 1878834066219066582),
+    (15666221734240810795, 1503067252975253265),
+    (12532977387392648636, 1202453802380202612),
+    (5295368560860596524, 1923926083808324180),
+    (4236294848688477220, 1539140867046659344),
+    (7078384693692692099, 1231312693637327475),
+    (11325415509908307358, 1970100309819723960),
+    (9060332407926645887, 1576080247855779168),
+    (14626963555825137356, 1260864198284623334),
+    (12335095245094488799, 2017382717255397335),
+    (9868076196075591040, 1613906173804317868),
+    (15273158586344293478, 1291124939043454294),
+    (13369007293925138595, 2065799902469526871),
+    (7005857020398200553, 1652639921975621497),
+    (16672732060544291412, 1322111937580497197),
+    (11918976037903224966, 2115379100128795516),
+    (5845832015580669650, 1692303280103036413),
+    (12055363241948356366, 1353842624082429130),
+    (841837113407818570, 2166148198531886609),
+    (4362818505468165179, 1732918558825509287),
+    (14558301248600263113, 1386334847060407429),
+    (12225235553534690011, 2218135755296651887),
+    (2401490813343931363, 1774508604237321510),
+    (1921192650675145090, 1419606883389857208),
+    (17831303500047873437, 2271371013423771532),
+    (6886345170554478103, 1817096810739017226),
+    (1819727321701672159, 1453677448591213781),
+    (16213177116328979020, 1162941958872971024),
+    (14873036941900635463, 1860707134196753639),
+    (15587778368262418694, 1488565707357402911),
+    (8780873879868024632, 1190852565885922329),
+    (2981351763563108441, 1905364105417475727),
+    (13453127855076217722, 1524291284333980581),
+    (7073153469319063855, 1219433027467184465),
+    (11317045550910502167, 1951092843947495144),
+    (12742985255470312057, 1560874275157996115),
+    (10194388204376249646, 1248699420126396892),
+    (1553625868034358140, 1997919072202235028),
+    (8621598323911307159, 1598335257761788022),
+    (17965325103354776697, 1278668206209430417),
+    (13987124906400001422, 2045869129935088668),
+    (121653480894270168, 1636695303948070935),
+    (97322784715416134, 1309356243158456748),
+    (14913111714512307107, 2094969989053530796),
+    (8241140556867935363, 1675975991242824637),
+    (17660958889720079260, 1340780792994259709),
+    (17189487779326395846, 2145249268790815535),
+    (13751590223461116677, 1716199415032652428),
+    (18379969808252713988, 1372959532026121942),
+    (14650556434236701088, 2196735251241795108),
+    (652398703163629901, 1757388200993436087),
+    (11589965406756634890, 1405910560794748869),
+    (7475898206584884855, 2249456897271598191),
+    (2291369750525997561, 1799565517817278553),
+    (9211793429904618695, 1439652414253822842),
+    (18428218302589300235, 2303443862806116547),
+    (7363877012587619542, 1842755090244893238),
+    (13269799239553916280, 1474204072195914590),
+    (10615839391643133024, 1179363257756731672),
+    (2227947767661371545, 1886981212410770676),
+    (16539753473096738529, 1509584969928616540),
+    (13231802778477390823, 1207667975942893232),
+    (6413489186596184024, 1932268761508629172),
+    (16198837793502678189, 1545815009206903337),
+    (5580372605318321905, 1236652007365522670),
+    (8928596168509315048, 1978643211784836272),
+    (18210923379033183008, 1582914569427869017),
+    (7190041073742725760, 1266331655542295214),
+    (436019273762630246, 2026130648867672343),
+    (7727513048493924843, 1620904519094137874),
+    (9871359253537050198, 1296723615275310299),
+    (4726128361433549347, 2074757784440496479),
+    (7470251503888749801, 1659806227552397183),
+    (13354898832594820487, 1327844982041917746),
+    (13989140502667892133, 2124551971267068394),
+    (14880661216876224029, 1699641577013654715),
+    (11904528973500979224, 1359713261610923772),
+    (4289851098633925465, 2175541218577478036),
+    (18189276137874781665, 1740432974861982428),
+    (3483374466074094362, 1392346379889585943),
+    (1884050330976640656, 2227754207823337509),
+    (5196589079523222848, 1782203366258670007),
+    (15225317707844309248, 1425762693006936005),
+    (5913764258841343181, 2281220308811097609),
+    (8420360221814984868, 1824976247048878087),
+    (17804334621677718864, 1459980997639102469),
+    (17932816512084085415, 1167984798111281975),
+    (10245762345624985047, 1868775676978051161),
+    (4507261061758077715, 1495020541582440929),
+    (7295157664148372495, 1196016433265952743),
+    (7982903447895485668, 1913626293225524389),
+    (10075671573058298858, 1530901034580419511),
+    (4371188443704728763, 1224720827664335609),
+    (14372599139411386667, 1959553324262936974),
+    (15187428126271019657, 1567642659410349579),
+    (15839291315758726049, 1254114127528279663),
+    (3206773216762499739, 2006582604045247462),
+    (13633465017635730761, 1605266083236197969),
+    (14596120828850494932, 1284212866588958375),
+    (4907049252451240275, 2054740586542333401),
+    (236290587219081897, 1643792469233866721),
+    (14946427728742906810, 1315033975387093376),
+    (16535586736504830250, 2104054360619349402),
+    (5849771759720043554, 1683243488495479522),
+    (15747863852001765813, 1346594790796383617),
+    (10439186904235184007, 2154551665274213788),
+    (15730047152871967852, 1723641332219371030),
+    (12584037722297574282, 1378913065775496824),
+    (9066413911450387881, 2206260905240794919),
+    (10942479943902220628, 1765008724192635935),
+    (8753983955121776503, 1412006979354108748),
+    (10317025513452932081, 2259211166966573997),
+    (874922781278525018, 1807368933573259198),
+    (8078635854506640661, 1445895146858607358),
+    (13841606313089133175, 1156716117486885886),
+    (14767872471458792434, 1850745787979017418),
+    (746251532941302978, 1480596630383213935),
+    (597001226353042382, 1184477304306571148),
+    (15712597221132509104, 1895163686890513836),
+    (8880728962164096960, 1516130949512411069),
+    (10793931984473187891, 1212904759609928855),
+    (17270291175157100626, 1940647615375886168),
+    (2748186495899949531, 1552518092300708935),
+    (2198549196719959625, 1242014473840567148),
+    (18275073973719576693, 1987223158144907436),
+    (10930710364233751031, 1589778526515925949),
+    (12433917106128911148, 1271822821212740759),
+    (8826220925580526867, 2034916513940385215),
+    (7060976740464421494, 1627933211152308172),
+    (16716827836597268165, 1302346568921846537),
+    (11989529279587987770, 2083754510274954460),
+    (9591623423670390216, 1667003608219963568),
+    (15051996368420132820, 1333602886575970854),
+    (13015147745246481542, 2133764618521553367),
+    (3033420566713364587, 1707011694817242694),
+    (6116085268112601993, 1365609355853794155),
+    (9785736428980163188, 2184974969366070648),
+    (15207286772667951197, 1747979975492856518),
+    (1097782973908629988, 1398383980394285215),
+    (1756452758253807981, 2237414368630856344),
+    (5094511021344956708, 1789931494904685075),
+    (4075608817075965366, 1431945195923748060),
+    (6520974107321544586, 2291112313477996896),
+    (1527430471115325346, 1832889850782397517),
+    (12289990821117991246, 1466311880625918013),
+    (17210690286378213644, 1173049504500734410),
+    (9090360384495590213, 1876879207201175057),
+    (18340334751822203140, 1501503365760940045),
+    (14672267801457762512, 1201202692608752036),
+    (16096930852848599373, 1921924308174003258),
+    (1809498238053148529, 1537539446539202607),
+    (12515645034668249793, 1230031557231362085),
+    (1578287981759648052, 1968050491570179337),
+    (12330676829633449412, 1574440393256143469),
+    (13553890278448669853, 1259552314604914775),
+    (3239480371808320148, 2015283703367863641),
+    (17348979556414297411, 1612226962694290912),
+    (6500486015647617283, 1289781570155432730),
+    (10400777625036187652, 2063650512248692368),
+    (15699319729512770768, 1650920409798953894),
+    (16248804598352126938, 1320736327839163115),
+    (7551343283653851484, 2113178124542660985),
+    (6041074626923081187, 1690542499634128788),
+    (12211557331022285596, 1352433999707303030),
+    (1091747655926105338, 2163894399531684849),
+    (4562746939482794594, 1731115519625347879),
+    (7339546366328145998, 1384892415700278303),
+    (8053925371383123274, 2215827865120445285),
+    (6443140297106498619, 1772662292096356228),
+    (12533209867169019542, 1418129833677084982),
+    (5295740528502789974, 2269007733883335972),
+    (15304638867027962949, 1815206187106668777),
+    (4865013464138549713, 1452164949685335022),
+    (14960057215536570740, 1161731959748268017),
+    (9178696285890871890, 1858771135597228828),
+    (14721654658196518159, 1487016908477783062),
+    (4398626097073393881, 1189613526782226450),
+    (7037801755317430209, 1903381642851562320),
+    (5630241404253944167, 1522705314281249856),
+    (814844308661245011, 1218164251424999885),
+    (1303750893857992017, 1949062802279999816),
+    (15800395974054034906, 1559250241823999852),
+    (5261619149759407279, 1247400193459199882),
+    (12107939454356961969, 1995840309534719811),
+    (5997002748743659252, 1596672247627775849),
+    (8486951013736837725, 1277337798102220679),
+    (2511075177753209390, 2043740476963553087),
+    (13076906586428298482, 1634992381570842469),
+    (14150874083884549109, 1307993905256673975),
+    (4194654460505726958, 2092790248410678361),
+    (18113118827372222859, 1674232198728542688),
+    (3422448617672047318, 1339385758982834151),
+    (16543964232501006678, 2143017214372534641),
+    (9545822571258895019, 1714413771498027713),
+    (15015355686490936662, 1371531017198422170),
+    (5577825024675947042, 2194449627517475473),
+    (11840957649224578280, 1755559702013980378),
+    (16851463748863483271, 1404447761611184302),
+    (12204946739213931940, 2247116418577894884),
+    (13453306206113055875, 1797693134862315907),
+    (3383947335406624054, 1438154507889852726),
+    (16482362180876329456, 2301047212623764361),
+    (9496540929959153242, 1840837770099011489),
+    (11286581558709232917, 1472670216079209191),
+    (5339916432225476010, 1178136172863367353),
+    (4854517476818851293, 1885017876581387765),
+    (3883613981455081034, 1508014301265110212),
+    (14174937629389795797, 1206411441012088169),
+    (11611853762797942306, 1930258305619341071),
+    (5600134195496443521, 1544206644495472857),
+    (15548153800622885787, 1235365315596378285),
+    (6430302007287065643, 1976584504954205257),
+    (16212288050055383484, 1581267603963364205),
+    (12969830440044306787, 1265014083170691364),
+    (9683682259845159889, 2024022533073106183),
+    (15125643437359948558, 1619218026458484946),
+    (8411165935146048523, 1295374421166787957),
+    (17147214310975587960, 2072599073866860731),
+    (10028422634038560045, 1658079259093488585),
+    (8022738107230848036, 1326463407274790868),
+    (9147032156827446534, 2122341451639665389),
+    (11006974540203867551, 1697873161311732311),
+    (5116230817421183718, 1358298529049385849),
+    (15564666937357714594, 2173277646479017358),
+    (1383687105660440706, 1738622117183213887),
+    (12174996128754083534, 1390897693746571109),
+    (8411947361780802685, 2225436309994513775),
+    (6729557889424642148, 1780349047995611020),
+    (5383646311539713719, 1424279238396488816),
+    (1235136468979721303, 2278846781434382106),
+    (15745504434151418335, 1823077425147505684),
+    (16285752362063044992, 1458461940118004547),
+    (5649904260166615347, 1166769552094403638),
+    (5350498001524674232, 1866831283351045821),
+    (591049586477829062, 1493465026680836657),
+    (11540886113407994219, 1194772021344669325),
+    (18673707743239135, 1911635234151470921),
+    (14772334225162232601, 1529308187321176736),
+    (8128518565387875758, 1223446549856941389),
+    (1937583260394870242, 1957514479771106223),
+    (8928764237799716840, 1566011583816884978),
+    (14521709019723594119, 1252809267053507982),
+    (8477339172590109297, 2004494827285612772),
+    (17849917782297818407, 1603595861828490217),
+    (6901236596354434079, 1282876689462792174),
+    (18420676183650915173, 2052602703140467478),
+    (3668494502695001169, 1642082162512373983),
+    (10313493231639821582, 1313665730009899186),
+    (9122891541139893884, 2101865168015838698),
+    (14677010862395735754, 1681492134412670958),
+    (673562245690857633, 1345193707530136767),
+];
+
+pub static DOUBLE_POW5_SPLIT: [(u64, u64); DOUBLE_POW5_TABLE_SIZE] = [
+    (0, 1152921504606846976),
+    (0, 1441151880758558720),
+    (0, 1801439850948198400),
+    (0, 2251799813685248000),
+    (0, 1407374883553280000),
+    (0, 1759218604441600000),
+    (0, 2199023255552000000),
+    (0, 1374389534720000000),
+    (0, 1717986918400000000),
+    (0, 2147483648000000000),
+    (0, 1342177280000000000),
+    (0, 1677721600000000000),
+    (0, 2097152000000000000),
+    (0, 1310720000000000000),
+    (0, 1638400000000000000),
+    (0, 2048000000000000000),
+    (0, 1280000000000000000),
+    (0, 1600000000000000000),
+    (0, 2000000000000000000),
+    (0, 1250000000000000000),
+    (0, 1562500000000000000),
+    (0, 1953125000000000000),
+    (0, 1220703125000000000),
+    (0, 1525878906250000000),
+    (0, 1907348632812500000),
+    (0, 1192092895507812500),
+    (0, 1490116119384765625),
+    (4611686018427387904, 1862645149230957031),
+    (9799832789158199296, 1164153218269348144),
+    (12249790986447749120, 1455191522836685180),
+    (15312238733059686400, 1818989403545856475),
+    (14528612397897220096, 2273736754432320594),
+    (13692068767113150464, 1421085471520200371),
+    (12503399940464050176, 1776356839400250464),
+    (15629249925580062720, 2220446049250313080),
+    (9768281203487539200, 1387778780781445675),
+    (7598665485932036096, 1734723475976807094),
+    (274959820560269312, 2168404344971008868),
+    (9395221924704944128, 1355252715606880542),
+    (2520655369026404352, 1694065894508600678),
+    (12374191248137781248, 2117582368135750847),
+    (14651398557727195136, 1323488980084844279),
+    (13702562178731606016, 1654361225106055349),
+    (3293144668132343808, 2067951531382569187),
+    (18199116482078572544, 1292469707114105741),
+    (8913837547316051968, 1615587133892632177),
+    (15753982952572452864, 2019483917365790221),
+    (12152082354571476992, 1262177448353618888),
+    (15190102943214346240, 1577721810442023610),
+    (9764256642163156992, 1972152263052529513),
+    (17631875447420442880, 1232595164407830945),
+    (8204786253993389888, 1540743955509788682),
+    (1032610780636961552, 1925929944387235853),
+    (2951224747111794922, 1203706215242022408),
+    (3689030933889743652, 1504632769052528010),
+    (13834660704216955373, 1880790961315660012),
+    (17870034976990372916, 1175494350822287507),
+    (17725857702810578241, 1469367938527859384),
+    (3710578054803671186, 1836709923159824231),
+    (26536550077201078, 2295887403949780289),
+    (11545800389866720434, 1434929627468612680),
+    (14432250487333400542, 1793662034335765850),
+    (8816941072311974870, 2242077542919707313),
+    (17039803216263454053, 1401298464324817070),
+    (12076381983474541759, 1751623080406021338),
+    (5872105442488401391, 2189528850507526673),
+    (15199280947623720629, 1368455531567204170),
+    (9775729147674874978, 1710569414459005213),
+    (16831347453020981627, 2138211768073756516),
+    (1296220121283337709, 1336382355046097823),
+    (15455333206886335848, 1670477943807622278),
+    (10095794471753144002, 2088097429759527848),
+    (6309871544845715001, 1305060893599704905),
+    (12499025449484531656, 1631326116999631131),
+    (11012095793428276666, 2039157646249538914),
+    (11494245889320060820, 1274473528905961821),
+    (532749306367912313, 1593091911132452277),
+    (5277622651387278295, 1991364888915565346),
+    (7910200175544436838, 1244603055572228341),
+    (14499436237857933952, 1555753819465285426),
+    (8900923260467641632, 1944692274331606783),
+    (12480606065433357876, 1215432671457254239),
+    (10989071563364309441, 1519290839321567799),
+    (9124653435777998898, 1899113549151959749),
+    (8008751406574943263, 1186945968219974843),
+    (5399253239791291175, 1483682460274968554),
+    (15972438586593889776, 1854603075343710692),
+    (759402079766405302, 1159126922089819183),
+    (14784310654990170340, 1448908652612273978),
+    (9257016281882937117, 1811135815765342473),
+    (16182956370781059300, 2263919769706678091),
+    (7808504722524468110, 1414949856066673807),
+    (5148944884728197234, 1768687320083342259),
+    (1824495087482858639, 2210859150104177824),
+    (1140309429676786649, 1381786968815111140),
+    (1425386787095983311, 1727233711018888925),
+    (6393419502297367043, 2159042138773611156),
+    (13219259225790630210, 1349401336733506972),
+    (16524074032238287762, 1686751670916883715),
+    (16043406521870471799, 2108439588646104644),
+    (803757039314269066, 1317774742903815403),
+    (14839754354425000045, 1647218428629769253),
+    (4714634887749086344, 2059023035787211567),
+    (9864175832484260821, 1286889397367007229),
+    (16941905809032713930, 1608611746708759036),
+    (2730638187581340797, 2010764683385948796),
+    (10930020904093113806, 1256727927116217997),
+    (18274212148543780162, 1570909908895272496),
+    (4396021111970173586, 1963637386119090621),
+    (5053356204195052443, 1227273366324431638),
+    (15540067292098591362, 1534091707905539547),
+    (14813398096695851299, 1917614634881924434),
+    (13870059828862294966, 1198509146801202771),
+    (12725888767650480803, 1498136433501503464),
+    (15907360959563101004, 1872670541876879330),
+    (14553786618154326031, 1170419088673049581),
+    (4357175217410743827, 1463023860841311977),
+    (10058155040190817688, 1828779826051639971),
+    (7961007781811134206, 2285974782564549964),
+    (14199001900486734687, 1428734239102843727),
+    (13137066357181030455, 1785917798878554659),
+    (11809646928048900164, 2232397248598193324),
+    (16604401366885338411, 1395248280373870827),
+    (16143815690179285109, 1744060350467338534),
+    (10956397575869330579, 2180075438084173168),
+    (6847748484918331612, 1362547148802608230),
+    (17783057643002690323, 1703183936003260287),
+    (17617136035325974999, 2128979920004075359),
+    (17928239049719816230, 1330612450002547099),
+    (17798612793722382384, 1663265562503183874),
+    (13024893955298202172, 2079081953128979843),
+    (5834715712847682405, 1299426220705612402),
+    (16516766677914378815, 1624282775882015502),
+    (11422586310538197711, 2030353469852519378),
+    (11750802462513761473, 1268970918657824611),
+    (10076817059714813937, 1586213648322280764),
+    (12596021324643517422, 1982767060402850955),
+    (5566670318688504437, 1239229412751781847),
+    (2346651879933242642, 1549036765939727309),
+    (7545000868343941206, 1936295957424659136),
+    (4715625542714963254, 1210184973390411960),
+    (5894531928393704067, 1512731216738014950),
+    (16591536947346905892, 1890914020922518687),
+    (17287239619732898039, 1181821263076574179),
+    (16997363506238734644, 1477276578845717724),
+    (2799960309088866689, 1846595723557147156),
+    (10973347230035317489, 1154122327223216972),
+    (13716684037544146861, 1442652909029021215),
+    (12534169028502795672, 1803316136286276519),
+    (11056025267201106687, 2254145170357845649),
+    (18439230838069161439, 1408840731473653530),
+    (13825666510731675991, 1761050914342066913),
+    (3447025083132431277, 2201313642927583642),
+    (6766076695385157452, 1375821026829739776),
+    (8457595869231446815, 1719776283537174720),
+    (10571994836539308519, 2149720354421468400),
+    (6607496772837067824, 1343575221513417750),
+    (17482743002901110588, 1679469026891772187),
+    (17241742735199000331, 2099336283614715234),
+    (15387775227926763111, 1312085177259197021),
+    (5399660979626290177, 1640106471573996277),
+    (11361262242960250625, 2050133089467495346),
+    (11712474920277544544, 1281333180917184591),
+    (10028907631919542777, 1601666476146480739),
+    (7924448521472040567, 2002083095183100924),
+    (14176152362774801162, 1251301934489438077),
+    (3885132398186337741, 1564127418111797597),
+    (9468101516160310080, 1955159272639746996),
+    (15140935484454969608, 1221974545399841872),
+    (479425281859160394, 1527468181749802341),
+    (5210967620751338397, 1909335227187252926),
+    (17091912818251750210, 1193334516992033078),
+    (12141518985959911954, 1491668146240041348),
+    (15176898732449889943, 1864585182800051685),
+    (11791404716994875166, 1165365739250032303),
+    (10127569877816206054, 1456707174062540379),
+    (8047776328842869663, 1820883967578175474),
+    (836348374198811271, 2276104959472719343),
+    (7440246761515338900, 1422565599670449589),
+    (13911994470321561530, 1778206999588061986),
+    (8166621051047176104, 2222758749485077483),
+    (2798295147690791113, 1389224218428173427),
+    (17332926989895652603, 1736530273035216783),
+    (17054472718942177850, 2170662841294020979),
+    (8353202440125167204, 1356664275808763112),
+    (10441503050156459005, 1695830344760953890),
+    (3828506775840797949, 2119787930951192363),
+    (86973725686804766, 1324867456844495227),
+    (13943775212390669669, 1656084321055619033),
+    (3594660960206173375, 2070105401319523792),
+    (2246663100128858359, 1293815875824702370),
+    (12031700912015848757, 1617269844780877962),
+    (5816254103165035138, 2021587305976097453),
+    (5941001823691840913, 1263492066235060908),
+    (7426252279614801142, 1579365082793826135),
+    (4671129331091113523, 1974206353492282669),
+    (5225298841145639904, 1233878970932676668),
+    (6531623551432049880, 1542348713665845835),
+    (3552843420862674446, 1927935892082307294),
+    (16055585193321335241, 1204959932551442058),
+    (10846109454796893243, 1506199915689302573),
+    (18169322836923504458, 1882749894611628216),
+    (11355826773077190286, 1176718684132267635),
+    (9583097447919099954, 1470898355165334544),
+    (11978871809898874942, 1838622943956668180),
+    (14973589762373593678, 2298278679945835225),
+    (2440964573842414192, 1436424174966147016),
+    (3051205717303017741, 1795530218707683770),
+    (13037379183483547984, 2244412773384604712),
+    (8148361989677217490, 1402757983365377945),
+    (14797138505523909766, 1753447479206722431),
+    (13884737113477499304, 2191809349008403039),
+    (15595489723564518921, 1369880843130251899),
+    (14882676136028260747, 1712351053912814874),
+    (9379973133180550126, 2140438817391018593),
+    (17391698254306313589, 1337774260869386620),
+    (3292878744173340370, 1672217826086733276),
+    (4116098430216675462, 2090272282608416595),
+    (266718509671728212, 1306420176630260372),
+    (333398137089660265, 1633025220787825465),
+    (5028433689789463235, 2041281525984781831),
+    (10060300083759496378, 1275800953740488644),
+    (12575375104699370472, 1594751192175610805),
+    (1884160825592049379, 1993438990219513507),
+    (17318501580490888525, 1245899368887195941),
+    (7813068920331446945, 1557374211108994927),
+    (5154650131986920777, 1946717763886243659),
+    (915813323278131534, 1216698602428902287),
+    (14979824709379828129, 1520873253036127858),
+    (9501408849870009354, 1901091566295159823),
+    (12855909558809837702, 1188182228934474889),
+    (2234828893230133415, 1485227786168093612),
+    (2793536116537666769, 1856534732710117015),
+    (8663489100477123587, 1160334207943823134),
+    (1605989338741628675, 1450417759929778918),
+    (11230858710281811652, 1813022199912223647),
+    (9426887369424876662, 2266277749890279559),
+    (12809333633531629769, 1416423593681424724),
+    (16011667041914537212, 1770529492101780905),
+    (6179525747111007803, 2213161865127226132),
+    (13085575628799155685, 1383226165704516332),
+    (16356969535998944606, 1729032707130645415),
+    (15834525901571292854, 2161290883913306769),
+    (2979049660840976177, 1350806802445816731),
+    (17558870131333383934, 1688508503057270913),
+    (8113529608884566205, 2110635628821588642),
+    (9682642023980241782, 1319147268013492901),
+    (16714988548402690132, 1648934085016866126),
+    (11670363648648586857, 2061167606271082658),
+    (11905663298832754689, 1288229753919426661),
+    (1047021068258779650, 1610287192399283327),
+    (15143834390605638274, 2012858990499104158),
+    (4853210475701136017, 1258036869061940099),
+    (1454827076199032118, 1572546086327425124),
+    (1818533845248790147, 1965682607909281405),
+    (3442426662494187794, 1228551629943300878),
+    (13526405364972510550, 1535689537429126097),
+    (3072948650933474476, 1919611921786407622),
+    (15755650962115585259, 1199757451116504763),
+    (15082877684217093670, 1499696813895630954),
+    (9630225068416591280, 1874621017369538693),
+    (8324733676974063502, 1171638135855961683),
+    (5794231077790191473, 1464547669819952104),
+    (7242788847237739342, 1830684587274940130),
+    (18276858095901949986, 2288355734093675162),
+    (16034722328366106645, 1430222333808546976),
+    (1596658836748081690, 1787777917260683721),
+    (6607509564362490017, 2234722396575854651),
+    (1823850468512862308, 1396701497859909157),
+    (6891499104068465790, 1745876872324886446),
+    (17837745916940358045, 2182346090406108057),
+    (4231062170446641922, 1363966306503817536),
+    (5288827713058302403, 1704957883129771920),
+    (6611034641322878003, 2131197353912214900),
+    (13355268687681574560, 1331998346195134312),
+    (16694085859601968200, 1664997932743917890),
+    (11644235287647684442, 2081247415929897363),
+    (4971804045566108824, 1300779634956185852),
+    (6214755056957636030, 1625974543695232315),
+    (3156757802769657134, 2032468179619040394),
+    (6584659645158423613, 1270292612261900246),
+    (17454196593302805324, 1587865765327375307),
+    (17206059723201118751, 1984832206659219134),
+    (6142101308573311315, 1240520129162011959),
+    (3065940617289251240, 1550650161452514949),
+    (8444111790038951954, 1938312701815643686),
+    (665883850346957067, 1211445438634777304),
+    (832354812933696334, 1514306798293471630),
+    (10263815553021896226, 1892883497866839537),
+    (17944099766707154901, 1183052186166774710),
+    (13206752671529167818, 1478815232708468388),
+    (16508440839411459773, 1848519040885585485),
+    (12623618533845856310, 1155324400553490928),
+    (15779523167307320387, 1444155500691863660),
+    (1277659885424598868, 1805194375864829576),
+    (1597074856780748586, 2256492969831036970),
+    (5609857803915355770, 1410308106144398106),
+    (16235694291748970521, 1762885132680497632),
+    (1847873790976661535, 2203606415850622041),
+    (12684136165428883219, 1377254009906638775),
+    (11243484188358716120, 1721567512383298469),
+    (219297180166231438, 2151959390479123087),
+    (7054589765244976505, 1344974619049451929),
+    (13429923224983608535, 1681218273811814911),
+    (12175718012802122765, 2101522842264768639),
+    (14527352785642408584, 1313451776415480399),
+    (13547504963625622826, 1641814720519350499),
+    (12322695186104640628, 2052268400649188124),
+    (16925056528170176201, 1282667750405742577),
+    (7321262604930556539, 1603334688007178222),
+    (18374950293017971482, 2004168360008972777),
+    (4566814905495150320, 1252605225005607986),
+    (14931890668723713708, 1565756531257009982),
+    (9441491299049866327, 1957195664071262478),
+    (1289246043478778550, 1223247290044539049),
+    (6223243572775861092, 1529059112555673811),
+    (3167368447542438461, 1911323890694592264),
+    (1979605279714024038, 1194577431684120165),
+    (7086192618069917952, 1493221789605150206),
+    (18081112809442173248, 1866527237006437757),
+    (13606538515115052232, 1166579523129023598),
+    (7784801107039039482, 1458224403911279498),
+    (507629346944023544, 1822780504889099373),
+    (5246222702107417334, 2278475631111374216),
+    (3278889188817135834, 1424047269444608885),
+    (8710297504448807696, 1780059086805761106),
+];
diff --git a/src/d2s_intrinsics.rs b/src/d2s_intrinsics.rs
new file mode 100644
index 0000000..918ccab
--- /dev/null
+++ b/src/d2s_intrinsics.rs
@@ -0,0 +1,171 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+use core::ptr;
+
+// Returns (lo, hi).
+#[cfg(not(integer128))]
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn umul128(a: u64, b: u64) -> (u64, u64) {
+    let a_lo = a as u32;
+    let a_hi = (a >> 32) as u32;
+    let b_lo = b as u32;
+    let b_hi = (b >> 32) as u32;
+
+    let b00 = a_lo as u64 * b_lo as u64;
+    let b01 = a_lo as u64 * b_hi as u64;
+    let b10 = a_hi as u64 * b_lo as u64;
+    let b11 = a_hi as u64 * b_hi as u64;
+
+    let b00_lo = b00 as u32;
+    let b00_hi = (b00 >> 32) as u32;
+
+    let mid1 = b10 + b00_hi as u64;
+    let mid1_lo = mid1 as u32;
+    let mid1_hi = (mid1 >> 32) as u32;
+
+    let mid2 = b01 + mid1_lo as u64;
+    let mid2_lo = mid2 as u32;
+    let mid2_hi = (mid2 >> 32) as u32;
+
+    let p_hi = b11 + mid1_hi as u64 + mid2_hi as u64;
+    let p_lo = ((mid2_lo as u64) << 32) | b00_lo as u64;
+
+    (p_lo, p_hi)
+}
+
+#[cfg(not(integer128))]
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn shiftright128(lo: u64, hi: u64, dist: u32) -> u64 {
+    // We don't need to handle the case dist >= 64 here (see above).
+    debug_assert!(dist > 0);
+    debug_assert!(dist < 64);
+    (hi << (64 - dist)) | (lo >> dist)
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn div5(x: u64) -> u64 {
+    x / 5
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn div10(x: u64) -> u64 {
+    x / 10
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn div100(x: u64) -> u64 {
+    x / 100
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+fn pow5_factor(mut value: u64) -> u32 {
+    let mut count = 0u32;
+    loop {
+        debug_assert!(value != 0);
+        let q = div5(value);
+        let r = (value as u32).wrapping_sub(5u32.wrapping_mul(q as u32));
+        if r != 0 {
+            break;
+        }
+        value = q;
+        count += 1;
+    }
+    count
+}
+
+// Returns true if value is divisible by 5^p.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn multiple_of_power_of_5(value: u64, p: u32) -> bool {
+    // I tried a case distinction on p, but there was no performance difference.
+    pow5_factor(value) >= p
+}
+
+// Returns true if value is divisible by 2^p.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn multiple_of_power_of_2(value: u64, p: u32) -> bool {
+    debug_assert!(value != 0);
+    debug_assert!(p < 64);
+    // __builtin_ctzll doesn't appear to be faster here.
+    (value & ((1u64 << p) - 1)) == 0
+}
+
+#[cfg(integer128)]
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn mul_shift_64(m: u64, mul: &(u64, u64), j: u32) -> u64 {
+    let b0 = m as u128 * mul.0 as u128;
+    let b2 = m as u128 * mul.1 as u128;
+    (((b0 >> 64) + b2) >> (j - 64)) as u64
+}
+
+#[cfg(integer128)]
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn mul_shift_all_64(
+    m: u64,
+    mul: &(u64, u64),
+    j: u32,
+    vp: *mut u64,
+    vm: *mut u64,
+    mm_shift: u32,
+) -> u64 {
+    ptr::write(vp, mul_shift_64(4 * m + 2, mul, j));
+    ptr::write(vm, mul_shift_64(4 * m - 1 - mm_shift as u64, mul, j));
+    mul_shift_64(4 * m, mul, j)
+}
+
+#[cfg(not(integer128))]
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn mul_shift_all_64(
+    mut m: u64,
+    mul: &(u64, u64),
+    j: u32,
+    vp: *mut u64,
+    vm: *mut u64,
+    mm_shift: u32,
+) -> u64 {
+    m <<= 1;
+    // m is maximum 55 bits
+    let (lo, tmp) = umul128(m, mul.0);
+    let (mut mid, mut hi) = umul128(m, mul.1);
+    mid = mid.wrapping_add(tmp);
+    hi = hi.wrapping_add((mid < tmp) as u64); // overflow into hi
+
+    let lo2 = lo.wrapping_add(mul.0);
+    let mid2 = mid.wrapping_add(mul.1).wrapping_add((lo2 < lo) as u64);
+    let hi2 = hi.wrapping_add((mid2 < mid) as u64);
+    ptr::write(vp, shiftright128(mid2, hi2, j - 64 - 1));
+
+    if mm_shift == 1 {
+        let lo3 = lo.wrapping_sub(mul.0);
+        let mid3 = mid.wrapping_sub(mul.1).wrapping_sub((lo3 > lo) as u64);
+        let hi3 = hi.wrapping_sub((mid3 > mid) as u64);
+        ptr::write(vm, shiftright128(mid3, hi3, j - 64 - 1));
+    } else {
+        let lo3 = lo + lo;
+        let mid3 = mid.wrapping_add(mid).wrapping_add((lo3 < lo) as u64);
+        let hi3 = hi.wrapping_add(hi).wrapping_add((mid3 < mid) as u64);
+        let lo4 = lo3.wrapping_sub(mul.0);
+        let mid4 = mid3.wrapping_sub(mul.1).wrapping_sub((lo4 > lo3) as u64);
+        let hi4 = hi3.wrapping_sub((mid4 > mid3) as u64);
+        ptr::write(vm, shiftright128(mid4, hi4, j - 64));
+    }
+
+    shiftright128(mid, hi, j - 64 - 1)
+}
diff --git a/src/d2s_small_table.rs b/src/d2s_small_table.rs
new file mode 100644
index 0000000..08519a2
--- /dev/null
+++ b/src/d2s_small_table.rs
@@ -0,0 +1,205 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+use crate::common::*;
+#[cfg(not(integer128))]
+use crate::d2s_intrinsics::*;
+
+pub static DOUBLE_POW5_INV_SPLIT2: [(u64, u64); 13] = [
+    (1, 2305843009213693952),
+    (5955668970331000884, 1784059615882449851),
+    (8982663654677661702, 1380349269358112757),
+    (7286864317269821294, 2135987035920910082),
+    (7005857020398200553, 1652639921975621497),
+    (17965325103354776697, 1278668206209430417),
+    (8928596168509315048, 1978643211784836272),
+    (10075671573058298858, 1530901034580419511),
+    (597001226353042382, 1184477304306571148),
+    (1527430471115325346, 1832889850782397517),
+    (12533209867169019542, 1418129833677084982),
+    (5577825024675947042, 2194449627517475473),
+    (11006974540203867551, 1697873161311732311),
+];
+
+pub static POW5_INV_OFFSETS: [u32; 19] = [
+    0x54544554, 0x04055545, 0x10041000, 0x00400414, 0x40010000, 0x41155555, 0x00000454, 0x00010044,
+    0x40000000, 0x44000041, 0x50454450, 0x55550054, 0x51655554, 0x40004000, 0x01000001, 0x00010500,
+    0x51515411, 0x05555554, 0x00000000,
+];
+
+pub static DOUBLE_POW5_SPLIT2: [(u64, u64); 13] = [
+    (0, 1152921504606846976),
+    (0, 1490116119384765625),
+    (1032610780636961552, 1925929944387235853),
+    (7910200175544436838, 1244603055572228341),
+    (16941905809032713930, 1608611746708759036),
+    (13024893955298202172, 2079081953128979843),
+    (6607496772837067824, 1343575221513417750),
+    (17332926989895652603, 1736530273035216783),
+    (13037379183483547984, 2244412773384604712),
+    (1605989338741628675, 1450417759929778918),
+    (9630225068416591280, 1874621017369538693),
+    (665883850346957067, 1211445438634777304),
+    (14931890668723713708, 1565756531257009982),
+];
+
+pub static POW5_OFFSETS: [u32; 21] = [
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40000000, 0x59695995, 0x55545555, 0x56555515,
+    0x41150504, 0x40555410, 0x44555145, 0x44504540, 0x45555550, 0x40004000, 0x96440440, 0x55565565,
+    0x54454045, 0x40154151, 0x55559155, 0x51405555, 0x00000105,
+];
+
+pub static DOUBLE_POW5_TABLE: [u64; 26] = [
+    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,
+];
+
+// Computes 5^i in the form required by Ryū.
+#[cfg(integer128)]
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn compute_pow5(i: u32) -> (u64, u64) {
+    let base = i / DOUBLE_POW5_TABLE.len() as u32;
+    let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
+    let offset = i - base2;
+    debug_assert!(base < DOUBLE_POW5_SPLIT2.len() as u32);
+    let mul = *DOUBLE_POW5_SPLIT2.get_unchecked(base as usize);
+    if offset == 0 {
+        return mul;
+    }
+    debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
+    let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize);
+    let b0 = m as u128 * mul.0 as u128;
+    let b2 = m as u128 * mul.1 as u128;
+    let delta = pow5bits(i as i32) - pow5bits(base2 as i32);
+    debug_assert!(base < POW5_OFFSETS.len() as u32);
+    let shifted_sum = (b0 >> delta)
+        + (b2 << (64 - delta))
+        + ((*POW5_OFFSETS.get_unchecked((i / 16) as usize) >> ((i % 16) << 1)) & 3) as u128;
+    (shifted_sum as u64, (shifted_sum >> 64) as u64)
+}
+
+// Computes 5^-i in the form required by Ryū.
+#[cfg(integer128)]
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn compute_inv_pow5(i: u32) -> (u64, u64) {
+    let base = (i + DOUBLE_POW5_TABLE.len() as u32 - 1) / DOUBLE_POW5_TABLE.len() as u32;
+    let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
+    let offset = base2 - i;
+    debug_assert!(base < DOUBLE_POW5_INV_SPLIT2.len() as u32);
+    let mul = *DOUBLE_POW5_INV_SPLIT2.get_unchecked(base as usize); // 1/5^base2
+    if offset == 0 {
+        return mul;
+    }
+    debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
+    let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize); // 5^offset
+    let b0 = m as u128 * (mul.0 - 1) as u128;
+    let b2 = m as u128 * mul.1 as u128; // 1/5^base2 * 5^offset = 1/5^(base2-offset) = 1/5^i
+    let delta = pow5bits(base2 as i32) - pow5bits(i as i32);
+    debug_assert!(base < POW5_INV_OFFSETS.len() as u32);
+    let shifted_sum = ((b0 >> delta) + (b2 << (64 - delta)))
+        + 1
+        + ((*POW5_INV_OFFSETS.get_unchecked((i / 16) as usize) >> ((i % 16) << 1)) & 3) as u128;
+    (shifted_sum as u64, (shifted_sum >> 64) as u64)
+}
+
+// Computes 5^i in the form required by Ryū, and stores it in the given pointer.
+#[cfg(not(integer128))]
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn compute_pow5(i: u32) -> (u64, u64) {
+    let base = i / DOUBLE_POW5_TABLE.len() as u32;
+    let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
+    let offset = i - base2;
+    debug_assert!(base < DOUBLE_POW5_SPLIT2.len() as u32);
+    let mul = *DOUBLE_POW5_SPLIT2.get_unchecked(base as usize);
+    if offset == 0 {
+        return mul;
+    }
+    debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
+    let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize);
+    let (low1, mut high1) = umul128(m, mul.1);
+    let (low0, high0) = umul128(m, mul.0);
+    let sum = high0 + low1;
+    if sum < high0 {
+        high1 += 1; // overflow into high1
+    }
+    // high1 | sum | low0
+    let delta = pow5bits(i as i32) - pow5bits(base2 as i32);
+    debug_assert!(base < POW5_OFFSETS.len() as u32);
+    (
+        shiftright128(low0, sum, delta as u32)
+            + ((*POW5_OFFSETS.get_unchecked((i / 16) as usize) >> ((i % 16) << 1)) & 3) as u64,
+        shiftright128(sum, high1, delta as u32),
+    )
+}
+
+// Computes 5^-i in the form required by Ryū, and stores it in the given pointer.
+#[cfg(not(integer128))]
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn compute_inv_pow5(i: u32) -> (u64, u64) {
+    let base = (i + DOUBLE_POW5_TABLE.len() as u32 - 1) / DOUBLE_POW5_TABLE.len() as u32;
+    let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
+    let offset = base2 - i;
+    debug_assert!(base < DOUBLE_POW5_INV_SPLIT2.len() as u32);
+    let mul = *DOUBLE_POW5_INV_SPLIT2.get_unchecked(base as usize); // 1/5^base2
+    if offset == 0 {
+        return mul;
+    }
+    debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
+    let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize);
+    let (low1, mut high1) = umul128(m, mul.1);
+    let (low0, high0) = umul128(m, mul.0 - 1);
+    let sum = high0 + low1;
+    if sum < high0 {
+        high1 += 1; // overflow into high1
+    }
+    // high1 | sum | low0
+    let delta = pow5bits(base2 as i32) - pow5bits(i as i32);
+    debug_assert!(base < POW5_INV_OFFSETS.len() as u32);
+    (
+        shiftright128(low0, sum, delta as u32)
+            + 1
+            + ((*POW5_INV_OFFSETS.get_unchecked((i / 16) as usize) >> ((i % 16) << 1)) & 3) as u64,
+        shiftright128(sum, high1, delta as u32),
+    )
+}
diff --git a/src/digit_table.rs b/src/digit_table.rs
new file mode 100644
index 0000000..d871f03
--- /dev/null
+++ b/src/digit_table.rs
@@ -0,0 +1,28 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+// A table of all two-digit numbers. This is used to speed up decimal digit
+// generation by copying pairs of digits into the final output.
+pub static DIGIT_TABLE: [u8; 200] = *b"\
+    0001020304050607080910111213141516171819\
+    2021222324252627282930313233343536373839\
+    4041424344454647484950515253545556575859\
+    6061626364656667686970717273747576777879\
+    8081828384858687888990919293949596979899";
diff --git a/src/f2s.rs b/src/f2s.rs
new file mode 100644
index 0000000..eeb457a
--- /dev/null
+++ b/src/f2s.rs
@@ -0,0 +1,176 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+use crate::common::*;
+use crate::f2s_intrinsics::*;
+
+pub const FLOAT_MANTISSA_BITS: u32 = 23;
+pub const FLOAT_EXPONENT_BITS: u32 = 8;
+const FLOAT_BIAS: i32 = 127;
+pub use crate::f2s_intrinsics::{FLOAT_POW5_BITCOUNT, FLOAT_POW5_INV_BITCOUNT};
+
+// A floating decimal representing m * 10^e.
+pub struct FloatingDecimal32 {
+    pub mantissa: u32,
+    // Decimal exponent's range is -45 to 38
+    // inclusive, and can fit in i16 if needed.
+    pub exponent: i32,
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn f2d(ieee_mantissa: u32, ieee_exponent: u32) -> FloatingDecimal32 {
+    let (e2, m2) = if ieee_exponent == 0 {
+        (
+            // We subtract 2 so that the bounds computation has 2 additional bits.
+            1 - FLOAT_BIAS - FLOAT_MANTISSA_BITS as i32 - 2,
+            ieee_mantissa,
+        )
+    } else {
+        (
+            ieee_exponent as i32 - FLOAT_BIAS - FLOAT_MANTISSA_BITS as i32 - 2,
+            (1u32 << FLOAT_MANTISSA_BITS) | ieee_mantissa,
+        )
+    };
+    let even = (m2 & 1) == 0;
+    let accept_bounds = even;
+
+    // Step 2: Determine the interval of valid decimal representations.
+    let mv = 4 * m2;
+    let mp = 4 * m2 + 2;
+    // Implicit bool -> int conversion. True is 1, false is 0.
+    let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32;
+    let mm = 4 * m2 - 1 - mm_shift;
+
+    // Step 3: Convert to a decimal power base using 64-bit arithmetic.
+    let mut vr: u32;
+    let mut vp: u32;
+    let mut vm: u32;
+    let e10: i32;
+    let mut vm_is_trailing_zeros = false;
+    let mut vr_is_trailing_zeros = false;
+    let mut last_removed_digit = 0u8;
+    if e2 >= 0 {
+        let q = log10_pow2(e2);
+        e10 = q as i32;
+        let k = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1;
+        let i = -e2 + q as i32 + k;
+        vr = mul_pow5_inv_div_pow2(mv, q, i);
+        vp = mul_pow5_inv_div_pow2(mp, q, i);
+        vm = mul_pow5_inv_div_pow2(mm, q, i);
+        if q != 0 && (vp - 1) / 10 <= vm / 10 {
+            // We need to know one removed digit even if we are not going to loop below. We could use
+            // q = X - 1 above, except that would require 33 bits for the result, and we've found that
+            // 32-bit arithmetic is faster even on 64-bit machines.
+            let l = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32 - 1) - 1;
+            last_removed_digit =
+                (mul_pow5_inv_div_pow2(mv, q - 1, -e2 + q as i32 - 1 + l) % 10) as u8;
+        }
+        if q <= 9 {
+            // The largest power of 5 that fits in 24 bits is 5^10, but q <= 9 seems to be safe as well.
+            // Only one of mp, mv, and mm can be a multiple of 5, if any.
+            if mv % 5 == 0 {
+                vr_is_trailing_zeros = multiple_of_power_of_5_32(mv, q);
+            } else if accept_bounds {
+                vm_is_trailing_zeros = multiple_of_power_of_5_32(mm, q);
+            } else {
+                vp -= multiple_of_power_of_5_32(mp, q) as u32;
+            }
+        }
+    } else {
+        let q = log10_pow5(-e2);
+        e10 = q as i32 + e2;
+        let i = -e2 - q as i32;
+        let k = pow5bits(i) - FLOAT_POW5_BITCOUNT;
+        let mut j = q as i32 - k;
+        vr = mul_pow5_div_pow2(mv, i as u32, j);
+        vp = mul_pow5_div_pow2(mp, i as u32, j);
+        vm = mul_pow5_div_pow2(mm, i as u32, j);
+        if q != 0 && (vp - 1) / 10 <= vm / 10 {
+            j = q as i32 - 1 - (pow5bits(i + 1) - FLOAT_POW5_BITCOUNT);
+            last_removed_digit = (mul_pow5_div_pow2(mv, (i + 1) as u32, j) % 10) as u8;
+        }
+        if q <= 1 {
+            // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
+            // mv = 4 * m2, so it always has at least two trailing 0 bits.
+            vr_is_trailing_zeros = true;
+            if accept_bounds {
+                // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
+                vm_is_trailing_zeros = mm_shift == 1;
+            } else {
+                // mp = mv + 2, so it always has at least one trailing 0 bit.
+                vp -= 1;
+            }
+        } else if q < 31 {
+            // TODO(ulfjack): Use a tighter bound here.
+            vr_is_trailing_zeros = multiple_of_power_of_2_32(mv, q - 1);
+        }
+    }
+
+    // Step 4: Find the shortest decimal representation in the interval of valid representations.
+    let mut removed = 0i32;
+    let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
+        // General case, which happens rarely (~4.0%).
+        while vp / 10 > vm / 10 {
+            vm_is_trailing_zeros &= vm - (vm / 10) * 10 == 0;
+            vr_is_trailing_zeros &= last_removed_digit == 0;
+            last_removed_digit = (vr % 10) as u8;
+            vr /= 10;
+            vp /= 10;
+            vm /= 10;
+            removed += 1;
+        }
+        if vm_is_trailing_zeros {
+            while vm % 10 == 0 {
+                vr_is_trailing_zeros &= last_removed_digit == 0;
+                last_removed_digit = (vr % 10) as u8;
+                vr /= 10;
+                vp /= 10;
+                vm /= 10;
+                removed += 1;
+            }
+        }
+        if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
+            // Round even if the exact number is .....50..0.
+            last_removed_digit = 4;
+        }
+        // We need to take vr + 1 if vr is outside bounds or we need to round up.
+        vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
+            as u32
+    } else {
+        // Specialized for the common case (~96.0%). Percentages below are relative to this.
+        // Loop iterations below (approximately):
+        // 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01%
+        while vp / 10 > vm / 10 {
+            last_removed_digit = (vr % 10) as u8;
+            vr /= 10;
+            vp /= 10;
+            vm /= 10;
+            removed += 1;
+        }
+        // We need to take vr + 1 if vr is outside bounds or we need to round up.
+        vr + (vr == vm || last_removed_digit >= 5) as u32
+    };
+    let exp = e10 + removed;
+
+    FloatingDecimal32 {
+        exponent: exp,
+        mantissa: output,
+    }
+}
diff --git a/src/f2s_intrinsics.rs b/src/f2s_intrinsics.rs
new file mode 100644
index 0000000..1a35218
--- /dev/null
+++ b/src/f2s_intrinsics.rs
@@ -0,0 +1,113 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+use crate::d2s;
+
+pub const FLOAT_POW5_INV_BITCOUNT: i32 = d2s::DOUBLE_POW5_INV_BITCOUNT - 64;
+pub const FLOAT_POW5_BITCOUNT: i32 = d2s::DOUBLE_POW5_BITCOUNT - 64;
+
+#[cfg_attr(feature = "no-panic", inline)]
+fn pow5factor_32(mut value: u32) -> u32 {
+    let mut count = 0u32;
+    loop {
+        debug_assert!(value != 0);
+        let q = value / 5;
+        let r = value % 5;
+        if r != 0 {
+            break;
+        }
+        value = q;
+        count += 1;
+    }
+    count
+}
+
+// Returns true if value is divisible by 5^p.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn multiple_of_power_of_5_32(value: u32, p: u32) -> bool {
+    pow5factor_32(value) >= p
+}
+
+// Returns true if value is divisible by 2^p.
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn multiple_of_power_of_2_32(value: u32, p: u32) -> bool {
+    // __builtin_ctz doesn't appear to be faster here.
+    (value & ((1u32 << p) - 1)) == 0
+}
+
+// It seems to be slightly faster to avoid uint128_t here, although the
+// generated code for uint128_t looks slightly nicer.
+#[cfg_attr(feature = "no-panic", inline)]
+fn mul_shift_32(m: u32, factor: u64, shift: i32) -> u32 {
+    debug_assert!(shift > 32);
+
+    // The casts here help MSVC to avoid calls to the __allmul library
+    // function.
+    let factor_lo = factor as u32;
+    let factor_hi = (factor >> 32) as u32;
+    let bits0 = m as u64 * factor_lo as u64;
+    let bits1 = m as u64 * factor_hi as u64;
+
+    let sum = (bits0 >> 32) + bits1;
+    let shifted_sum = sum >> (shift - 32);
+    debug_assert!(shifted_sum <= u32::max_value() as u64);
+    shifted_sum as u32
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn mul_pow5_inv_div_pow2(m: u32, q: u32, j: i32) -> u32 {
+    #[cfg(feature = "small")]
+    {
+        // The inverse multipliers are defined as [2^x / 5^y] + 1; the upper 64
+        // bits from the double lookup table are the correct bits for [2^x /
+        // 5^y], so we have to add 1 here. Note that we rely on the fact that
+        // the added 1 that's already stored in the table never overflows into
+        // the upper 64 bits.
+        let pow5 = unsafe { d2s::compute_inv_pow5(q) };
+        mul_shift_32(m, pow5.1 + 1, j)
+    }
+
+    #[cfg(not(feature = "small"))]
+    {
+        debug_assert!(q < d2s::DOUBLE_POW5_INV_SPLIT.len() as u32);
+        unsafe {
+            mul_shift_32(
+                m,
+                d2s::DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize).1 + 1,
+                j,
+            )
+        }
+    }
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub fn mul_pow5_div_pow2(m: u32, i: u32, j: i32) -> u32 {
+    #[cfg(feature = "small")]
+    {
+        let pow5 = unsafe { d2s::compute_pow5(i) };
+        mul_shift_32(m, pow5.1, j)
+    }
+
+    #[cfg(not(feature = "small"))]
+    {
+        debug_assert!(i < d2s::DOUBLE_POW5_SPLIT.len() as u32);
+        unsafe { mul_shift_32(m, d2s::DOUBLE_POW5_SPLIT.get_unchecked(i as usize).1, j) }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..db6ee16
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,117 @@
+//! [![github]](https://github.com/dtolnay/ryu)&ensp;[![crates-io]](https://crates.io/crates/ryu)&ensp;[![docs-rs]](https://docs.rs/ryu)
+//!
+//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
+//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
+//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
+//!
+//! <br>
+//!
+//! Pure Rust implementation of Ryū, an algorithm to quickly convert floating
+//! point numbers to decimal strings.
+//!
+//! The PLDI'18 paper [*Ryū: fast float-to-string conversion*][paper] by Ulf
+//! Adams includes a complete correctness proof of the algorithm. The paper is
+//! available under the creative commons CC-BY-SA license.
+//!
+//! This Rust implementation is a line-by-line port of Ulf Adams' implementation
+//! in C, [https://github.com/ulfjack/ryu][upstream].
+//!
+//! [paper]: https://dl.acm.org/citation.cfm?id=3192369
+//! [upstream]: https://github.com/ulfjack/ryu
+//!
+//! # Example
+//!
+//! ```
+//! fn main() {
+//!     let mut buffer = ryu::Buffer::new();
+//!     let printed = buffer.format(1.234);
+//!     assert_eq!(printed, "1.234");
+//! }
+//! ```
+//!
+//! ## Performance
+//!
+//! You can run upstream's benchmarks with:
+//!
+//! ```console
+//! $ git clone https://github.com/ulfjack/ryu c-ryu
+//! $ cd c-ryu
+//! $ bazel run -c opt //ryu/benchmark
+//! ```
+//!
+//! And the same benchmark against our implementation with:
+//!
+//! ```console
+//! $ git clone https://github.com/dtolnay/ryu rust-ryu
+//! $ cd rust-ryu
+//! $ cargo run --example upstream_benchmark --release
+//! ```
+//!
+//! These benchmarks measure the average time to print a 32-bit float and average
+//! time to print a 64-bit float, where the inputs are distributed as uniform random
+//! bit patterns 32 and 64 bits wide.
+//!
+//! The upstream C code, the unsafe direct Rust port, and the safe pretty Rust API
+//! all perform the same, taking around 21 nanoseconds to format a 32-bit float and
+//! 31 nanoseconds to format a 64-bit float.
+//!
+//! There is also a Rust-specific benchmark comparing this implementation to the
+//! standard library which you can run with:
+//!
+//! ```console
+//! $ cargo bench
+//! ```
+//!
+//! The benchmark shows Ryū approximately 4-10x faster than the standard library
+//! across a range of f32 and f64 inputs. Measurements are in nanoseconds per
+//! iteration; smaller is better.
+//!
+//! | type=f32 | 0.0  | 0.1234 | 2.718281828459045 | f32::MAX |
+//! |:--------:|:----:|:------:|:-----------------:|:--------:|
+//! | RYU      | 3ns  | 28ns   | 23ns              | 22ns     |
+//! | STD      | 40ns | 106ns  | 128ns             | 110ns    |
+//!
+//! | type=f64 | 0.0  | 0.1234 | 2.718281828459045 | f64::MAX |
+//! |:--------:|:----:|:------:|:-----------------:|:--------:|
+//! | RYU      | 3ns  | 50ns   | 35ns              | 32ns     |
+//! | STD      | 39ns | 105ns  | 128ns             | 202ns    |
+//!
+//! ## Formatting
+//!
+//! This library tends to produce more human-readable output than the standard
+//! library's to\_string, which never uses scientific notation. Here are two
+//! examples:
+//!
+//! - *ryu:* 1.23e40, *std:* 12300000000000000000000000000000000000000
+//! - *ryu:* 1.23e-40, *std:* 0.000000000000000000000000000000000000000123
+//!
+//! Both libraries print short decimals such as 0.0000123 without scientific
+//! notation.
+
+#![no_std]
+#![doc(html_root_url = "https://docs.rs/ryu/1.0.5")]
+#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
+#![cfg_attr(
+    feature = "cargo-clippy",
+    allow(cast_lossless, many_single_char_names, unreadable_literal,)
+)]
+
+mod buffer;
+mod common;
+mod d2s;
+#[cfg(not(feature = "small"))]
+mod d2s_full_table;
+mod d2s_intrinsics;
+#[cfg(feature = "small")]
+mod d2s_small_table;
+mod digit_table;
+mod f2s;
+mod f2s_intrinsics;
+mod pretty;
+
+pub use crate::buffer::{Buffer, Float};
+
+/// Unsafe functions that mirror the API of the C implementation of Ryū.
+pub mod raw {
+    pub use crate::pretty::{format32, format64};
+}
diff --git a/src/parse.rs b/src/parse.rs
new file mode 100644
index 0000000..00f7983
--- /dev/null
+++ b/src/parse.rs
@@ -0,0 +1,19 @@
+use core::fmt::{self, Display};
+
+#[derive(Copy, Clone, Debug)]
+pub enum Error {
+    InputTooShort,
+    InputTooLong,
+    MalformedInput,
+}
+
+impl Display for Error {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let msg = match self {
+            Error::InputTooShort => "input too short",
+            Error::InputTooLong => "input too long",
+            Error::MalformedInput => "malformed input",
+        };
+        formatter.write_str(msg)
+    }
+}
diff --git a/src/pretty/exponent.rs b/src/pretty/exponent.rs
new file mode 100644
index 0000000..84053d5
--- /dev/null
+++ b/src/pretty/exponent.rs
@@ -0,0 +1,48 @@
+use crate::digit_table::*;
+use core::ptr;
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn write_exponent3(mut k: isize, mut result: *mut u8) -> usize {
+    let sign = k < 0;
+    if sign {
+        *result = b'-';
+        result = result.offset(1);
+        k = -k;
+    }
+
+    debug_assert!(k < 1000);
+    if k >= 100 {
+        *result = b'0' + (k / 100) as u8;
+        k %= 100;
+        let d = DIGIT_TABLE.get_unchecked(k as usize * 2);
+        ptr::copy_nonoverlapping(d, result.offset(1), 2);
+        sign as usize + 3
+    } else if k >= 10 {
+        let d = DIGIT_TABLE.get_unchecked(k as usize * 2);
+        ptr::copy_nonoverlapping(d, result, 2);
+        sign as usize + 2
+    } else {
+        *result = b'0' + k as u8;
+        sign as usize + 1
+    }
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn write_exponent2(mut k: isize, mut result: *mut u8) -> usize {
+    let sign = k < 0;
+    if sign {
+        *result = b'-';
+        result = result.offset(1);
+        k = -k;
+    }
+
+    debug_assert!(k < 100);
+    if k >= 10 {
+        let d = DIGIT_TABLE.get_unchecked(k as usize * 2);
+        ptr::copy_nonoverlapping(d, result, 2);
+        sign as usize + 2
+    } else {
+        *result = b'0' + k as u8;
+        sign as usize + 1
+    }
+}
diff --git a/src/pretty/mantissa.rs b/src/pretty/mantissa.rs
new file mode 100644
index 0000000..e5fc202
--- /dev/null
+++ b/src/pretty/mantissa.rs
@@ -0,0 +1,50 @@
+use crate::digit_table::*;
+use core::ptr;
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn write_mantissa_long(mut output: u64, mut result: *mut u8) {
+    if (output >> 32) != 0 {
+        // One expensive 64-bit division.
+        let mut output2 = (output - 100_000_000 * (output / 100_000_000)) as u32;
+        output /= 100_000_000;
+
+        let c = output2 % 10_000;
+        output2 /= 10_000;
+        let d = output2 % 10_000;
+        let c0 = (c % 100) << 1;
+        let c1 = (c / 100) << 1;
+        let d0 = (d % 100) << 1;
+        let d1 = (d / 100) << 1;
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c0 as usize), result.offset(-2), 2);
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c1 as usize), result.offset(-4), 2);
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(d0 as usize), result.offset(-6), 2);
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(d1 as usize), result.offset(-8), 2);
+        result = result.offset(-8);
+    }
+    write_mantissa(output as u32, result);
+}
+
+#[cfg_attr(feature = "no-panic", inline)]
+pub unsafe fn write_mantissa(mut output: u32, mut result: *mut u8) {
+    while output >= 10_000 {
+        let c = (output - 10_000 * (output / 10_000)) as u32;
+        output /= 10_000;
+        let c0 = (c % 100) << 1;
+        let c1 = (c / 100) << 1;
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c0 as usize), result.offset(-2), 2);
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c1 as usize), result.offset(-4), 2);
+        result = result.offset(-4);
+    }
+    if output >= 100 {
+        let c = ((output % 100) << 1) as u32;
+        output /= 100;
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c as usize), result.offset(-2), 2);
+        result = result.offset(-2);
+    }
+    if output >= 10 {
+        let c = (output << 1) as u32;
+        ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c as usize), result.offset(-2), 2);
+    } else {
+        *result.offset(-1) = b'0' + output as u8;
+    }
+}
diff --git a/src/pretty/mod.rs b/src/pretty/mod.rs
new file mode 100644
index 0000000..a82692d
--- /dev/null
+++ b/src/pretty/mod.rs
@@ -0,0 +1,225 @@
+mod exponent;
+mod mantissa;
+
+use self::exponent::*;
+use self::mantissa::*;
+use crate::common;
+use crate::d2s::{self, *};
+use crate::f2s::*;
+use core::{mem, ptr};
+#[cfg(feature = "no-panic")]
+use no_panic::no_panic;
+
+/// Print f64 to the given buffer and return number of bytes written.
+///
+/// At most 24 bytes will be written.
+///
+/// ## Special cases
+///
+/// This function **does not** check for NaN or infinity. If the input
+/// number is not a finite float, the printed representation will be some
+/// correctly formatted but unspecified numerical value.
+///
+/// Please check [`is_finite`] yourself before calling this function, or
+/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
+///
+/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
+/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
+/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
+///
+/// ## Safety
+///
+/// The `result` pointer argument must point to sufficiently many writable bytes
+/// to hold Ryū's representation of `f`.
+///
+/// ## Example
+///
+/// ```
+/// use std::{mem::MaybeUninit, slice, str};
+///
+/// let f = 1.234f64;
+///
+/// unsafe {
+///     let mut buffer = [MaybeUninit::<u8>::uninit(); 24];
+///     let len = ryu::raw::format64(f, buffer.as_mut_ptr() as *mut u8);
+///     let slice = slice::from_raw_parts(buffer.as_ptr() as *const u8, len);
+///     let print = str::from_utf8_unchecked(slice);
+///     assert_eq!(print, "1.234");
+/// }
+/// ```
+#[must_use]
+#[cfg_attr(feature = "no-panic", no_panic)]
+pub unsafe fn format64(f: f64, result: *mut u8) -> usize {
+    let bits = mem::transmute::<f64, u64>(f);
+    let sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0;
+    let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1);
+    let ieee_exponent =
+        (bits >> DOUBLE_MANTISSA_BITS) as u32 & ((1u32 << DOUBLE_EXPONENT_BITS) - 1);
+
+    let mut index = 0isize;
+    if sign {
+        *result = b'-';
+        index += 1;
+    }
+
+    if ieee_exponent == 0 && ieee_mantissa == 0 {
+        ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
+        return sign as usize + 3;
+    }
+
+    let v = d2d(ieee_mantissa, ieee_exponent);
+
+    let length = d2s::decimal_length17(v.mantissa) as isize;
+    let k = v.exponent as isize;
+    let kk = length + k; // 10^(kk-1) <= v < 10^kk
+    debug_assert!(k >= -324);
+
+    if 0 <= k && kk <= 16 {
+        // 1234e7 -> 12340000000.0
+        write_mantissa_long(v.mantissa, result.offset(index + length));
+        for i in length..kk {
+            *result.offset(index + i) = b'0';
+        }
+        *result.offset(index + kk) = b'.';
+        *result.offset(index + kk + 1) = b'0';
+        index as usize + kk as usize + 2
+    } else if 0 < kk && kk <= 16 {
+        // 1234e-2 -> 12.34
+        write_mantissa_long(v.mantissa, result.offset(index + length + 1));
+        ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
+        *result.offset(index + kk) = b'.';
+        index as usize + length as usize + 1
+    } else if -5 < kk && kk <= 0 {
+        // 1234e-6 -> 0.001234
+        *result.offset(index) = b'0';
+        *result.offset(index + 1) = b'.';
+        let offset = 2 - kk;
+        for i in 2..offset {
+            *result.offset(index + i) = b'0';
+        }
+        write_mantissa_long(v.mantissa, result.offset(index + length + offset));
+        index as usize + length as usize + offset as usize
+    } else if length == 1 {
+        // 1e30
+        *result.offset(index) = b'0' + v.mantissa as u8;
+        *result.offset(index + 1) = b'e';
+        index as usize + 2 + write_exponent3(kk - 1, result.offset(index + 2))
+    } else {
+        // 1234e30 -> 1.234e33
+        write_mantissa_long(v.mantissa, result.offset(index + length + 1));
+        *result.offset(index) = *result.offset(index + 1);
+        *result.offset(index + 1) = b'.';
+        *result.offset(index + length + 1) = b'e';
+        index as usize
+            + length as usize
+            + 2
+            + write_exponent3(kk - 1, result.offset(index + length + 2))
+    }
+}
+
+/// Print f32 to the given buffer and return number of bytes written.
+///
+/// At most 16 bytes will be written.
+///
+/// ## Special cases
+///
+/// This function **does not** check for NaN or infinity. If the input
+/// number is not a finite float, the printed representation will be some
+/// correctly formatted but unspecified numerical value.
+///
+/// Please check [`is_finite`] yourself before calling this function, or
+/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
+///
+/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_finite
+/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_nan
+/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_infinite
+///
+/// ## Safety
+///
+/// The `result` pointer argument must point to sufficiently many writable bytes
+/// to hold Ryū's representation of `f`.
+///
+/// ## Example
+///
+/// ```
+/// use std::{mem::MaybeUninit, slice, str};
+///
+/// let f = 1.234f32;
+///
+/// unsafe {
+///     let mut buffer = [MaybeUninit::<u8>::uninit(); 16];
+///     let len = ryu::raw::format32(f, buffer.as_mut_ptr() as *mut u8);
+///     let slice = slice::from_raw_parts(buffer.as_ptr() as *const u8, len);
+///     let print = str::from_utf8_unchecked(slice);
+///     assert_eq!(print, "1.234");
+/// }
+/// ```
+#[must_use]
+#[cfg_attr(feature = "no-panic", no_panic)]
+pub unsafe fn format32(f: f32, result: *mut u8) -> usize {
+    let bits = mem::transmute::<f32, u32>(f);
+    let sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0;
+    let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1);
+    let ieee_exponent =
+        ((bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1)) as u32;
+
+    let mut index = 0isize;
+    if sign {
+        *result = b'-';
+        index += 1;
+    }
+
+    if ieee_exponent == 0 && ieee_mantissa == 0 {
+        ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
+        return sign as usize + 3;
+    }
+
+    let v = f2d(ieee_mantissa, ieee_exponent);
+
+    let length = common::decimal_length9(v.mantissa) as isize;
+    let k = v.exponent as isize;
+    let kk = length + k; // 10^(kk-1) <= v < 10^kk
+    debug_assert!(k >= -45);
+
+    if 0 <= k && kk <= 13 {
+        // 1234e7 -> 12340000000.0
+        write_mantissa(v.mantissa, result.offset(index + length));
+        for i in length..kk {
+            *result.offset(index + i) = b'0';
+        }
+        *result.offset(index + kk) = b'.';
+        *result.offset(index + kk + 1) = b'0';
+        index as usize + kk as usize + 2
+    } else if 0 < kk && kk <= 13 {
+        // 1234e-2 -> 12.34
+        write_mantissa(v.mantissa, result.offset(index + length + 1));
+        ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
+        *result.offset(index + kk) = b'.';
+        index as usize + length as usize + 1
+    } else if -6 < kk && kk <= 0 {
+        // 1234e-6 -> 0.001234
+        *result.offset(index) = b'0';
+        *result.offset(index + 1) = b'.';
+        let offset = 2 - kk;
+        for i in 2..offset {
+            *result.offset(index + i) = b'0';
+        }
+        write_mantissa(v.mantissa, result.offset(index + length + offset));
+        index as usize + length as usize + offset as usize
+    } else if length == 1 {
+        // 1e30
+        *result.offset(index) = b'0' + v.mantissa as u8;
+        *result.offset(index + 1) = b'e';
+        index as usize + 2 + write_exponent2(kk - 1, result.offset(index + 2))
+    } else {
+        // 1234e30 -> 1.234e33
+        write_mantissa(v.mantissa, result.offset(index + length + 1));
+        *result.offset(index) = *result.offset(index + 1);
+        *result.offset(index + 1) = b'.';
+        *result.offset(index + length + 1) = b'e';
+        index as usize
+            + length as usize
+            + 2
+            + write_exponent2(kk - 1, result.offset(index + length + 2))
+    }
+}
diff --git a/src/s2d.rs b/src/s2d.rs
new file mode 100644
index 0000000..3d3808d
--- /dev/null
+++ b/src/s2d.rs
@@ -0,0 +1,216 @@
+use crate::common::*;
+use crate::d2s;
+use crate::d2s_intrinsics::*;
+use crate::parse::Error;
+#[cfg(feature = "no-panic")]
+use no_panic::no_panic;
+
+const DOUBLE_EXPONENT_BIAS: usize = 1023;
+
+fn floor_log2(value: u64) -> u32 {
+    63_u32.wrapping_sub(value.leading_zeros())
+}
+
+#[cfg_attr(feature = "no-panic", no_panic)]
+pub fn s2d(buffer: &[u8]) -> Result<f64, Error> {
+    let len = buffer.len();
+    if len == 0 {
+        return Err(Error::InputTooShort);
+    }
+
+    let mut m10digits = 0;
+    let mut e10digits = 0;
+    let mut dot_index = len;
+    let mut e_index = len;
+    let mut m10 = 0u64;
+    let mut e10 = 0i32;
+    let mut signed_m = false;
+    let mut signed_e = false;
+
+    let mut i = 0;
+    if unsafe { *buffer.get_unchecked(0) } == b'-' {
+        signed_m = true;
+        i += 1;
+    }
+
+    while let Some(c) = buffer.get(i).copied() {
+        if c == b'.' {
+            if dot_index != len {
+                return Err(Error::MalformedInput);
+            }
+            dot_index = i;
+            i += 1;
+            continue;
+        }
+        if c < b'0' || c > b'9' {
+            break;
+        }
+        if m10digits >= 17 {
+            return Err(Error::InputTooLong);
+        }
+        m10 = 10 * m10 + (c - b'0') as u64;
+        if m10 != 0 {
+            m10digits += 1;
+        }
+        i += 1;
+    }
+
+    if let Some(b'e') | Some(b'E') = buffer.get(i) {
+        e_index = i;
+        i += 1;
+        match buffer.get(i) {
+            Some(b'-') => {
+                signed_e = true;
+                i += 1;
+            }
+            Some(b'+') => i += 1,
+            _ => {}
+        }
+        while let Some(c) = buffer.get(i).copied() {
+            if c < b'0' || c > b'9' {
+                return Err(Error::MalformedInput);
+            }
+            if e10digits > 3 {
+                // TODO: Be more lenient. Return +/-Infinity or +/-0 instead.
+                return Err(Error::InputTooLong);
+            }
+            e10 = 10 * e10 + (c - b'0') as i32;
+            if e10 != 0 {
+                e10digits += 1;
+            }
+            i += 1;
+        }
+    }
+
+    if i < len {
+        return Err(Error::MalformedInput);
+    }
+    if signed_e {
+        e10 = -e10;
+    }
+    e10 -= if dot_index < e_index {
+        (e_index - dot_index - 1) as i32
+    } else {
+        0
+    };
+    if m10 == 0 {
+        return Ok(if signed_m { -0.0 } else { 0.0 });
+    }
+
+    if m10digits + e10 <= -324 || m10 == 0 {
+        // Number is less than 1e-324, which should be rounded down to 0; return
+        // +/-0.0.
+        let ieee = (signed_m as u64) << (d2s::DOUBLE_EXPONENT_BITS + d2s::DOUBLE_MANTISSA_BITS);
+        return Ok(f64::from_bits(ieee));
+    }
+    if m10digits + e10 >= 310 {
+        // Number is larger than 1e+309, which should be rounded to +/-Infinity.
+        let ieee = ((signed_m as u64) << (d2s::DOUBLE_EXPONENT_BITS + d2s::DOUBLE_MANTISSA_BITS))
+            | (0x7ff_u64 << d2s::DOUBLE_MANTISSA_BITS);
+        return Ok(f64::from_bits(ieee));
+    }
+
+    // Convert to binary float m2 * 2^e2, while retaining information about
+    // whether the conversion was exact (trailing_zeros).
+    let e2: i32;
+    let m2: u64;
+    let mut trailing_zeros: bool;
+    if e10 >= 0 {
+        // The length of m * 10^e in bits is:
+        //   log2(m10 * 10^e10) = log2(m10) + e10 log2(10) = log2(m10) + e10 + e10 * log2(5)
+        //
+        // We want to compute the DOUBLE_MANTISSA_BITS + 1 top-most bits (+1 for
+        // the implicit leading one in IEEE format). We therefore choose a
+        // binary output exponent of
+        //   log2(m10 * 10^e10) - (DOUBLE_MANTISSA_BITS + 1).
+        //
+        // We use floor(log2(5^e10)) so that we get at least this many bits;
+        // better to have an additional bit than to not have enough bits.
+        e2 = floor_log2(m10)
+            .wrapping_add(e10 as u32)
+            .wrapping_add(log2_pow5(e10) as u32)
+            .wrapping_sub(d2s::DOUBLE_MANTISSA_BITS + 1) as i32;
+
+        // We now compute [m10 * 10^e10 / 2^e2] = [m10 * 5^e10 / 2^(e2-e10)].
+        // To that end, we use the DOUBLE_POW5_SPLIT table.
+        let j = e2
+            .wrapping_sub(e10)
+            .wrapping_sub(ceil_log2_pow5(e10))
+            .wrapping_add(d2s::DOUBLE_POW5_BITCOUNT);
+        debug_assert!(j >= 0);
+        debug_assert!(e10 < d2s::DOUBLE_POW5_SPLIT.len() as i32);
+        m2 = mul_shift_64(
+            m10,
+            unsafe { d2s::DOUBLE_POW5_SPLIT.get_unchecked(e10 as usize) },
+            j as u32,
+        );
+
+        // We also compute if the result is exact, i.e.,
+        //   [m10 * 10^e10 / 2^e2] == m10 * 10^e10 / 2^e2.
+        // This can only be the case if 2^e2 divides m10 * 10^e10, which in turn
+        // requires that the largest power of 2 that divides m10 + e10 is
+        // greater than e2. If e2 is less than e10, then the result must be
+        // exact. Otherwise we use the existing multiple_of_power_of_2 function.
+        trailing_zeros =
+            e2 < e10 || e2 - e10 < 64 && multiple_of_power_of_2(m10, (e2 - e10) as u32);
+    } else {
+        e2 = floor_log2(m10)
+            .wrapping_add(e10 as u32)
+            .wrapping_sub(ceil_log2_pow5(-e10) as u32)
+            .wrapping_sub(d2s::DOUBLE_MANTISSA_BITS + 1) as i32;
+        let j = e2
+            .wrapping_sub(e10)
+            .wrapping_add(ceil_log2_pow5(-e10))
+            .wrapping_sub(1)
+            .wrapping_add(d2s::DOUBLE_POW5_INV_BITCOUNT);
+        debug_assert!(-e10 < d2s::DOUBLE_POW5_INV_SPLIT.len() as i32);
+        m2 = mul_shift_64(
+            m10,
+            unsafe { d2s::DOUBLE_POW5_INV_SPLIT.get_unchecked(-e10 as usize) },
+            j as u32,
+        );
+        trailing_zeros = multiple_of_power_of_5(m10, -e10 as u32);
+    }
+
+    // Compute the final IEEE exponent.
+    let mut ieee_e2 = i32::max(0, e2 + DOUBLE_EXPONENT_BIAS as i32 + floor_log2(m2) as i32) as u32;
+
+    if ieee_e2 > 0x7fe {
+        // Final IEEE exponent is larger than the maximum representable; return +/-Infinity.
+        let ieee = ((signed_m as u64) << (d2s::DOUBLE_EXPONENT_BITS + d2s::DOUBLE_MANTISSA_BITS))
+            | (0x7ff_u64 << d2s::DOUBLE_MANTISSA_BITS);
+        return Ok(f64::from_bits(ieee));
+    }
+
+    // We need to figure out how much we need to shift m2. The tricky part is
+    // that we need to take the final IEEE exponent into account, so we need to
+    // reverse the bias and also special-case the value 0.
+    let shift = if ieee_e2 == 0 { 1 } else { ieee_e2 as i32 }
+        .wrapping_sub(e2)
+        .wrapping_sub(DOUBLE_EXPONENT_BIAS as i32)
+        .wrapping_sub(d2s::DOUBLE_MANTISSA_BITS as i32);
+    debug_assert!(shift >= 0);
+
+    // We need to round up if the exact value is more than 0.5 above the value
+    // we computed. That's equivalent to checking if the last removed bit was 1
+    // and either the value was not just trailing zeros or the result would
+    // otherwise be odd.
+    //
+    // We need to update trailing_zeros given that we have the exact output
+    // exponent ieee_e2 now.
+    trailing_zeros &= (m2 & ((1_u64 << (shift - 1)) - 1)) == 0;
+    let last_removed_bit = (m2 >> (shift - 1)) & 1;
+    let round_up = last_removed_bit != 0 && (!trailing_zeros || ((m2 >> shift) & 1) != 0);
+
+    let mut ieee_m2 = (m2 >> shift).wrapping_add(round_up as u64);
+    if ieee_m2 == (1_u64 << (d2s::DOUBLE_MANTISSA_BITS + 1)) {
+        // Due to how the IEEE represents +/-Infinity, we don't need to check
+        // for overflow here.
+        ieee_e2 += 1;
+    }
+    ieee_m2 &= (1_u64 << d2s::DOUBLE_MANTISSA_BITS) - 1;
+    let ieee = ((((signed_m as u64) << d2s::DOUBLE_EXPONENT_BITS) | ieee_e2 as u64)
+        << d2s::DOUBLE_MANTISSA_BITS)
+        | ieee_m2;
+    Ok(f64::from_bits(ieee))
+}
diff --git a/src/s2f.rs b/src/s2f.rs
new file mode 100644
index 0000000..d9e0744
--- /dev/null
+++ b/src/s2f.rs
@@ -0,0 +1,207 @@
+use crate::common::*;
+use crate::f2s;
+use crate::f2s_intrinsics::*;
+use crate::parse::Error;
+#[cfg(feature = "no-panic")]
+use no_panic::no_panic;
+
+const FLOAT_EXPONENT_BIAS: usize = 127;
+
+fn floor_log2(value: u32) -> u32 {
+    31_u32.wrapping_sub(value.leading_zeros())
+}
+
+#[cfg_attr(feature = "no-panic", no_panic)]
+pub fn s2f(buffer: &[u8]) -> Result<f32, Error> {
+    let len = buffer.len();
+    if len == 0 {
+        return Err(Error::InputTooShort);
+    }
+
+    let mut m10digits = 0;
+    let mut e10digits = 0;
+    let mut dot_index = len;
+    let mut e_index = len;
+    let mut m10 = 0u32;
+    let mut e10 = 0i32;
+    let mut signed_m = false;
+    let mut signed_e = false;
+
+    let mut i = 0;
+    if unsafe { *buffer.get_unchecked(0) } == b'-' {
+        signed_m = true;
+        i += 1;
+    }
+
+    while let Some(c) = buffer.get(i).copied() {
+        if c == b'.' {
+            if dot_index != len {
+                return Err(Error::MalformedInput);
+            }
+            dot_index = i;
+            i += 1;
+            continue;
+        }
+        if c < b'0' || c > b'9' {
+            break;
+        }
+        if m10digits >= 9 {
+            return Err(Error::InputTooLong);
+        }
+        m10 = 10 * m10 + (c - b'0') as u32;
+        if m10 != 0 {
+            m10digits += 1;
+        }
+        i += 1;
+    }
+
+    if let Some(b'e') | Some(b'E') = buffer.get(i) {
+        e_index = i;
+        i += 1;
+        match buffer.get(i) {
+            Some(b'-') => {
+                signed_e = true;
+                i += 1;
+            }
+            Some(b'+') => i += 1,
+            _ => {}
+        }
+        while let Some(c) = buffer.get(i).copied() {
+            if c < b'0' || c > b'9' {
+                return Err(Error::MalformedInput);
+            }
+            if e10digits > 3 {
+                // TODO: Be more lenient. Return +/-Infinity or +/-0 instead.
+                return Err(Error::InputTooLong);
+            }
+            e10 = 10 * e10 + (c - b'0') as i32;
+            if e10 != 0 {
+                e10digits += 1;
+            }
+            i += 1;
+        }
+    }
+
+    if i < len {
+        return Err(Error::MalformedInput);
+    }
+    if signed_e {
+        e10 = -e10;
+    }
+    e10 -= if dot_index < e_index {
+        (e_index - dot_index - 1) as i32
+    } else {
+        0
+    };
+    if m10 == 0 {
+        return Ok(if signed_m { -0.0 } else { 0.0 });
+    }
+
+    if m10digits + e10 <= -46 || m10 == 0 {
+        // Number is less than 1e-46, which should be rounded down to 0; return
+        // +/-0.0.
+        let ieee = (signed_m as u32) << (f2s::FLOAT_EXPONENT_BITS + f2s::FLOAT_MANTISSA_BITS);
+        return Ok(f32::from_bits(ieee));
+    }
+    if m10digits + e10 >= 40 {
+        // Number is larger than 1e+39, which should be rounded to +/-Infinity.
+        let ieee = ((signed_m as u32) << (f2s::FLOAT_EXPONENT_BITS + f2s::FLOAT_MANTISSA_BITS))
+            | (0xff_u32 << f2s::FLOAT_MANTISSA_BITS);
+        return Ok(f32::from_bits(ieee));
+    }
+
+    // Convert to binary float m2 * 2^e2, while retaining information about
+    // whether the conversion was exact (trailing_zeros).
+    let e2: i32;
+    let m2: u32;
+    let mut trailing_zeros: bool;
+    if e10 >= 0 {
+        // The length of m * 10^e in bits is:
+        //   log2(m10 * 10^e10) = log2(m10) + e10 log2(10) = log2(m10) + e10 + e10 * log2(5)
+        //
+        // We want to compute the FLOAT_MANTISSA_BITS + 1 top-most bits (+1 for
+        // the implicit leading one in IEEE format). We therefore choose a
+        // binary output exponent of
+        //   log2(m10 * 10^e10) - (FLOAT_MANTISSA_BITS + 1).
+        //
+        // We use floor(log2(5^e10)) so that we get at least this many bits; better to
+        // have an additional bit than to not have enough bits.
+        e2 = floor_log2(m10)
+            .wrapping_add(e10 as u32)
+            .wrapping_add(log2_pow5(e10) as u32)
+            .wrapping_sub(f2s::FLOAT_MANTISSA_BITS + 1) as i32;
+
+        // We now compute [m10 * 10^e10 / 2^e2] = [m10 * 5^e10 / 2^(e2-e10)].
+        // To that end, we use the FLOAT_POW5_SPLIT table.
+        let j = e2
+            .wrapping_sub(e10)
+            .wrapping_sub(ceil_log2_pow5(e10))
+            .wrapping_add(f2s::FLOAT_POW5_BITCOUNT);
+        debug_assert!(j >= 0);
+        m2 = mul_pow5_div_pow2(m10, e10 as u32, j);
+
+        // We also compute if the result is exact, i.e.,
+        //   [m10 * 10^e10 / 2^e2] == m10 * 10^e10 / 2^e2.
+        // This can only be the case if 2^e2 divides m10 * 10^e10, which in turn
+        // requires that the largest power of 2 that divides m10 + e10 is
+        // greater than e2. If e2 is less than e10, then the result must be
+        // exact. Otherwise we use the existing multiple_of_power_of_2 function.
+        trailing_zeros =
+            e2 < e10 || e2 - e10 < 32 && multiple_of_power_of_2_32(m10, (e2 - e10) as u32);
+    } else {
+        e2 = floor_log2(m10)
+            .wrapping_add(e10 as u32)
+            .wrapping_sub(ceil_log2_pow5(-e10) as u32)
+            .wrapping_sub(f2s::FLOAT_MANTISSA_BITS + 1) as i32;
+        let j = e2
+            .wrapping_sub(e10)
+            .wrapping_add(ceil_log2_pow5(-e10))
+            .wrapping_sub(1)
+            .wrapping_add(f2s::FLOAT_POW5_INV_BITCOUNT);
+        m2 = mul_pow5_inv_div_pow2(m10, -e10 as u32, j);
+        trailing_zeros = multiple_of_power_of_5_32(m10, -e10 as u32);
+    }
+
+    // Compute the final IEEE exponent.
+    let mut ieee_e2 = i32::max(0, e2 + FLOAT_EXPONENT_BIAS as i32 + floor_log2(m2) as i32) as u32;
+
+    if ieee_e2 > 0xfe {
+        // Final IEEE exponent is larger than the maximum representable; return
+        // +/-Infinity.
+        let ieee = ((signed_m as u32) << (f2s::FLOAT_EXPONENT_BITS + f2s::FLOAT_MANTISSA_BITS))
+            | (0xff_u32 << f2s::FLOAT_MANTISSA_BITS);
+        return Ok(f32::from_bits(ieee));
+    }
+
+    // We need to figure out how much we need to shift m2. The tricky part is
+    // that we need to take the final IEEE exponent into account, so we need to
+    // reverse the bias and also special-case the value 0.
+    let shift = if ieee_e2 == 0 { 1 } else { ieee_e2 as i32 }
+        .wrapping_sub(e2)
+        .wrapping_sub(FLOAT_EXPONENT_BIAS as i32)
+        .wrapping_sub(f2s::FLOAT_MANTISSA_BITS as i32);
+    debug_assert!(shift >= 0);
+
+    // We need to round up if the exact value is more than 0.5 above the value
+    // we computed. That's equivalent to checking if the last removed bit was 1
+    // and either the value was not just trailing zeros or the result would
+    // otherwise be odd.
+    //
+    // We need to update trailing_zeros given that we have the exact output
+    // exponent ieee_e2 now.
+    trailing_zeros &= (m2 & ((1_u32 << (shift - 1)) - 1)) == 0;
+    let last_removed_bit = (m2 >> (shift - 1)) & 1;
+    let round_up = last_removed_bit != 0 && (!trailing_zeros || ((m2 >> shift) & 1) != 0);
+
+    let mut ieee_m2 = (m2 >> shift).wrapping_add(round_up as u32);
+    if ieee_m2 == (1_u32 << (f2s::FLOAT_MANTISSA_BITS + 1)) {
+        // Due to how the IEEE represents +/-Infinity, we don't need to check
+        // for overflow here.
+        ieee_e2 += 1;
+    }
+    ieee_m2 &= (1_u32 << f2s::FLOAT_MANTISSA_BITS) - 1;
+    let ieee = ((((signed_m as u32) << f2s::FLOAT_EXPONENT_BITS) | ieee_e2 as u32)
+        << f2s::FLOAT_MANTISSA_BITS)
+        | ieee_m2;
+    Ok(f32::from_bits(ieee))
+}
diff --git a/tests/common_test.rs b/tests/common_test.rs
new file mode 100644
index 0000000..a6010e9
--- /dev/null
+++ b/tests/common_test.rs
@@ -0,0 +1,83 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+#![allow(dead_code)]
+
+#[path = "../src/common.rs"]
+mod common;
+
+use common::*;
+
+#[test]
+fn test_decimal_length9() {
+    assert_eq!(1, decimal_length9(0));
+    assert_eq!(1, decimal_length9(1));
+    assert_eq!(1, decimal_length9(9));
+    assert_eq!(2, decimal_length9(10));
+    assert_eq!(2, decimal_length9(99));
+    assert_eq!(3, decimal_length9(100));
+    assert_eq!(3, decimal_length9(999));
+    assert_eq!(9, decimal_length9(999999999));
+}
+
+#[test]
+fn test_ceil_log2_pow5() {
+    assert_eq!(1, ceil_log2_pow5(0));
+    assert_eq!(3, ceil_log2_pow5(1));
+    assert_eq!(5, ceil_log2_pow5(2));
+    assert_eq!(7, ceil_log2_pow5(3));
+    assert_eq!(10, ceil_log2_pow5(4));
+    assert_eq!(8192, ceil_log2_pow5(3528));
+}
+
+#[test]
+fn test_log10_pow2() {
+    assert_eq!(0, log10_pow2(0));
+    assert_eq!(0, log10_pow2(1));
+    assert_eq!(0, log10_pow2(2));
+    assert_eq!(0, log10_pow2(3));
+    assert_eq!(1, log10_pow2(4));
+    assert_eq!(496, log10_pow2(1650));
+}
+
+#[test]
+fn test_log10_pow5() {
+    assert_eq!(0, log10_pow5(0));
+    assert_eq!(0, log10_pow5(1));
+    assert_eq!(1, log10_pow5(2));
+    assert_eq!(2, log10_pow5(3));
+    assert_eq!(2, log10_pow5(4));
+    assert_eq!(1831, log10_pow5(2620));
+}
+
+#[test]
+fn test_float_to_bits() {
+    assert_eq!(0, 0.0_f32.to_bits());
+    assert_eq!(0x40490fda, 3.1415926_f32.to_bits());
+}
+
+#[test]
+fn test_double_to_bits() {
+    assert_eq!(0, 0.0_f64.to_bits());
+    assert_eq!(
+        0x400921FB54442D18,
+        3.1415926535897932384626433_f64.to_bits(),
+    );
+}
diff --git a/tests/d2s_table_test.rs b/tests/d2s_table_test.rs
new file mode 100644
index 0000000..aa45c55
--- /dev/null
+++ b/tests/d2s_table_test.rs
@@ -0,0 +1,50 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+#![allow(dead_code)]
+
+#[path = "../src/common.rs"]
+mod common;
+
+#[path = "../src/d2s_full_table.rs"]
+mod d2s_full_table;
+
+#[path = "../src/d2s_intrinsics.rs"]
+mod d2s_intrinsics;
+
+#[path = "../src/d2s_small_table.rs"]
+mod d2s_small_table;
+
+use d2s_full_table::*;
+use d2s_small_table::*;
+
+#[test]
+fn test_compute_pow5() {
+    for (i, entry) in DOUBLE_POW5_SPLIT.iter().enumerate() {
+        assert_eq!(*entry, unsafe { compute_pow5(i as u32) }, "entry {}", i);
+    }
+}
+
+#[test]
+fn test_compute_inv_pow5() {
+    for (i, entry) in DOUBLE_POW5_INV_SPLIT[..292].iter().enumerate() {
+        assert_eq!(*entry, unsafe { compute_inv_pow5(i as u32) }, "entry {}", i);
+    }
+}
diff --git a/tests/d2s_test.rs b/tests/d2s_test.rs
new file mode 100644
index 0000000..604ab69
--- /dev/null
+++ b/tests/d2s_test.rs
@@ -0,0 +1,320 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+#[macro_use]
+mod macros;
+
+use std::f64;
+
+fn pretty(f: f64) -> String {
+    ryu::Buffer::new().format(f).to_owned()
+}
+
+fn ieee_parts_to_double(sign: bool, ieee_exponent: u32, ieee_mantissa: u64) -> f64 {
+    assert!(ieee_exponent <= 2047);
+    assert!(ieee_mantissa <= (1u64 << 53) - 1);
+    f64::from_bits(((sign as u64) << 63) | ((ieee_exponent as u64) << 52) | ieee_mantissa)
+}
+
+#[test]
+fn test_ryu() {
+    check!(0.3);
+    check!(1234000000000000.0);
+    check!(1.234e16);
+    check!(2.71828);
+    check!(1.1e128);
+    check!(1.1e-64);
+    check!(2.718281828459045);
+    check!(5e-324);
+    check!(1.7976931348623157e308);
+}
+
+#[test]
+fn test_random() {
+    let n = if cfg!(miri) { 100 } else { 1000000 };
+    let mut buffer = ryu::Buffer::new();
+    for _ in 0..n {
+        let f: f64 = rand::random();
+        assert_eq!(f, buffer.format_finite(f).parse().unwrap());
+    }
+}
+
+#[test]
+#[cfg_attr(miri, ignore)]
+fn test_non_finite() {
+    for i in 0u64..1 << 23 {
+        let f = f64::from_bits((((1 << 11) - 1) << 52) + (i << 29));
+        assert!(!f.is_finite(), "f={}", f);
+        ryu::Buffer::new().format_finite(f);
+    }
+}
+
+#[test]
+fn test_basic() {
+    check!(0.0);
+    check!(-0.0);
+    check!(1.0);
+    check!(-1.0);
+    assert_eq!(pretty(f64::NAN), "NaN");
+    assert_eq!(pretty(f64::INFINITY), "inf");
+    assert_eq!(pretty(f64::NEG_INFINITY), "-inf");
+}
+
+#[test]
+fn test_switch_to_subnormal() {
+    check!(2.2250738585072014e-308);
+}
+
+#[test]
+fn test_min_and_max() {
+    assert_eq!(f64::from_bits(0x7fefffffffffffff), 1.7976931348623157e308);
+    check!(1.7976931348623157e308);
+    assert_eq!(f64::from_bits(1), 5e-324);
+    check!(5e-324);
+}
+
+#[test]
+fn test_lots_of_trailing_zeros() {
+    check!(2.9802322387695312e-8);
+}
+
+#[test]
+fn test_regression() {
+    check!(-2.109808898695963e16);
+    check!(4.940656e-318);
+    check!(1.18575755e-316);
+    check!(2.989102097996e-312);
+    check!(9060801153433600.0);
+    check!(4.708356024711512e18);
+    check!(9.409340012568248e18);
+    check!(1.2345678);
+}
+
+#[test]
+fn test_looks_like_pow5() {
+    // These numbers have a mantissa that is a multiple of the largest power of
+    // 5 that fits, and an exponent that causes the computation for q to result
+    // in 22, which is a corner case for Ryū.
+    assert_eq!(f64::from_bits(0x4830F0CF064DD592), 5.764607523034235e39);
+    check!(5.764607523034235e39);
+    assert_eq!(f64::from_bits(0x4840F0CF064DD592), 1.152921504606847e40);
+    check!(1.152921504606847e40);
+    assert_eq!(f64::from_bits(0x4850F0CF064DD592), 2.305843009213694e40);
+    check!(2.305843009213694e40);
+}
+
+#[test]
+fn test_output_length() {
+    check!(1.0); // already tested in Basic
+    check!(1.2);
+    check!(1.23);
+    check!(1.234);
+    check!(1.2345);
+    check!(1.23456);
+    check!(1.234567);
+    check!(1.2345678); // already tested in Regression
+    check!(1.23456789);
+    check!(1.234567895); // 1.234567890 would be trimmed
+    check!(1.2345678901);
+    check!(1.23456789012);
+    check!(1.234567890123);
+    check!(1.2345678901234);
+    check!(1.23456789012345);
+    check!(1.234567890123456);
+    check!(1.2345678901234567);
+
+    // Test 32-bit chunking
+    check!(4.294967294); // 2^32 - 2
+    check!(4.294967295); // 2^32 - 1
+    check!(4.294967296); // 2^32
+    check!(4.294967297); // 2^32 + 1
+    check!(4.294967298); // 2^32 + 2
+}
+
+// Test min, max shift values in shiftright128
+#[test]
+fn test_min_max_shift() {
+    let max_mantissa = (1u64 << 53) - 1;
+
+    // 32-bit opt-size=0:  49 <= dist <= 50
+    // 32-bit opt-size=1:  30 <= dist <= 50
+    // 64-bit opt-size=0:  50 <= dist <= 50
+    // 64-bit opt-size=1:  30 <= dist <= 50
+    assert_eq!(1.7800590868057611E-307, ieee_parts_to_double(false, 4, 0));
+    check!(1.7800590868057611e-307);
+    // 32-bit opt-size=0:  49 <= dist <= 49
+    // 32-bit opt-size=1:  28 <= dist <= 49
+    // 64-bit opt-size=0:  50 <= dist <= 50
+    // 64-bit opt-size=1:  28 <= dist <= 50
+    assert_eq!(
+        2.8480945388892175E-306,
+        ieee_parts_to_double(false, 6, max_mantissa)
+    );
+    check!(2.8480945388892175e-306);
+    // 32-bit opt-size=0:  52 <= dist <= 53
+    // 32-bit opt-size=1:   2 <= dist <= 53
+    // 64-bit opt-size=0:  53 <= dist <= 53
+    // 64-bit opt-size=1:   2 <= dist <= 53
+    assert_eq!(2.446494580089078E-296, ieee_parts_to_double(false, 41, 0));
+    check!(2.446494580089078e-296);
+    // 32-bit opt-size=0:  52 <= dist <= 52
+    // 32-bit opt-size=1:   2 <= dist <= 52
+    // 64-bit opt-size=0:  53 <= dist <= 53
+    // 64-bit opt-size=1:   2 <= dist <= 53
+    assert_eq!(
+        4.8929891601781557E-296,
+        ieee_parts_to_double(false, 40, max_mantissa)
+    );
+    check!(4.8929891601781557e-296);
+
+    // 32-bit opt-size=0:  57 <= dist <= 58
+    // 32-bit opt-size=1:  57 <= dist <= 58
+    // 64-bit opt-size=0:  58 <= dist <= 58
+    // 64-bit opt-size=1:  58 <= dist <= 58
+    assert_eq!(1.8014398509481984E16, ieee_parts_to_double(false, 1077, 0));
+    check!(1.8014398509481984e16);
+    // 32-bit opt-size=0:  57 <= dist <= 57
+    // 32-bit opt-size=1:  57 <= dist <= 57
+    // 64-bit opt-size=0:  58 <= dist <= 58
+    // 64-bit opt-size=1:  58 <= dist <= 58
+    assert_eq!(
+        3.6028797018963964E16,
+        ieee_parts_to_double(false, 1076, max_mantissa)
+    );
+    check!(3.6028797018963964e16);
+    // 32-bit opt-size=0:  51 <= dist <= 52
+    // 32-bit opt-size=1:  51 <= dist <= 59
+    // 64-bit opt-size=0:  52 <= dist <= 52
+    // 64-bit opt-size=1:  52 <= dist <= 59
+    assert_eq!(2.900835519859558E-216, ieee_parts_to_double(false, 307, 0));
+    check!(2.900835519859558e-216);
+    // 32-bit opt-size=0:  51 <= dist <= 51
+    // 32-bit opt-size=1:  51 <= dist <= 59
+    // 64-bit opt-size=0:  52 <= dist <= 52
+    // 64-bit opt-size=1:  52 <= dist <= 59
+    assert_eq!(
+        5.801671039719115E-216,
+        ieee_parts_to_double(false, 306, max_mantissa)
+    );
+    check!(5.801671039719115e-216);
+
+    // https://github.com/ulfjack/ryu/commit/19e44d16d80236f5de25800f56d82606d1be00b9#commitcomment-30146483
+    // 32-bit opt-size=0:  49 <= dist <= 49
+    // 32-bit opt-size=1:  44 <= dist <= 49
+    // 64-bit opt-size=0:  50 <= dist <= 50
+    // 64-bit opt-size=1:  44 <= dist <= 50
+    assert_eq!(
+        3.196104012172126E-27,
+        ieee_parts_to_double(false, 934, 0x000FA7161A4D6E0C)
+    );
+    check!(3.196104012172126e-27);
+}
+
+#[test]
+fn test_small_integers() {
+    check!(9007199254740991.0); // 2^53-1
+    check!(9007199254740992.0); // 2^53
+
+    check!(1.0);
+    check!(12.0);
+    check!(123.0);
+    check!(1234.0);
+    check!(12345.0);
+    check!(123456.0);
+    check!(1234567.0);
+    check!(12345678.0);
+    check!(123456789.0);
+    check!(1234567890.0);
+    check!(1234567895.0);
+    check!(12345678901.0);
+    check!(123456789012.0);
+    check!(1234567890123.0);
+    check!(12345678901234.0);
+    check!(123456789012345.0);
+    check!(1234567890123456.0);
+
+    // 10^i
+    check!(1.0);
+    check!(10.0);
+    check!(100.0);
+    check!(1000.0);
+    check!(10000.0);
+    check!(100000.0);
+    check!(1000000.0);
+    check!(10000000.0);
+    check!(100000000.0);
+    check!(1000000000.0);
+    check!(10000000000.0);
+    check!(100000000000.0);
+    check!(1000000000000.0);
+    check!(10000000000000.0);
+    check!(100000000000000.0);
+    check!(1000000000000000.0);
+
+    // 10^15 + 10^i
+    check!(1000000000000001.0);
+    check!(1000000000000010.0);
+    check!(1000000000000100.0);
+    check!(1000000000001000.0);
+    check!(1000000000010000.0);
+    check!(1000000000100000.0);
+    check!(1000000001000000.0);
+    check!(1000000010000000.0);
+    check!(1000000100000000.0);
+    check!(1000001000000000.0);
+    check!(1000010000000000.0);
+    check!(1000100000000000.0);
+    check!(1001000000000000.0);
+    check!(1010000000000000.0);
+    check!(1100000000000000.0);
+
+    // Largest power of 2 <= 10^(i+1)
+    check!(8.0);
+    check!(64.0);
+    check!(512.0);
+    check!(8192.0);
+    check!(65536.0);
+    check!(524288.0);
+    check!(8388608.0);
+    check!(67108864.0);
+    check!(536870912.0);
+    check!(8589934592.0);
+    check!(68719476736.0);
+    check!(549755813888.0);
+    check!(8796093022208.0);
+    check!(70368744177664.0);
+    check!(562949953421312.0);
+    check!(9007199254740992.0);
+
+    // 1000 * (Largest power of 2 <= 10^(i+1))
+    check!(8000.0);
+    check!(64000.0);
+    check!(512000.0);
+    check!(8192000.0);
+    check!(65536000.0);
+    check!(524288000.0);
+    check!(8388608000.0);
+    check!(67108864000.0);
+    check!(536870912000.0);
+    check!(8589934592000.0);
+    check!(68719476736000.0);
+    check!(549755813888000.0);
+    check!(8796093022208000.0);
+}
diff --git a/tests/exhaustive.rs b/tests/exhaustive.rs
new file mode 100644
index 0000000..e97045e
--- /dev/null
+++ b/tests/exhaustive.rs
@@ -0,0 +1,52 @@
+#![cfg(exhaustive)]
+
+use std::str;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+#[test]
+fn test_exhaustive() {
+    const BATCH_SIZE: u32 = 1_000_000;
+    let counter = Arc::new(AtomicUsize::new(0));
+    let finished = Arc::new(AtomicUsize::new(0));
+
+    let mut workers = Vec::new();
+    for _ in 0..num_cpus::get() {
+        let counter = counter.clone();
+        let finished = finished.clone();
+        workers.push(thread::spawn(move || loop {
+            let batch = counter.fetch_add(1, Ordering::SeqCst) as u32;
+            if batch > u32::max_value() / BATCH_SIZE {
+                return;
+            }
+
+            let min = batch * BATCH_SIZE;
+            let max = if batch == u32::max_value() / BATCH_SIZE {
+                u32::max_value()
+            } else {
+                min + BATCH_SIZE - 1
+            };
+
+            let mut bytes = [0u8; 24];
+            let mut buffer = ryu::Buffer::new();
+            for u in min..=max {
+                let f = f32::from_bits(u);
+                if !f.is_finite() {
+                    continue;
+                }
+                let n = unsafe { ryu::raw::format32(f, &mut bytes[0]) };
+                assert_eq!(Ok(Ok(f)), str::from_utf8(&bytes[..n]).map(str::parse));
+                assert_eq!(Ok(f), buffer.format_finite(f).parse());
+            }
+
+            let increment = (max - min + 1) as usize;
+            let update = finished.fetch_add(increment, Ordering::SeqCst);
+            println!("{}", update + increment);
+        }));
+    }
+
+    for w in workers {
+        w.join().unwrap();
+    }
+}
diff --git a/tests/f2s_test.rs b/tests/f2s_test.rs
new file mode 100644
index 0000000..916250e
--- /dev/null
+++ b/tests/f2s_test.rs
@@ -0,0 +1,172 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+#[macro_use]
+mod macros;
+
+use std::f32;
+
+fn pretty(f: f32) -> String {
+    ryu::Buffer::new().format(f).to_owned()
+}
+
+#[test]
+fn test_ryu() {
+    check!(0.3);
+    check!(1234000000000.0);
+    check!(1.234e13);
+    check!(2.71828);
+    check!(1.1e32);
+    check!(1.1e-32);
+    check!(2.7182817);
+    check!(1e-45);
+    check!(3.4028235e38);
+    check!(-0.001234);
+}
+
+#[test]
+fn test_random() {
+    let n = if cfg!(miri) { 100 } else { 1000000 };
+    let mut buffer = ryu::Buffer::new();
+    for _ in 0..n {
+        let f: f32 = rand::random();
+        assert_eq!(f, buffer.format_finite(f).parse().unwrap());
+    }
+}
+
+#[test]
+#[cfg_attr(miri, ignore)]
+fn test_non_finite() {
+    for i in 0u32..1 << 23 {
+        let f = f32::from_bits((((1 << 8) - 1) << 23) + i);
+        assert!(!f.is_finite(), "f={}", f);
+        ryu::Buffer::new().format_finite(f);
+    }
+}
+
+#[test]
+fn test_basic() {
+    check!(0.0);
+    check!(-0.0);
+    check!(1.0);
+    check!(-1.0);
+    assert_eq!(pretty(f32::NAN), "NaN");
+    assert_eq!(pretty(f32::INFINITY), "inf");
+    assert_eq!(pretty(f32::NEG_INFINITY), "-inf");
+}
+
+#[test]
+fn test_switch_to_subnormal() {
+    check!(1.1754944e-38);
+}
+
+#[test]
+fn test_min_and_max() {
+    assert_eq!(f32::from_bits(0x7f7fffff), 3.4028235e38);
+    check!(3.4028235e38);
+    assert_eq!(f32::from_bits(1), 1e-45);
+    check!(1e-45);
+}
+
+// Check that we return the exact boundary if it is the shortest
+// representation, but only if the original floating point number is even.
+#[test]
+fn test_boundary_round_even() {
+    check!(33554450.0);
+    check!(9000000000.0);
+    check!(34366720000.0);
+}
+
+// If the exact value is exactly halfway between two shortest representations,
+// then we round to even. It seems like this only makes a difference if the
+// last two digits are ...2|5 or ...7|5, and we cut off the 5.
+#[test]
+fn test_exact_value_round_even() {
+    check!(305404.12);
+    check!(8099.0312);
+}
+
+#[test]
+fn test_lots_of_trailing_zeros() {
+    // Pattern for the first test: 00111001100000000000000000000000
+    check!(0.00024414062);
+    check!(0.0024414062);
+    check!(0.0043945312);
+    check!(0.0063476562);
+}
+
+#[test]
+fn test_regression() {
+    check!(4.7223665e21);
+    check!(8388608.0);
+    check!(16777216.0);
+    check!(33554436.0);
+    check!(67131496.0);
+    check!(1.9310392e-38);
+    check!(-2.47e-43);
+    check!(1.993244e-38);
+    check!(4103.9004);
+    check!(5339999700.0);
+    check!(6.0898e-39);
+    check!(0.0010310042);
+    check!(2.882326e17);
+    check!(7.038531e-26);
+    check!(9.223404e17);
+    check!(67108870.0);
+    check!(1e-44);
+    check!(2.816025e14);
+    check!(9.223372e18);
+    check!(1.5846086e29);
+    check!(1.1811161e19);
+    check!(5.368709e18);
+    check!(4.6143166e18);
+    check!(0.007812537);
+    check!(1e-45);
+    check!(1.18697725e20);
+    check!(1.00014165e-36);
+    check!(200.0);
+    check!(33554432.0);
+}
+
+#[test]
+fn test_looks_like_pow5() {
+    // These numbers have a mantissa that is the largest power of 5 that fits,
+    // and an exponent that causes the computation for q to result in 10, which
+    // is a corner case for Ryū.
+    assert_eq!(f32::from_bits(0x5D1502F9), 6.7108864e17);
+    check!(6.7108864e17);
+    assert_eq!(f32::from_bits(0x5D9502F9), 1.3421773e18);
+    check!(1.3421773e18);
+    assert_eq!(f32::from_bits(0x5E1502F9), 2.6843546e18);
+    check!(2.6843546e18);
+}
+
+#[test]
+fn test_output_length() {
+    check!(1.0); // already tested in Basic
+    check!(1.2);
+    check!(1.23);
+    check!(1.234);
+    check!(1.2345);
+    check!(1.23456);
+    check!(1.234567);
+    check!(1.2345678);
+    check!(1.23456735e-36);
+}
diff --git a/tests/macros/mod.rs b/tests/macros/mod.rs
new file mode 100644
index 0000000..de6fb46
--- /dev/null
+++ b/tests/macros/mod.rs
@@ -0,0 +1,8 @@
+macro_rules! check {
+    ($f:tt) => {
+        assert_eq!(pretty($f), stringify!($f));
+    };
+    (-$f:tt) => {
+        assert_eq!(pretty(-$f), concat!("-", stringify!($f)));
+    };
+}
diff --git a/tests/s2d_test.rs b/tests/s2d_test.rs
new file mode 100644
index 0000000..f51c1ed
--- /dev/null
+++ b/tests/s2d_test.rs
@@ -0,0 +1,130 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+#![cfg(not(feature = "small"))]
+#![allow(dead_code)]
+
+#[path = "../src/common.rs"]
+mod common;
+
+#[path = "../src/d2s_full_table.rs"]
+mod d2s_full_table;
+
+#[path = "../src/d2s_intrinsics.rs"]
+mod d2s_intrinsics;
+
+#[path = "../src/d2s.rs"]
+mod d2s;
+
+#[path = "../src/s2d.rs"]
+mod s2d;
+
+#[path = "../src/parse.rs"]
+mod parse;
+
+use crate::parse::Error;
+use crate::s2d::s2d;
+
+impl PartialEq for Error {
+    fn eq(&self, other: &Self) -> bool {
+        *self as u8 == *other as u8
+    }
+}
+
+#[test]
+fn test_bad_input() {
+    assert_eq!(Error::MalformedInput, s2d(b"x").unwrap_err());
+    assert_eq!(Error::MalformedInput, s2d(b"1..1").unwrap_err());
+    assert_eq!(Error::MalformedInput, s2d(b"..").unwrap_err());
+    assert_eq!(Error::MalformedInput, s2d(b"1..1").unwrap_err());
+    assert_eq!(Error::MalformedInput, s2d(b"1ee1").unwrap_err());
+    assert_eq!(Error::MalformedInput, s2d(b"1e.1").unwrap_err());
+    assert_eq!(Error::InputTooShort, s2d(b"").unwrap_err());
+    assert_eq!(Error::InputTooLong, s2d(b"123456789012345678").unwrap_err());
+    assert_eq!(Error::InputTooLong, s2d(b"1e12345").unwrap_err());
+}
+
+#[test]
+fn test_basic() {
+    assert_eq!(0.0, s2d(b"0").unwrap());
+    assert_eq!(-0.0, s2d(b"-0").unwrap());
+    assert_eq!(1.0, s2d(b"1").unwrap());
+    assert_eq!(2.0, s2d(b"2").unwrap());
+    assert_eq!(123456789.0, s2d(b"123456789").unwrap());
+    assert_eq!(123.456, s2d(b"123.456").unwrap());
+    assert_eq!(123.456, s2d(b"123456e-3").unwrap());
+    assert_eq!(123.456, s2d(b"1234.56e-1").unwrap());
+    assert_eq!(1.453, s2d(b"1.453").unwrap());
+    assert_eq!(1453.0, s2d(b"1.453e+3").unwrap());
+    assert_eq!(0.0, s2d(b".0").unwrap());
+    assert_eq!(1.0, s2d(b"1e0").unwrap());
+    assert_eq!(1.0, s2d(b"1E0").unwrap());
+    assert_eq!(1.0, s2d(b"000001.000000").unwrap());
+}
+
+#[test]
+fn test_min_max() {
+    assert_eq!(
+        1.7976931348623157e308,
+        s2d(b"1.7976931348623157e308").unwrap(),
+    );
+    assert_eq!(5E-324, s2d(b"5E-324").unwrap());
+}
+
+#[test]
+fn test_mantissa_rounding_overflow() {
+    // This results in binary mantissa that is all ones and requires rounding up
+    // because it is closer to 1 than to the next smaller float. This is a
+    // regression test that the mantissa overflow is handled correctly by
+    // increasing the exponent.
+    assert_eq!(1.0, s2d(b"0.99999999999999999").unwrap());
+    // This number overflows the mantissa *and* the IEEE exponent.
+    assert_eq!(f64::INFINITY, s2d(b"1.7976931348623159e308").unwrap());
+}
+
+#[test]
+fn test_underflow() {
+    assert_eq!(0.0, s2d(b"2.4e-324").unwrap());
+    assert_eq!(0.0, s2d(b"1e-324").unwrap());
+    assert_eq!(0.0, s2d(b"9.99999e-325").unwrap());
+    // These are just about halfway between 0 and the smallest float.
+    // The first is just below the halfway point, the second just above.
+    assert_eq!(0.0, s2d(b"2.4703282292062327e-324").unwrap());
+    assert_eq!(5e-324, s2d(b"2.4703282292062328e-324").unwrap());
+}
+
+#[test]
+fn test_overflow() {
+    assert_eq!(f64::INFINITY, s2d(b"2e308").unwrap());
+    assert_eq!(f64::INFINITY, s2d(b"1e309").unwrap());
+}
+
+#[test]
+fn test_table_size_denormal() {
+    assert_eq!(5e-324, s2d(b"4.9406564584124654e-324").unwrap());
+}
+
+#[test]
+fn test_issue157() {
+    assert_eq!(
+        1.2999999999999999E+154,
+        s2d(b"1.2999999999999999E+154").unwrap(),
+    );
+}
diff --git a/tests/s2f_test.rs b/tests/s2f_test.rs
new file mode 100644
index 0000000..f49824a
--- /dev/null
+++ b/tests/s2f_test.rs
@@ -0,0 +1,77 @@
+// Translated from C to Rust. The original C code can be found at
+// https://github.com/ulfjack/ryu and carries the following license:
+//
+// Copyright 2018 Ulf Adams
+//
+// The contents of this file may be used under the terms of the Apache License,
+// Version 2.0.
+//
+//    (See accompanying file LICENSE-Apache or copy at
+//     http://www.apache.org/licenses/LICENSE-2.0)
+//
+// Alternatively, the contents of this file may be used under the terms of
+// the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE-Boost or copy at
+//     https://www.boost.org/LICENSE_1_0.txt)
+//
+// Unless required by applicable law or agreed to in writing, this software
+// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.
+
+#![allow(dead_code)]
+
+#[path = "../src/common.rs"]
+mod common;
+
+#[path = "../src/d2s_full_table.rs"]
+mod d2s_full_table;
+
+#[path = "../src/d2s_intrinsics.rs"]
+mod d2s_intrinsics;
+
+#[path = "../src/d2s.rs"]
+mod d2s;
+
+#[path = "../src/f2s_intrinsics.rs"]
+mod f2s_intrinsics;
+
+#[path = "../src/f2s.rs"]
+mod f2s;
+
+#[path = "../src/s2f.rs"]
+mod s2f;
+
+#[path = "../src/parse.rs"]
+mod parse;
+
+use crate::parse::Error;
+use crate::s2f::s2f;
+
+impl PartialEq for Error {
+    fn eq(&self, other: &Self) -> bool {
+        *self as u8 == *other as u8
+    }
+}
+
+#[test]
+fn test_basic() {
+    assert_eq!(0.0, s2f(b"0").unwrap());
+    assert_eq!(-0.0, s2f(b"-0").unwrap());
+    assert_eq!(1.0, s2f(b"1").unwrap());
+    assert_eq!(-1.0, s2f(b"-1").unwrap());
+    assert_eq!(123456792.0, s2f(b"123456789").unwrap());
+    assert_eq!(299792448.0, s2f(b"299792458").unwrap());
+}
+
+#[test]
+fn test_min_max() {
+    assert_eq!(1e-45, s2f(b"1e-45").unwrap());
+    assert_eq!(f32::MIN_POSITIVE, s2f(b"1.1754944e-38").unwrap());
+    assert_eq!(f32::MAX, s2f(b"3.4028235e+38").unwrap());
+}
+
+#[test]
+fn test_mantissa_rounding_overflow() {
+    assert_eq!(1.0, s2f(b"0.999999999").unwrap());
+    assert_eq!(f32::INFINITY, s2f(b"3.4028236e+38").unwrap());
+}