Compare commits

..

622 Commits
v2.0.0 ... main

Author SHA1 Message Date
Daniel Lemire
f8c573d741
Merge pull request #338 from Gravecat/mingw-fix
Fixes compilation on GCC/MinGW
2025-11-23 16:25:56 -05:00
Raine 'Gravecat' Simmons
9d78a01ff7
Fixed formatting with clang-format 2025-11-22 21:53:37 +00:00
Raine 'Gravecat' Simmons
409d6215b4
Fixes compilation on GCC/MinGW 2025-11-22 16:11:06 +00:00
Daniel Lemire
7b21183a93
Merge pull request #336 from fastfloat/dependabot/github_actions/github-actions-625a8519cf
Bump the github-actions group across 1 directory with 4 updates
2025-10-30 22:04:57 -04:00
dependabot[bot]
8ac72791a9
Bump the github-actions group across 1 directory with 4 updates
Bumps the github-actions group with 4 updates in the / directory: [actions/checkout](https://github.com/actions/checkout), [actions/upload-artifact](https://github.com/actions/upload-artifact), [github/codeql-action](https://github.com/github/codeql-action) and [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/checkout` from 4 to 5
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

Updates `actions/upload-artifact` from 4 to 5
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

Updates `github/codeql-action` from 3 to 4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

Updates `actions/setup-node` from 4.4.0 to 6.0.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](49933ea528...2028fbc5c2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/setup-node
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 00:14:47 +00:00
Daniel Lemire
e77e2bca7e
Update README.md 2025-10-10 09:23:01 -04:00
Daniel Lemire
24fa687d45
Merge pull request #334 from toughengineer/max-infinity_corner_cases
Added corner cases around max value/infinity
2025-10-04 09:50:30 -04:00
Pavel Novikov
88f6c5e367
Added corner cases around max value/infinity 2025-10-04 14:39:00 +03:00
Daniel Lemire
83a4f696d7
Merge pull request #333 from toughengineer/made_function_non-template
Made function non-template
2025-09-30 10:25:59 -04:00
Pavel Novikov
1ea4d2563e
made function non-template
+fixed a couple of typos
2025-09-30 12:18:29 +03:00
Daniel Lemire
7262d9454e lint 2025-09-29 15:08:24 -04:00
Daniel Lemire
23f16adad4
Merge pull request #328 from toughengineer/int_multiplication_by_power_of_10
Added template overloads for `integer_times_pow10()`
2025-09-29 15:07:54 -04:00
Daniel Lemire
fd98fd6689
specialize for std::float32_t and std::float64_t explicitly
credit: @lemire
2025-09-29 21:43:36 +03:00
Pavel Novikov
197c0ffca7
clang format 2025-09-29 21:43:35 +03:00
Pavel Novikov
e9438e64ba
fixed copy&paste error and minor mess 2025-09-29 19:54:25 +03:00
Pavel Novikov
7abb574ffc
added doc to README and examples 2025-09-29 13:00:40 +03:00
Anders Dalvander
04e8e545d8
Merge pull request #330 from jbicha/repology-badge
README.md: update repology badge
2025-09-24 22:34:00 +02:00
Jeremy Bícha
e4b949e55b README.md: update repology badge 2025-09-24 16:08:34 -04:00
Pavel Novikov
01e505797b
added tests + some refactoring 2025-09-18 21:29:25 +03:00
Pavel Novikov
13345cab65
added template overload for integer_times_pow10() 2025-09-18 21:29:25 +03:00
Daniel Lemire
88b1e5321c version 8.1.0 2025-09-18 09:38:45 -06:00
Daniel Lemire
2aa6d0ba72
Merge pull request #326 from fastfloat/patch803
Release candidate 8.0.3
2025-09-18 09:37:20 -06:00
Daniel Lemire
0b6d911220 format 2025-09-18 08:30:28 -06:00
Daniel Lemire
2d6574483d
Merge pull request #327 from toughengineer/int_multiplication_by_power_of_10
Minor fix of forward declaration
2025-09-18 08:29:57 -06:00
Pavel Novikov
7a77227521
minor fix of forward declaration 2025-09-18 17:02:29 +03:00
Daniel Lemire
e20c952456
Merge pull request #320 from toughengineer/int_multiplication_by_power_of_10
Implemented multiplication of integer by a power of 10
2025-09-18 07:48:09 -06:00
Daniel Lemire
bb956b29db release candidate 8.0.3 2025-09-18 07:44:53 -06:00
Daniel Lemire
48fc5404d4 compatibility fix 2025-09-18 07:44:05 -06:00
Daniel Lemire
e5612e9f72
Merge pull request #325 from InvalidUsernameException/emoji-parses-as-int
Do not mis-parse certain wide-character emojis as integer
2025-09-18 07:39:59 -06:00
Daniel Lemire
fb384a4205
Add Blender to the list of projects using fast_float 2025-09-17 23:09:25 -06:00
InvalidUsernameException
9d81c71aef Do not mis-parse certain wide-character emojis as integer
When calling ch_to_digit() with a UTF-16 or UTF-32 code unit, it simply
truncates away any data stored in the non-low byte(s) of the code unit.
It then uses a lookup table to determine whether the low byte
corresponds to an ASCII digit. This is incorrect because as soon as any
bit outside the low byte is set, the number will never correspond to a
ASCII digit anymore.

To fix this, we produce a mask that is all zeroes if any bit outside the
low byte is set in the code unit, all ones otherwise. Anding this mask
with the original code unit forces the table lookup to return the
sentinel value from the zero-index if any high bit was set and causes
the code unit not to be parsed as integer.

This bug was discovered when loading Mastodon posts inside the Ladybird
browser where some of Mastodon's JavaScript would trigger the code path
that erroneously parsed the emoji as integer. It had the visible effect
that some digits inside the posts would get rendered as one of the
emojis that parsed to that digit. For more details see this issue:
https://github.com/LadybirdBrowser/ladybird/issues/6205

The emojis in the test case are simply all the emojis used on Mastodon
that caused the bug. They can be found here:
06803422da/app/javascript/mastodon/features/emoji/emoji_map.json
2025-09-15 23:12:28 +02:00
Daniel Lemire
fec4082f01
Merge pull request #322 from leiwen2025/rv-support
float_common.h: Support RISC-V
2025-09-12 15:11:45 -04:00
Daniel Lemire
e89e248bd7
Update risc.yml 2025-09-11 22:14:59 -04:00
Daniel Lemire
96fc38fb5a adding risc ci 2025-09-11 22:11:05 -04:00
WenLei
6677924083 float_common.h: Support RISC-V 2025-09-11 11:11:30 +08:00
Pavel Novikov
0a230326ab
now finally got the anti-ambiguity overloads right, right? 2025-09-06 02:22:43 +03:00
Pavel Novikov
7ae62ee0d5
finally got the anti-ambiguity overloads right? 2025-09-06 02:10:55 +03:00
Daniel Lemire
4bb6fd1271
Merge pull request #314 from fastfloat/P2497R0
implementation of p2497
2025-09-05 18:24:48 -04:00
Pavel Novikov
e12463583f
added lacking overloads to avoid potential ambiguity 2025-09-06 00:12:41 +03:00
Pavel Novikov
6702cd4244
added doc section in the README,
added example code test executable
2025-09-05 13:36:23 +03:00
Pavel Novikov
20a7383442
renamed the function, cleaned up return type 2025-09-05 13:36:23 +03:00
Pavel Novikov
763558b9ac
cleaned up tests 2025-09-05 13:34:48 +03:00
Daniel Lemire
ce511cb9ee
Merge pull request #317 from fastfloat/dependabot/github_actions/github-actions-a331d3ec2d
Bump actions/checkout from 4 to 5 in the github-actions group
2025-09-03 12:07:06 -04:00
Daniel Lemire
42db9ac1de
Merge branch 'main' into P2497R0 2025-09-03 12:04:36 -04:00
Pavel Novikov
6be07d66a8
inlining Clinger's fast path because why not,
and it seems to bring performance to the level before the changes somewhat
2025-09-03 16:59:40 +03:00
dependabot[bot]
42836b48eb
Bump actions/checkout from 4 to 5 in the github-actions group
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 4 to 5
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 21:31:49 +00:00
Daniel Lemire
a9ac1b40c9
Merge pull request #316 from fastfloat/emscripten
adding emscripten build test
2025-09-02 17:29:37 -04:00
Daniel Lemire
c0582c27f5 typos in the comments 2025-09-02 17:29:12 -04:00
Pavel Novikov
cc90f240ee
added some tests 2025-09-02 23:03:38 +03:00
Pavel Novikov
a134561e4b
added missing inline specifiers 2025-09-02 23:03:38 +03:00
Pavel Novikov
7b8f04500a
implemented multiplication of integer by power of 10 2025-09-02 13:20:36 +03:00
Daniel Lemire
0a9257e825
Merge pull request #318 from SirLynix/patch-1
README: Add xmake repository reference
2025-08-25 09:27:45 -04:00
Jérôme Leclercq
e1c2c806ed
README: Add xmake repository reference 2025-08-25 13:42:53 +02:00
Daniel Lemire
6b10835aa9 update 2025-06-04 21:14:09 -04:00
Daniel Lemire
3d09138b1b trimming 2025-06-04 20:45:56 -04:00
Daniel Lemire
3ee7eb018d updating CI 2025-06-03 18:19:33 -04:00
Daniel Lemire
2d2b42bb38 forked doctest 2025-06-03 18:15:52 -04:00
Daniel Lemire
73b27b7d68 hmmm 2025-06-02 09:52:34 -04:00
Daniel Lemire
fbc85aa706 adding cmake policy for doctest 2025-06-02 09:25:42 -04:00
Daniel Lemire
5a4b793818 adding emscripten build test 2025-06-02 09:15:06 -04:00
Daniel Lemire
e5d93c993e updating workflow 2025-05-19 18:32:56 -04:00
Daniel Lemire
a1e272f515 lint 2025-05-19 18:16:14 -04:00
Daniel Lemire
447ee0bc82 updating documentation 2025-05-19 18:12:35 -04:00
Daniel Lemire
0458c20061 adding missing file 2025-05-19 18:09:34 -04:00
Daniel Lemire
81b8306c5f implementation of https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2497r0.html 2025-05-19 18:08:36 -04:00
Daniel Lemire
b8085ba363
Update README.md 2025-05-19 11:55:15 -04:00
Daniel Lemire
c5a3ca37c4
Merge pull request #312 from fastfloat/dependabot/github_actions/github-actions-ce77f7460b
Bump jidicula/clang-format-action from 4.14.0 to 4.15.0 in the github-actions group
2025-03-25 09:58:53 -04:00
dependabot[bot]
d42004eca2
Bump jidicula/clang-format-action in the github-actions group
Bumps the github-actions group with 1 update: [jidicula/clang-format-action](https://github.com/jidicula/clang-format-action).


Updates `jidicula/clang-format-action` from 4.14.0 to 4.15.0
- [Release notes](https://github.com/jidicula/clang-format-action/releases)
- [Commits](d05cecd4a1...4726374d1a)

---
updated-dependencies:
- dependency-name: jidicula/clang-format-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-24 00:53:11 +00:00
Daniel Lemire
3f7ff422bf
Merge pull request #311 from ADKaster/reference-ak
README: Reference SerenityOS/Ladybird Browser implementation
2025-03-21 13:00:42 -04:00
Andrew Kaster
b3fe1e63cf README: Reference SerenityOS/Ladybird Browser implementation 2025-03-21 10:29:13 -06:00
Daniel Lemire
50a80a73ab v8.0.2 2025-03-11 09:51:53 -04:00
Anders Dalvander
f9bed3f7b1
Merge pull request #309 from fastfloat/benchvs
supporting benchmarks under Windows
2025-03-11 14:35:15 +01:00
Daniel Lemire
c0affad8b4
Update benchmark.cpp 2025-03-11 09:19:28 -04:00
Daniel Lemire
7597ca61aa supporting benchmarks under Windows 2025-03-10 19:44:18 -04:00
Daniel Lemire
1bf7010153 v8.0.1 2025-03-10 09:04:05 -04:00
Daniel Lemire
e019768d49
Merge pull request #308 from fastfloat/turing_json_into_macro_parameter
turning json option into macro parameter
2025-03-10 09:02:53 -04:00
Daniel Lemire
c6732cd28b lint 2025-03-10 09:02:38 -04:00
Daniel Lemire
b29208f93d adding FASTFLOAT_IF_CONSTEXPR17 2025-03-09 17:10:55 -04:00
Daniel Lemire
ba92c88901
Update pull_request_template.md 2025-03-09 15:33:11 -04:00
Daniel Lemire
0305a7d336
Update README.md 2025-03-09 15:31:45 -04:00
Daniel Lemire
e720395b61
Update README.md 2025-03-09 15:31:11 -04:00
Daniel Lemire
8e5e92b6e2
Merge pull request #305 from dankmeme01/main
fix /permissive- flag causing a compile error on clang for windows
2025-03-09 15:15:35 -04:00
Daniel Lemire
6f0049a2e7 lint 2025-03-09 15:14:52 -04:00
Daniel Lemire
95dedd0aed turning json option into macro parameter 2025-03-09 15:13:43 -04:00
Daniel Lemire
1504792a9f Merge branch 'main' of github.com:fastfloat/fast_float 2025-03-09 14:27:38 -04:00
Daniel Lemire
864e790d22 allowing custom file parsing 2025-03-09 14:27:07 -04:00
Daniel Lemire
3c5b166f6c
Merge pull request #306 from fastfloat/dependabot/github_actions/github-actions-559242db6c
Bump uraimo/run-on-arch-action from 2 to 3 in the github-actions group
2025-03-03 15:58:21 +00:00
dependabot[bot]
c584342aa5
Bump uraimo/run-on-arch-action from 2 to 3 in the github-actions group
Bumps the github-actions group with 1 update: [uraimo/run-on-arch-action](https://github.com/uraimo/run-on-arch-action).


Updates `uraimo/run-on-arch-action` from 2 to 3
- [Release notes](https://github.com/uraimo/run-on-arch-action/releases)
- [Commits](https://github.com/uraimo/run-on-arch-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: uraimo/run-on-arch-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 00:21:03 +00:00
dankmeme01
b7b17e6cac improve check for /permissive- flag 2025-02-26 14:49:12 +01:00
dankmeme01
810a750306 fix /permissive- flag casuing a compile error on clang for windows 2025-02-26 12:18:16 +01:00
Anders Dalvander
d28a3320c2
Merge pull request #303 from biojppm/fix_wundef
Fix -Wundef warnings seen in gcc 4.8
2025-02-13 13:24:47 +01:00
Joao Paulo Magalhaes
3e9dc96eb4 Fix -Wundef warnings seen in gcc 4.8 and 4.9 2025-02-13 08:45:11 +00:00
Daniel Lemire
96067ad107
Merge pull request #302 from biojppm/fix_wundef
fix warning from -Wundef
2025-02-12 19:41:56 -05:00
Joao Paulo Magalhaes
9cfa5ff4a0 fix warnings from -Wundef 2025-02-12 23:51:44 +00:00
Daniel Lemire
77cc847c84 pushing version change 2025-02-08 11:49:58 -05:00
Daniel Lemire
717112d257 lint 2025-02-06 20:25:09 -05:00
Daniel Lemire
f0c709e3e4 ignoring failures 2025-02-06 20:24:43 -05:00
Daniel Lemire
7a5ee5af60
Merge pull request #296 from dalle/dalle/float16
16-bit float support
2025-02-06 19:41:37 -05:00
Daniel Lemire
1d50f57dd9 better wording. 2025-02-06 00:16:28 -05:00
Daniel Lemire
3a74bfb500
Merge pull request #299 from fastfloat/document_bench
documenting benchmarks
2025-02-05 19:53:33 -05:00
Daniel Lemire
babfa8b85c documenting benchmarks 2025-02-05 19:52:55 -05:00
Daniel Lemire
482cc1f251
Merge pull request #298 from jrade/main
Silenced Visual Studio compiler warning
2025-01-16 15:55:12 -05:00
Johan Råde
d4d5748ec8 Silenced Visual Studio compiler warning
This code caused a C4127 "conditional expression is constant" compiler warning
when compiled with Visual Studio at warning level 4 with T a signed integer type.
2025-01-16 16:12:34 +01:00
Daniel Lemire
be9a8453c4 more iterations 2024-12-13 22:54:04 -05:00
Anders Dalvander
87aae63ed7 readme updates for std::float16_t and std::bfloat16_t 2024-12-04 21:12:25 +01:00
Anders Dalvander
dc39efa49d readme fix for skip_white_space and allow_leading_plus 2024-12-04 21:03:11 +01:00
Anders Dalvander
ce274f7051 include and order types largerst to smallest 2024-12-04 10:17:05 +01:00
Anders Dalvander
7226c00490 cleanup 2024-12-04 09:35:32 +01:00
Anders Dalvander
2a6c2604b5 check for IEEE 754 compliance 2024-12-04 09:34:34 +01:00
Anders Dalvander
f23ced2e4e fix for supplemental 2024-12-04 01:02:20 +01:00
Anders Dalvander
baaf58d2dd fix -Werror=maybe-uninitialized 2024-12-04 00:13:20 +01:00
Anders Dalvander
62f526dfd3 fix -Werror=conversion 2024-12-03 23:52:57 +01:00
Anders Dalvander
63bbefad6b templates and types 2024-12-03 23:47:21 +01:00
Anders Dalvander
ac453a091a overly precise tests for imprecise floats 2024-12-03 23:23:35 +01:00
Anders Dalvander
da819feb74 back to std::bit_cast 2024-12-03 23:23:35 +01:00
Anders Dalvander
3b9ff76143 duplicate tests for both float and double 2024-12-03 23:23:34 +01:00
Anders Dalvander
c62b853648 float.rounds_to_nearest 2024-12-03 23:23:34 +01:00
Anders Dalvander
b3acae22ea fix parse_zero and parse_negative_zero output 2024-12-03 23:23:34 +01:00
Anders Dalvander
74e00e1401 fix double test in float region in basictest 2024-12-03 23:23:34 +01:00
Anders Dalvander
558bec8b9b fix logging in basictest 2024-12-03 23:23:34 +01:00
Anders Dalvander
8cee025082 add powers_of_ten/max_mantissa for float16_t/bfloat16_t 2024-12-03 23:23:34 +01:00
Daniel Lemire
6f8fd6728d make it build 2024-12-03 23:23:34 +01:00
Daniel Lemire
c526899951 cleaning. 2024-12-03 23:23:34 +01:00
Daniel Lemire
bfcff49c83 16-bit float support 2024-12-03 23:23:34 +01:00
Daniel Lemire
31cc0d1a0e
Merge pull request #295 from fastfloat/dependabot/github_actions/github-actions-d1805d1051
Bump jidicula/clang-format-action from 4.13.0 to 4.14.0 in the github-actions group
2024-12-02 13:22:05 -05:00
dependabot[bot]
832307e7b6
Bump jidicula/clang-format-action in the github-actions group
Bumps the github-actions group with 1 update: [jidicula/clang-format-action](https://github.com/jidicula/clang-format-action).


Updates `jidicula/clang-format-action` from 4.13.0 to 4.14.0
- [Release notes](https://github.com/jidicula/clang-format-action/releases)
- [Commits](c74383674b...d05cecd4a1)

---
updated-dependencies:
- dependency-name: jidicula/clang-format-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-02 01:02:18 +00:00
Anders Dalvander
49aed7782e
Merge pull request #294 from dalle/dalle/equiv_uint_t
* introduce equiv_uint_t helper
* harmonize ifdef checks
* formatted code
2024-12-01 17:01:25 +01:00
Anders Dalvander
3775a81ced formatted code 2024-12-01 16:39:28 +01:00
Anders Dalvander
0a1bf11560 harmonize ifdef checks 2024-12-01 16:36:45 +01:00
Anders Dalvander
3146e686d0 introduce equiv_uint_t 2024-12-01 16:36:17 +01:00
Daniel Lemire
82865ad475
Update README.md 2024-11-26 10:01:42 -05:00
Daniel Lemire
0a17150096
Update README.md 2024-11-25 15:49:40 -05:00
Daniel Lemire
7178b7dddd
Merge pull request #293 from fastfloat/add_benchmarks
adding actual benchmarks to the project
2024-11-25 14:13:18 -05:00
Daniel Lemire
8832c532b8 lint 2024-11-25 11:59:48 -05:00
Daniel Lemire
3f2cd66c1c adding actual benchmarks to the project 2024-11-25 11:59:03 -05:00
Anders Dalvander
ac33f96a60
Merge pull request #292 from dalle/dalle/unicode-tests
Add basic tests for all supported char types (char, wchar_t, char8_t, char16_t, char32_t)
2024-11-25 16:01:22 +01:00
Anders Dalvander
396feb6353 add char8_t test 2024-11-25 15:43:51 +01:00
Anders Dalvander
2c4b48d4e0 add unicode_test 2024-11-25 15:39:04 +01:00
Anders Dalvander
321d3a78b8
Merge pull request #290 from dalle/dalle/fix-scripts
* fixed version updates in release script
* formatted Python scripts with `black` - The uncompromising code formatter (https://black.readthedocs.io/)
* f-string all the things
2024-11-23 11:46:07 +01:00
Anders Dalvander
49068fbfda fix newline in readme 2024-11-23 11:32:39 +01:00
Anders Dalvander
b9793e1cd4 update scripts
* fixed version updates in release script
* formatted Python scripts with `black` - The uncompromising code formatter (https://black.readthedocs.io/)
* f-string all the things
2024-11-23 11:24:39 +01:00
Anders Dalvander
6801b0ca20
Merge pull request #289 from dalle/issue288-char8-support
Support char8_t on C++20
2024-11-23 10:40:32 +01:00
Anders Dalvander
d3f7113d6e add char8_t support 2024-11-23 10:21:48 +01:00
Anders Dalvander
65911af27c
Merge pull request #287 from dalle/fix-clang-warning-unused-function
* Fix clang warning unused function
* `east const` formatting
2024-11-23 09:58:46 +01:00
Anders Dalvander
1a15c66fb9 harmonize files to use "east const"
manually checked modified files for errors, but not committing .clang-format due to the following warning:
>>> Setting `QualifierAlignment` to something other than `Leave`, COULD lead to incorrect code formatting due to incorrect decisions made due to clang-formats lack of complete semantic information. As such extra care should be taken to review code changes made by the use of this option.
2024-11-23 09:46:18 +01:00
Anders Dalvander
7f476cd259 fix clang unused-function warning 2024-11-23 09:04:46 +01:00
Daniel Lemire
54782eb30c
Merge pull request #285 from fastfloat/fix_release_script
the release script will update the header file with the new version numbers (automatically!!!)
2024-11-22 13:38:58 -05:00
Daniel Lemire
f436996266 the release script will update the header file with the new version numbers (automatically!!!) 2024-11-22 13:22:36 -05:00
Daniel Lemire
cf771eaa83
Merge pull request #283 from dalle/issue282-better-error-messages
Better compile time error messages for unsupported types
2024-11-22 13:09:49 -05:00
Daniel Lemire
3d33762eed
Merge pull request #284 from dalle/issue273-version-macros
Add version macros
2024-11-22 12:23:48 -05:00
Anders Dalvander
35d0c8c179 add version macros 2024-11-22 16:44:56 +01:00
Anders Dalvander
74829bb77d better compile time error messages for unsupported types 2024-11-22 16:25:31 +01:00
Daniel Lemire
cb1d42aaa1 version 7 2024-11-21 13:18:48 -05:00
Daniel Lemire
b9661b41af
Merge pull request #280 from dalle/issue275-deprecate-feature-macros
Add allow_leading_plus and skip_white_space in chars_format
2024-11-21 13:02:17 -05:00
Anders Dalvander
4ed0177782 update and reformat README.md 2024-11-21 18:50:42 +01:00
Anders Dalvander
dead62d440 disable test if fast-math is enabled 2024-11-21 00:09:10 +01:00
Anders Dalvander
50ee38af65 fix for fastfloat_strncasecmp for wchar_t and larger char types 2024-11-21 00:09:05 +01:00
Anders Dalvander
7ff885d45c fix for is_space for wchar_t and larger char types 2024-11-21 00:09:00 +01:00
Anders Dalvander
3e26cf4cea add failing test for wide chars 2024-11-21 00:08:55 +01:00
Anders Dalvander
1e188d9715 add missing json test 2024-11-21 00:08:47 +01:00
Anders Dalvander
43a428d658 remove feature macro from tests 2024-11-21 00:08:43 +01:00
Anders Dalvander
cd28b563fc update rcppfastfloat_test 2024-11-21 00:08:33 +01:00
Anders Dalvander
d80be1a5b0 RFC 8259 obsoletes RFC 7159 2024-11-21 00:08:23 +01:00
Anders Dalvander
48252a6483 check feature macros in once place 2024-11-21 00:08:19 +01:00
Anders Dalvander
0bbba960f4 move from feature macros to format flags 2024-11-21 00:06:08 +01:00
Daniel Lemire
724834fe8e
Merge pull request #279 from dalle/issue278-scoped-enum
enum class chars_format
2024-11-20 17:16:55 -05:00
Anders Dalvander
b3526da935 uint64_t as enum base for chars_format 2024-11-20 23:01:56 +01:00
Anders Dalvander
b8b5da75a5 enum class chars_format 2024-11-20 09:33:16 +01:00
Daniel Lemire
42fded19ae
Update vs17-arm-ci.yml 2024-11-19 10:54:10 -05:00
Daniel Lemire
8e5f76ce73
Merge pull request #277 from dalle/issue276-plus-minus-infnan
Issue276 plus/minus handling in parse_infnan
2024-11-19 10:39:49 -05:00
Anders Dalvander
b635dec11a clang format 2024-11-19 10:32:37 +01:00
Anders Dalvander
23787fc71a fix #276: parse_infnan handles FASTFLOAT_ALLOWS_LEADING_PLUS correctly 2024-11-17 16:23:01 +01:00
Anders Dalvander
72b2a7382a const ness 2024-11-17 16:22:05 +01:00
Anders Dalvander
1df71f1e9d add failing test 2024-11-17 16:20:44 +01:00
Anders Dalvander
269867fa43 spelling 2024-11-17 16:17:35 +01:00
Daniel Lemire
e800cabe48
Merge pull request #274 from BYVoid/bazel
Add Bazel build rules.
2024-11-01 17:01:12 -04:00
Carbo Kuo
d65285a48c Add Bazel build rules. 2024-11-01 09:16:24 -04:00
Daniel Lemire
7665574628
Merge pull request #272 from jwakely/trailing-whitespace
Prevent amalgamate.py from adding trailing whitespace
2024-10-24 12:02:33 -04:00
Jonathan Wakely
a60ef3b2e1 Prevent amalgamate.py from adding trailing whitespace
When adding the license text, only add leading whitespace to non-empty
lines, otherwise it becomes trailing whitespace.
2024-10-24 10:47:17 +01:00
Daniel Lemire
9ab35254a8
fixing issue https://github.com/fastfloat/fast_float/issues/271 2024-10-16 14:21:02 -04:00
Daniel Lemire
4266c34620
[no-ci] Update README.md 2024-09-15 10:56:20 -04:00
Daniel Lemire
00c8c7b0d5 version 6.1.6 2024-09-08 13:36:17 -04:00
Daniel Lemire
fe53e769e4
Merge pull request #270 from pranav-sivaraman/add-find-package
doctest: if SYSTEM_DOCTEST add a `find_package(doctest REQUIRED)`
2024-09-08 13:35:34 -04:00
Pranav Sivaraman
a7ed4e89c7 doctest: if SYSTEM_DOCTEST add a find_package(doctest REQUIRED) 2024-09-08 12:34:44 -04:00
Daniel Lemire
564af6d074 version 6.1.5 2024-09-02 15:32:58 -04:00
Daniel Lemire
8c911d9b71
Merge pull request #269 from eukarpov/support-aarch64-w64-mingw32-target
Support aarch64-w64-mingw32 target
2024-09-02 15:32:14 -04:00
Evgeny Karpov
c9f8339668 Fix the formatting and remove the previous condition 2024-09-02 18:13:01 +02:00
Evgeny Karpov
2609d5fd4b The patch resolves GCC compilation issues for the C++ language targeting
aarch64-w64-mingw32.

More information could be found here:
https://gcc.gnu.org/pipermail/gcc-patches/2024-September/662020.html
2024-09-02 17:24:33 +02:00
Daniel Lemire
9117ec4f69 formatting 2024-08-31 18:27:44 -04:00
Daniel Lemire
c8abf94560
Merge pull request #268 from yfeldblum/fix-deprecated-cxx17-cexpr-static-data-defn-out-of-line-redundant
remove out-of-line defns of constexpr static data members under c++17
2024-08-31 18:26:51 -04:00
Yedidya Feldblum
159589d356 remove out-of-line defns of constexpr static data members under c++17
Address warnings of this form under c++17 when building with `-Werror -Wdeprecated`:
```
fast_float/float_common.h:446:58: error: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated [-Werror,-Wdeprecated]
constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
                                                         ^
```
2024-08-30 10:30:40 -05:00
Daniel Lemire
f03b76f0e6
Update README.md 2024-08-27 08:25:45 -04:00
Daniel Lemire
6385dca660
Update README.md 2024-08-26 10:49:31 -04:00
Daniel Lemire
cba388b1dc
Update README.md 2024-08-26 10:47:39 -04:00
Daniel Lemire
bc042d6318
Update README.md 2024-08-26 10:18:35 -04:00
Daniel Lemire
56153ba9d8
Update README.md 2024-08-26 10:15:53 -04:00
Daniel Lemire
4c303f4759
Merge pull request #266 from fastfloat/dependabot/github_actions/github-actions-65ba76e7df
Bump the github-actions group with 3 updates
2024-08-24 13:10:10 -04:00
dependabot[bot]
ce0eb58dac
Bump the github-actions group with 3 updates
Bumps the github-actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [actions/upload-artifact](https://github.com/actions/upload-artifact) and [github/codeql-action](https://github.com/github/codeql-action).


Updates `actions/checkout` from 1 to 4
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v1...v4)

Updates `actions/upload-artifact` from 3 to 4
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

Updates `github/codeql-action` from 2 to 3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-24 16:19:19 +00:00
Daniel Lemire
202965c7e7
Merge pull request #265 from cclauss/patch-1
Keep GitHub Actions up to date with GitHub's Dependabot
2024-08-24 12:13:18 -04:00
Christian Clauss
63b75cf1dd
Keep GitHub Actions up to date with GitHub's Dependabot
Fixes software supply chain safety warnings like at the bottom right of
https://github.com/fastfloat/fast_float/actions/runs/10534472547

* [Keeping your actions up to date with Dependabot](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot)
* [Configuration options for the dependabot.yml file - package-ecosystem](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem)
2024-08-24 16:25:23 +02:00
Daniel Lemire
00f25932b0
Merge pull request #263 from fastfloat/doc_issue_261
documentation for issue 261
2024-08-23 19:31:05 -04:00
Daniel Lemire
dab0827f97 documentation for issue 261 2024-08-23 19:29:16 -04:00
Daniel Lemire
3642e92a36
[no-ci] Update README.md 2024-08-14 10:05:20 -04:00
Daniel Lemire
3e57d8dcfb v6.1.4 2024-08-14 09:58:41 -04:00
Daniel Lemire
5ad6aae0b1 lint 2024-08-14 09:57:47 -04:00
Daniel Lemire
8f3dae6b9f
Merge pull request #260 from fastfloat/intrin-for-umul
Include intrin.h for __umulh
2024-08-14 09:54:41 -04:00
Leszek Swirski
74e3c29215 Include intrin.h for __umulh
Arm64 uses __umulh, so add the same condition for including the intrin.h
header.
2024-08-14 15:10:14 +02:00
Daniel Lemire
d57ca3da1f
Merge pull request #259 from fastfloat/issue258
issue 258
2024-08-03 16:38:10 -04:00
Daniel Lemire
0e7a10ad80
Merge pull request #252 from LeszekSwirski/parse-error
Record parse failure reason and location
2024-08-03 10:08:48 -04:00
Daniel Lemire
3838b00751
Merge pull request #257 from fastfloat/lint
add linter
2024-08-03 10:08:14 -04:00
Daniel Lemire
207d66162a issue 258 2024-08-03 10:02:42 -04:00
Leszek Swirski
b6ce2c4de6 Record parse failure reason and location
In parse_number_string, if there is a parse error, report the specific
error as one of the values in a new parse_error enum, and update
lastmatch to match the error location. This allows users of the library
to print more helpful error messages for invalid inputs.
2024-07-23 18:11:10 +02:00
Daniel Lemire
77b8aa0a33 add linter 2024-07-23 10:42:54 -04:00
Daniel Lemire
d7417618f9 patch release 2024-07-23 10:29:14 -04:00
Daniel Lemire
97036e362c
Merge pull request #256 from fastfloat/json-accept-empty-exponent
Allow JSON empty exponent as trailing junk
2024-07-23 10:28:36 -04:00
Leszek Swirski
e6777f2eef Allow JSON empty exponent as trailing junk
Revert #251, and instead allow an invalid trailing empty exponent to be
treated as junk data in JSON parsing. Expand the test suite to test this
case, including testing the trailing junk.
2024-07-23 14:56:42 +02:00
Daniel Lemire
129f0e807e
Merge pull request #255 from fastfloat/version612
preparing patch release
2024-07-22 11:42:50 -04:00
Daniel Lemire
0460ab8bd6 preparing patch release 2024-07-22 09:56:25 -04:00
Daniel Lemire
28e7560c23
Merge pull request #253 from fastfloat/update_ci_to_ubuntu24
update CI to ubuntu 24 + safe a shift value to a variable (for elegance)
2024-07-22 09:41:38 -04:00
Daniel Lemire
d65638bf0f [no-ci] renaming CI 2024-07-22 09:39:57 -04:00
Daniel Lemire
ef9a60d516 update CI to ubuntu 24 + safe a shift value to a variable (for elegance) 2024-07-22 09:36:14 -04:00
Daniel Lemire
9468d50c89
Merge pull request #251 from LeszekSwirski/json-empty-exponent
Disallow empty exponent in JSON parsing
2024-07-22 09:07:08 -04:00
Leszek Swirski
b57207c811 Disallow empty exponent in JSON parsing
The JSON spec requires at least one digit in the exponential part, if
specified.
2024-07-22 12:34:58 +02:00
Daniel Lemire
92c79d078b
Merge pull request #249 from zejal/main
from_chars_advanced overload function taking parsed_number_string_t
2024-07-16 10:17:08 -04:00
Daniel Lemire
c8b3ca0d12
Merge pull request #167 from deadalnix/smallest_power_of_ten
Change smallest_power_of_ten to -64 for floats.
2024-06-27 19:03:59 -04:00
zejal
85911abb59 added couple of comments 2024-05-13 19:55:46 +02:00
zejal
1f9b1a6706 from_chars_advanced overload function taking parsed_number_string_t 2024-05-13 14:24:48 +02:00
Daniel Lemire
1fc3ac3932
Merge pull request #245 from fastfloat/removing_dead_function
removing unused function
2024-03-20 16:27:44 -04:00
Daniel Lemire
56a0092895 removing unused function 2024-03-20 14:24:16 -04:00
Daniel Lemire
7c19cafa93
Update README.md 2024-03-19 17:07:43 -04:00
Daniel Lemire
f476bc713f patch 2024-03-17 09:41:36 -04:00
Daniel Lemire
3b54a78805
Merge pull request #243 from matthew-wozniczka/patch-1
Add a missing check that __uint128_t exists before using it.
2024-03-17 09:40:39 -04:00
Matthew Wozniczka
9ab4ac837b
Add a missing check that __uint128_t exists before using it.
I noticed a compilation error when building a 64-bit binary with this library while using xlclang on AIX, and this change seems to fix it.
2024-03-16 12:18:20 -07:00
Daniel Lemire
bafd9d9c5f adding release script. 2024-03-08 18:15:20 -05:00
Daniel Lemire
9befad4626 adding release script 2024-03-08 18:10:15 -05:00
Daniel Lemire
2f3ed44e06
Merge pull request #241 from carlosal1015/fix/version-numbering
Use version 6.1.0 instead 6.0.0
2024-03-08 11:28:25 -05:00
Carlos Aznarán
131b1f9768
Use version 6.1.0 instead 6.0.0 2024-03-08 08:46:50 -05:00
Daniel Lemire
ff7cb44263
Create SECURITY.md 2024-02-09 17:18:36 -05:00
Daniel Lemire
4b035185e7
Merge pull request #239 from wojdyr/main
check C++ standard version before including <stdfloat>
2024-02-03 11:30:06 -05:00
Marcin Wojdyr
55a5b3c8e1 check C++ standard version before including <stdfloat>
fixes #238
2024-02-03 15:05:30 +01:00
Daniel Lemire
8378916ed8
Merge pull request #237 from fastfloat/release610_candidate
preparing version 6.1.0
2024-01-28 16:17:51 -05:00
Daniel Lemire
797e3e0bc2 more fixes 2024-01-28 16:01:46 -05:00
Daniel Lemire
12ece3c84f removing leftover print 2024-01-28 15:20:41 -05:00
Daniel Lemire
a0ea962bf5 more fixes 2024-01-28 15:18:40 -05:00
Daniel Lemire
4dcbd30d3f More robust tests for C++23 2024-01-28 15:00:27 -05:00
Daniel Lemire
eb584f748e explicit cast 2024-01-28 13:29:55 -05:00
Daniel Lemire
9da74ca5fb minor fixes 2024-01-28 12:49:00 -05:00
Daniel Lemire
3ab68b69d1 preparing version 6.1.0 2024-01-28 11:51:28 -05:00
Daniel Lemire
fade2357ba fix missing typename 2024-01-28 11:46:27 -05:00
Daniel Lemire
c7e45fea9f
Merge pull request #229 from MTahaK/main
Support for float32_t and float64_t
2024-01-28 11:43:54 -05:00
Daniel Lemire
a642af5235
Merge pull request #236 from fastfloat/issue235
Verify and fix issue 235
2024-01-28 11:43:35 -05:00
Daniel Lemire
7977ec6054 fix 2024-01-28 11:11:19 -05:00
Daniel Lemire
9be7de5998 adding sanitized tests 2024-01-28 10:56:20 -05:00
Daniel Lemire
5334e2ba94 fix 2024-01-28 10:55:02 -05:00
Daniel Lemire
1d97749791 Verify and fix issue 235 2024-01-28 10:47:21 -05:00
Daniel Lemire
620376ad39
Merge branch 'main' into main 2024-01-28 10:09:56 -05:00
Daniel Lemire
f320619216
Merge pull request #227 from fastfloat/extra_test
adding a single test
2024-01-28 10:08:49 -05:00
Daniel Lemire
7cbbcee6ed
Merge pull request #233 from StefanBruens/optional_supplemental_tests
Make tests depending on supplemental_test_files optional
2024-01-28 10:08:33 -05:00
Daniel Lemire
68b9475585
Merge pull request #234 from beached/patch-1
Update float_common.h
2023-12-28 08:16:38 -05:00
Darrell Wright
b43f808190
Update float_common.h
The construct !! is a no-op for a bool, op< for uint64_t's.  Removed it and made it an explicit cast to match the operations being performed
2023-12-27 19:43:56 -05:00
Daniel Lemire
b90ba259ef
Update README.md 2023-12-26 16:10:05 -05:00
Daniel Lemire
5c2a4a026a
Update README.md 2023-12-26 16:09:07 -05:00
StefanBruens
7f46adc19c Make tests depending on supplemental_test_files optional
As the supplemental_test_files are quite large, it is useful to make
running the tests depending on it optional.

By default, the tests are kept enabled, but can be switched of by setting
`FASTFLOAT_SUPPLEMENTAL_TEST=OFF`.

Fixes: #232
2023-12-26 02:23:30 +01:00
Daniel Lemire
f3ff46fd40 version bump 2023-12-14 17:56:17 -05:00
Daniel Lemire
e59e8f547a adding space 2023-12-14 17:54:39 -05:00
Daniel Lemire
8b849ebab0 Documentation regarding the integer types 2023-12-14 17:53:30 -05:00
Daniel Lemire
ede1d6b69e
Merge pull request #231 from mayawarrier/main
from_chars integer parser
2023-12-14 17:34:56 -05:00
Maya Warrier
882a716c12
Explicit curly bracket where suggested
Co-authored-by: Daniel Lemire <daniel@lemire.me>
2023-12-14 16:28:23 -05:00
Maya Warrier
a59a62cb5c
Merge branch 'fastfloat:main' into main 2023-12-13 17:54:05 -05:00
Maya Warrier
bdee16bcad - Add SIMD acceleration to fast_int
- fix MSVC warning
2023-12-13 17:42:30 -05:00
TheRandomGuy146275
26a5b2eb16 Added test case for ignoring leading zeros for all bases
- added: fix incorrect base for leading zeros test

---------

Co-authored-by: Marvin <marvin.wu@mail.utoronto.ca>
Co-authored-by: Maya Warrier <34803055+mayawarrier@users.noreply.github.com>
2023-12-13 17:22:51 -05:00
Maya Warrier
36aaded3dd Fix handling of leading zeros 2023-12-13 17:11:41 -05:00
Daniel Lemire
506b01a236
Merge pull request #230 from mayawarrier/fix-arm-neon-macro
Fix for ARM SIMD
2023-12-13 10:43:18 -05:00
Maya Warrier
1e0a9da538 Change FASTLOAT_ARM64 macro to FASTFLOAT_NEON 2023-12-13 00:56:35 -05:00
MTahaK
9c8891eed1 Update contributors 2023-12-13 00:41:28 -05:00
MTahaK
96c0059fe9 Revised implementation of float32_t and float64_t support as template specializations, added test 2023-12-13 00:33:16 -05:00
Maya Warrier
30b3165520 Fix fast_int test Werrors 2023-12-12 22:45:14 -05:00
Maya Warrier
a30fe866f6 Fix bugs highlighted in tests 2023-12-12 22:35:58 -05:00
Maya Warrier
01e8c50a33 Fix bugs in tests/fast_int.cpp 2023-12-12 22:35:34 -05:00
Maya Warrier
5fcc62a6f5 Merge contributed tests 2023-12-12 22:03:33 -05:00
Maya Warrier
624ba49434 Fix more Werrors
- Werror=conversion,char-subscripts
2023-12-12 21:26:48 -05:00
Marvin
ebc15bec51 Added test case for numbers within range after converted from base 2023-12-12 20:20:05 -05:00
Marvin
0711006266 Fixed messages 2023-12-12 19:23:43 -05:00
MTahaK
ae99db48e6 Updated CMakeLists with new option for C++23 fixed width tests, added new basic fixedwidthtest file. 2023-12-12 19:19:06 -05:00
Marvin
681eb1ea38 More details to basic test error for debugging 2023-12-12 19:18:07 -05:00
Marvin
5fda2cc240 Debugging results 2023-12-12 19:07:51 -05:00
MTahaK
4d8a2f4000 Fixed typo. 2023-12-12 19:02:10 -05:00
MTahaK
f330ec1e9d Reverted CMakeLists changes, modifed workflow for g++13 & C++23. 2023-12-12 18:59:59 -05:00
TheRandomGuy146275
f904694d5d
Merge pull request #1 from mayawarrier/main
merge fast_float with testing
2023-12-12 18:46:42 -05:00
MTahaK
d10980bc9f Added conditions to include stdfloat, and new workflow for GCC 13 2023-12-12 18:46:38 -05:00
MTahaK
62a4129a1c Fixed ifdef macro 2023-12-12 18:18:13 -05:00
MTahaK
eb844a1ad9 Modified cmakelist to use C++23. Fixed ifdef for fixed-width float types 2023-12-12 18:16:27 -05:00
MTahaK
b0f4535781 Update test/CMakeLists.txt to use C++23 for fixed-width float types 2023-12-12 18:11:09 -05:00
MTahaK
cbbbc75c88 Fixed ifdef checks (incorrect syntax) 2023-12-12 17:59:04 -05:00
TheRandomGuy146275
e4702e039f Fixing cmake errrors 2023-12-12 17:58:32 -05:00
MTahaK
d8dbbc54ab Updated comment for float32_t and float64_t conversions in from_chars 2023-12-12 17:56:14 -05:00
MTahaK
f74e338e0a Added trivial support for float32_t and float64_t 2023-12-12 16:38:48 -05:00
Marvin
20c9375c5e Added new test cases for out of range errors that cover 8,16,32,64 bits, out of range errors for all bases (64 bit only), and fixed some test cases 2023-12-12 13:59:18 -05:00
Maya Warrier
7a21a8d6d7 Return invalid_argument in more places 2023-12-12 02:36:18 -05:00
Maya Warrier
3d446f1eba Fix gcc werrors 2023-12-12 01:56:24 -05:00
Marvin
e60b47455b Added test cases that coverunsigned, out or range errors, char pointers, invalid bases, and out of range bases 2023-12-11 18:17:33 -05:00
Maya Warrier
c9527c2e4f Skip leading zeros 2023-12-11 04:27:22 -05:00
Maya Warrier
122220e2f0 Version 1 of from_chars integer parser 2023-12-11 04:17:26 -05:00
Marvin
2fb59699db Added more test cases that checks for correct errors, base 2, octal, and hex 2023-12-11 01:22:48 -05:00
Marvin
a91521f5a0 Added basic tests for fast_int in fast_int.cpp 2023-12-10 23:40:27 -05:00
Daniel Lemire
604424b624 adding a test 2023-12-06 21:17:09 -05:00
Daniel Lemire
35d523195b
Merge pull request #225 from fastfloat/fix_win32_ci
fix for 32-bit Visual Studio when not set to round to nearest
2023-11-27 16:21:01 -05:00
Daniel Lemire
736561cd99
Merge pull request #224 from Coeur/coeur/input_num
float_common.h:237:52 Value stored to 'input_num' is never read
2023-11-27 15:51:37 -05:00
Daniel Lemire
fda97168f0 casting to avoid warnings 2023-11-27 15:50:47 -05:00
Daniel Lemire
70d8e0ad0e In some cases, Visual Studio, when compiling 32-bit binaries, gets
0*something == -0 even when the 'something' is positive, when the system
is not set to compile to nearest.
2023-11-27 15:44:24 -05:00
Antoine Cœur
c325db1b04 float_common.h:237:52 Value stored to 'input_num' is never read 2023-11-25 18:45:15 +01:00
Daniel Lemire
80400d270e Deactivate Win32 debug ci (VS) 2023-11-13 11:25:19 -05:00
Daniel Lemire
1e2e43c088
Update README.md 2023-11-13 11:14:13 -05:00
Daniel Lemire
c8b8321fd4 bumping version 2023-11-13 10:44:24 -05:00
Daniel Lemire
1dfad243ec
Merge pull request #221 from fastfloat/fortran
Fortran support (with fixes)
2023-09-15 09:51:24 -04:00
Daniel Lemire
7646f819a8 Merge branch 'main' of github.com:allenbarnett5/fast_float_ftn into fortran 2023-09-15 09:43:27 -04:00
Daniel Lemire
e6b370dc2c
Merge pull request #220 from mayawarrier/main
Add support for parsing numbers according to JSON format
2023-09-15 09:18:39 -04:00
Maya Warrier
7b1fc2f95d Add an option to allow inf/nan even in json mode
- Most JSON parsers offer this option too
2023-09-14 21:07:22 -04:00
Maya Warrier
ce562d9c65 Disallow inf/nan in json mode 2023-09-14 20:51:26 -04:00
Maya Warrier
2395482ad5 Ignore FASTFLOAT_ALLOWS_LEADING_PLUS for JSON format 2023-09-14 19:50:21 -04:00
Daniel Lemire
cd5160a007 Adding more tests. 2023-09-14 10:32:59 -04:00
Daniel Lemire
a79f2d9841 More tests. 2023-09-14 10:28:08 -04:00
Daniel Lemire
2eb7b2ebda Adding a couple of tests. 2023-09-14 10:13:55 -04:00
Maya Warrier
4de8d715e6 Add json fmt test 2023-09-13 21:07:40 -04:00
Maya Warrier
3f250c5a98 Use chars_format instead of parse_rules for parsing as JSON 2023-09-13 20:03:10 -04:00
Maya Warrier
396f41271f Implement JSON rules 2023-09-13 19:19:37 -04:00
allen.barnett@paperbirchsoftware.com
e22c027544 Allow fast_float to parse strings accepted by the Fortran internal read
function.
2023-08-07 08:39:06 -04:00
deadalnix
5167a5029d Change smallest_power_of_ten to -64 for floats.
`18446744073709551615e-65` rounds to `0`, and it is the largest possible value with exponent `-65`. Therefore, we can assume the minimal exponent for which we don't round to zero actually is `-64`.
2023-07-12 14:27:46 +00:00
Daniel Lemire
6a390f63e9
Merge pull request #217 from DavidKorczynski/cifuzz-int
Add CIFuzz workflow
2023-07-12 09:54:19 -04:00
David Korczynski
9f913bd835 Add CIFuzz workflow
Signed-off-by: David Korczynski <david@adalogics.com>
2023-07-11 14:29:31 -07:00
Daniel Lemire
33a2f8f203
Merge pull request #214 from Coeur/coeur/leading_zeroes_generic
float_common.h: fix possible misuse of comma operator
2023-07-10 10:59:05 -04:00
Cœur
3e90425e3c code review: FASTFLOAT_CONSTEXPR14 2023-06-12 22:27:31 +02:00
Cœur
f5f64e46cd float_common.h: fix possible misuse of comma operator 2023-06-12 21:05:51 +02:00
Daniel Lemire
84e2cb27e0
Merge pull request #213 from mayawarrier/msvc-v140-fix
Fix for broken MSVC v140 without removing enable_if
2023-06-12 10:55:01 -04:00
Maya Warrier
c5ecb5db9b Fix for MSVC v140 2023-06-11 22:22:06 -04:00
Daniel Lemire
2b2395f9ac Bumping version. 2023-06-10 10:49:39 -04:00
Daniel Lemire
c80ff7c18a
Merge pull request #212 from fastfloat/issue211
Renaming the using for the local type.
2023-06-10 10:49:03 -04:00
Daniel Lemire
a14f8c587e Fix. 2023-06-10 10:48:46 -04:00
Daniel Lemire
eea6fef9a1 tweak 2023-06-10 09:47:48 -04:00
Daniel Lemire
71c2ad1863
Merge pull request #210 from Pharago/main
Fix broken msvc v140
2023-06-10 09:46:54 -04:00
Daniel Lemire
ba328bbf7d Renaming the using for the local type. 2023-06-10 09:44:49 -04:00
Pharago
677231869b Revert "Update .gitignore"
This reverts commit 66740ad46bf0096c3750a9686ec8ec8286adcd92.
2023-06-10 06:23:29 +02:00
Pharago
66740ad46b Update .gitignore 2023-06-10 05:50:18 +02:00
Pharago
b8e3307fbd Fix broken msvc v140
When using msvc v140 (Visual Studio 2015 Toolset), compilation fails.
2023-06-10 05:46:30 +02:00
Daniel Lemire
91edc90464
Merge pull request #209 from fastfloat/arm_neon_opti
ARM NEON optimization (UTF-16 inputs)
2023-06-09 14:41:38 -04:00
Daniel Lemire
3cab724b80 Minor fixes 2023-06-09 12:30:20 -04:00
Daniel Lemire
b88345d06d Fixes 2023-06-09 11:28:51 -04:00
Daniel Lemire
e8c0378d6d Adding ARM NEON optimizations. 2023-06-09 11:19:34 -04:00
Daniel Lemire
bc98c5dfdf
Merge pull request #208 from lemire/other_chars
Bumping version.
2023-06-09 10:54:39 -04:00
Daniel Lemire
b1636bad07 Bumping version. 2023-06-09 10:43:36 -04:00
Daniel Lemire
8139e164b8
Merge pull request #198 from mayawarrier/main
Add opt-in SIMD support for char16_t
2023-06-09 10:40:04 -04:00
Daniel Lemire
127a6c7cbc
Merge pull request #207 from v1gnesh/main
Add zOS support to float_common.h
2023-06-09 10:39:22 -04:00
Daniel Lemire
f0e6182f67
Update README.md 2023-06-05 12:35:05 -04:00
v1gnesh
bb2ae14fe6
Add zOS support to float_common.h 2023-06-05 19:16:46 +05:30
Daniel Lemire
3a63b00d53
Merge pull request #206 from zangruochen/main
float_common.h: Support LoongArch64
2023-05-30 09:18:51 -04:00
zangruochen
fcee052311 Support LoongArch64 2023-05-30 15:16:27 +08:00
Daniel Lemire
c66df08ede
Merge pull request #205 from alugowski/onrelease
Add on-release workflow
2023-05-26 19:55:52 -04:00
Adam Lugowski
6951df1b19 Add on-release workflow 2023-05-26 16:41:12 -07:00
Daniel Lemire
f5a3e774ed
Merge pull request #203 from fastfloat/adding_boost
Adding BOOST License.
2023-05-25 14:36:10 -04:00
Daniel Lemire
c9b908bf97 Adding BOOST License. 2023-05-25 11:05:12 -04:00
Daniel Lemire
b7cb8148bb
Merge pull request #202 from fastfloat/cxx20ci
Adding C++20 tests in CI.
2023-05-23 11:15:41 -04:00
Daniel Lemire
92061ebe31 Pruning bad code. 2023-05-23 09:55:35 -04:00
Daniel Lemire
6d223ab557 Adding C++20 tests in CI. 2023-05-23 09:41:23 -04:00
Maya Warrier
b7119471b1
Update CONTRIBUTORS 2023-05-22 13:22:46 -04:00
Maya Warrier
a5632d5b57 Fix digit comparison, cleanup 2023-05-20 12:29:24 -04:00
Maya Warrier
cb397ef446 Fix for clang (missing _mm_cvtsi128_si64x) 2023-05-17 05:40:29 -04:00
Maya Warrier
1c9a3088bf Fix for VS 32-bit 2023-05-17 05:31:15 -04:00
Maya Warrier
064d2b832d Fix Werrors 2023-05-17 02:27:48 -04:00
Maya Warrier
38613a39f9 Fix perf decrease when UC = char 2023-05-17 01:34:33 -04:00
Maya Warrier
6ede038789 Apply changes from benchmarked version
- Move parse_truncated_number_string back inside parse_number_string
2023-05-09 22:19:23 -04:00
Daniel Lemire
396a0fc2ae
Merge branch 'main' into main 2023-05-08 16:44:35 -04:00
Daniel Lemire
cc1e01e9ee
Merge pull request #199 from mayawarrier/cmake_intellisense_fix
Fix for broken VS Intellisense
2023-05-08 16:43:45 -04:00
Daniel Lemire
ff77380a76
Merge pull request #200 from leni536/fix_static_tables
Make tables external linkage
2023-05-08 16:38:20 -04:00
Lenard Szolnoki
ddaefc0bbf Fix multiple definition linker errors for tables 2023-05-08 15:24:11 +01:00
Lenard Szolnoki
cc042ae409 Make lookup tables external linkage 2023-05-07 23:07:34 +01:00
Maya Warrier
53b065f38d Avoid redundant load in SSE2 code 2023-05-07 17:38:32 -04:00
Maya Warrier
db7991a612 amalmagate.py header inclusion order 2023-05-07 17:04:02 -04:00
Maya Warrier
4e7ae339d6 Implement intellisense fix 2023-05-07 00:38:10 -04:00
Maya Warrier
4cb09b5f59 Automatically detect SSE2 2023-05-02 13:05:57 -04:00
Maya Warrier
c811b027ea Remove testing macro 2023-05-02 01:52:00 -04:00
Maya Warrier
5136b181ba Fixes and cleanup 2023-05-02 01:41:49 -04:00
Maya Warrier
680ccc73ed Merge from upstream/main, fix conflicts 2023-05-01 20:27:29 -04:00
Maya Warrier
e08c55c380 Remove json parse rules/allow inf_nan 2023-05-01 19:45:50 -04:00
Daniel Lemire
fe571b1da7
Merge pull request #197 from lemire/other_chars
Support UTF-16 and UTF-32 inputs
2023-05-01 09:28:16 -04:00
Maya Warrier
091458d192 Add basic support for char32_t (unoptimized) 2023-04-30 02:23:33 -04:00
Daniel Lemire
1aba54bac2 Fixing version regression. 2023-04-26 19:46:06 -04:00
Daniel Lemire
8199baeb70 Slightly less ugly code. 2023-04-26 18:46:19 -04:00
Daniel Lemire
225df8c934
Merge branch 'fastfloat:main' into other_chars 2023-04-26 18:32:06 -04:00
Daniel Lemire
5223d7a460 address issues raised by @mayawarrier 2023-04-26 18:25:27 -04:00
Daniel Lemire
545c184596 Stack!!! 2023-04-26 16:53:44 -04:00
Maya Warrier
65bd922e38 Merge remote-tracking branch 'upstream/main'
- Fix conflicts
2023-04-26 16:47:42 -04:00
Daniel Lemire
acce5b4158 Documentation. 2023-04-26 16:46:09 -04:00
Daniel Lemire
927eb9bcd2 Merge branch 'main' of https://github.com/Pharago/fast_float into other_chars 2023-04-26 16:40:09 -04:00
Maya Warrier
89fc24007a Clean up 2023-04-26 16:25:41 -04:00
Daniel Lemire
dc88f6f882
Merge pull request #196 from fastfloat/dlemire/issue195
Removing dead code.
2023-04-26 15:39:15 -04:00
Daniel Lemire
1b9afafed5 Removing old CI 2023-04-21 15:56:56 -04:00
Maya Warrier
653790b5f3 fixes 2023-04-16 00:36:52 -04:00
Maya Warrier
c849b7a8ff Option to forbid nan/inf, refactor 2023-04-15 23:16:01 -04:00
Daniel Lemire
11eeab5463 Removing dead code. 2023-04-11 14:29:30 -04:00
Pharago
ece3b3886b Fix storage class errors on non msvc builds
Removed storage class declarations from explicit template specializations of string constants
2023-04-06 20:58:31 +02:00
Pharago
2bfbe4ca96 cosmetic changes 2023-04-06 00:58:34 +02:00
Pharago
764341c1d9 Revert "Update CONTRIBUTORS"
This reverts commit d833148d7ebc51c3549adb834937759040dfcc15.
2023-04-06 00:41:11 +02:00
Pharago
593709f056
Merge branch 'main' into main 2023-04-05 03:31:35 +02:00
Daniel Lemire
24374ece71
Update README.md 2023-04-04 14:27:06 -04:00
Daniel Lemire
ad3ff05751
Merge pull request #194 from aras-p/main
Fix warnings with -Wundef
2023-04-04 14:24:54 -04:00
Aras Pranckevičius
21fefa5b44
Fix warnings with -Wundef
- FASTFLOAT_ALLOWS_LEADING_PLUS and FASTFLOAT_SKIP_WHITE_SPACE are not defined by default, and compiling with -Wundef is emitting warnigns like "FASTFLOAT_ALLOWS_LEADING_PLUS is not defined, evaluates to 0".
- Likewise for FASTFLOAT_VISUAL_STUDIO, change checks to use #ifdef for that like in other places.
- __cpp_lib_bit_cast and __cpp_lib_is_constant_evaluated are not defined pre-C++20, and are emitting a warning too.
2023-04-04 21:18:57 +03:00
Pharago
148606637c
Merge branch 'fastfloat:main' into main 2023-04-03 02:25:46 +02:00
Daniel Lemire
f34e880ece
Merge pull request #193 from fastfloat/issue_191
We need to update some of our exhaustive tests to the new API
2023-04-02 19:44:03 -04:00
Pharago
1db8f62f8b
Merge branch 'fastfloat:main' into main 2023-04-02 23:49:31 +02:00
Daniel Lemire
ca43e6722e We need to update some of our exhaustive tests to the new API 2023-04-02 17:43:17 -04:00
Daniel Lemire
843fb97064
Merge pull request #190 from alugowski/fix_subnormal_tests
Fix subnormal test fails on GCC 9
2023-04-02 17:21:16 -04:00
Pharago
d833148d7e Update CONTRIBUTORS 2023-04-02 23:08:14 +02:00
Pharago
bc77f956e2 Initial Unicode release
Added support for the other char types
2023-04-02 22:58:01 +02:00
Maya Warrier
cda25408bc Optimize char16_t parsing for digit_comparison.h 2023-04-02 00:33:52 -04:00
Maya Warrier
2d57c09530 Fixes 2023-04-01 23:29:00 -04:00
Maya Warrier
8a9a9d538a SIMD optimization to parse 8 char16_t at a time 2023-04-01 22:43:00 -04:00
Maya Warrier
f59f73c4da Disable simd-related warnings 2023-04-01 04:09:00 -04:00
Adam Lugowski
ca90e36413 Fix subnormal test fails on GCC 9 2023-03-31 17:09:36 -07:00
Daniel Lemire
fbd5bd712e Bumping version 2023-03-30 18:37:48 -04:00
Daniel Lemire
c6303d3593 Merge branch 'main' of github.com:fastfloat/fast_float 2023-03-30 18:36:18 -04:00
Daniel Lemire
d41d507e8c
Merge pull request #189 from alugowski/out_of_range
Set errc::result_out_of_range on over/underflow
2023-03-30 18:35:12 -04:00
Adam Lugowski
37127b022f Add subnormal numbers to powers of ten table
On some platforms std::pow returns 0 instead of a subnormal number with `-ffast-math -O2` compiler options.
2023-03-30 13:14:35 -07:00
Maya Warrier
20f3870361 Fixes 2023-03-30 04:58:22 -04:00
Maya Warrier
2b118c843a Experimental support for char_t types 2023-03-30 04:48:18 -04:00
Maya Warrier
a699476fd2 ignore VS CMakeSettings file 2023-03-30 04:47:42 -04:00
Maya Warrier
b6acf38a2e Fix bugs 2023-03-29 18:42:24 -04:00
Adam Lugowski
bfee511d78 Set errc::result_out_of_range on over/underflow
Best-effort values are still returned, such as 0 for underflow and infinity for overflow, but now the returned ec is set to std::errc::result_out_of_range instead of std::errc().
2023-03-29 10:14:46 -07:00
Maya Warrier
3cafcca2ff Add support for json parsing rules and integers 2023-03-29 02:14:12 -04:00
Maya Warrier
8f94758c78 Expose parsed string (before computation) so it can be reused 2023-03-27 22:50:21 -04:00
Maya Warrier
7385c2053b Update .gitignore 2023-03-27 22:17:02 -04:00
Daniel Lemire
fde0116d4d New version. 2023-03-27 13:53:18 -04:00
Daniel Lemire
0cdf016847
Merge pull request #188 from fastfloat/dlemire/compile_time
Compile-time evaluation
2023-03-27 13:51:27 -04:00
Daniel Lemire
d7ba016c73 Fix. 2023-03-27 13:46:51 -04:00
Daniel Lemire
0394ea1fed Compile-time evaluation 2023-03-27 13:36:46 -04:00
Daniel Lemire
3ada7ca4d9
Merge pull request #187 from fastfloat/dlemire/enable_tests_automatically
If we detect that the compiler supports FASTFLOAT_IS_CONSTEXPR, then let us run the tests automatically.
2023-03-27 13:09:56 -04:00
Daniel Lemire
a662df817f If we detect that the compiler supports FASTFLOAT_IS_CONSTEXPR, then let us run the tests automatically. 2023-03-26 20:43:33 -04:00
Daniel Lemire
ab6c987ebc
Merge pull request #185 from fastfloat/dlemire/adding_more_cxx20_in_ci
Adding more CXX20 testing in CI.
2023-03-26 20:32:23 -04:00
Daniel Lemire
c8bba6ab29 Fix CI issue 2023-03-26 07:22:07 -04:00
Daniel Lemire
5a6b371739 Let us turn FASTFLOAT_CONSTEXPR_TESTS ON 2023-03-25 17:34:38 -04:00
Daniel Lemire
c52769174b Merge branch 'main' into dlemire/adding_more_cxx20_in_ci 2023-03-25 17:31:36 -04:00
Daniel Lemire
b50a729d93
Merge pull request #182 from leni536/constexpr-from_chars
Constexpr from_chars
2023-03-25 17:30:55 -04:00
Daniel Lemire
c09c855ea6
Merge pull request #186 from leni536/negative_zero_clang
Fix parsing -0 on clang in non-nearest rounding modes.
2023-03-25 16:03:13 -04:00
Lenard Szolnoki
5b8290433c Fix clang workaround for parsing -0 on non-nearest rounding mode 2023-03-25 19:38:44 +00:00
Lenard Szolnoki
360977f968 Add negative zero parsing test for all rounding modes.
This fails on clang.
2023-03-25 19:28:05 +00:00
Lenard Szolnoki
a1a7c4e3e0 Extend verify_options with constexpr test 2023-03-25 18:28:00 +00:00
Daniel Lemire
845d6575dc Adding more CXX20 testing in CI. 2023-03-24 14:24:38 -04:00
Lenard Szolnoki
e464bd7785 Replace memmove with copy_backward 2023-03-24 17:18:58 +00:00
Lenard Szolnoki
6bd1e776b2 Constexpr isnan 2023-03-24 16:35:25 +00:00
Lenard Szolnoki
24b2fdaad0 Separate verify and verify_runtime macros, extend constexpr checks
`verify` runs both runtime and constexpr checks if the constexpr checks
are enabled in cmake.

`verify_runtime` only runs the runtime checks.
2023-03-24 16:18:00 +00:00
Daniel Lemire
8cb0590c02
Merge pull request #184 from silvergasp/main
Adds a simple fuzz test
2023-03-12 20:50:58 -04:00
Nathaniel Brough
e702fba365 Adds simple fuzz test 2023-03-12 11:51:50 -07:00
Lenard Szolnoki
264414c0b0 Remove default capture with init-capture in hope of fixing MSVC build. 2023-03-12 15:17:14 +00:00
Lenard Szolnoki
612a7bf5eb Enable constexpr tests for VS17 c++20 build. 2023-03-12 11:40:20 +00:00
Lenard Szolnoki
d34d0d7405 Fix amalgamate.ph and add header for constexpr macros 2023-03-12 10:50:25 +00:00
Daniel Lemire
6a3fabc342 Preparing release 2023-03-06 21:02:19 -05:00
Daniel Lemire
eb8c001201
Merge pull request #183 from redis-performance/fix.parse_infnan.plus.inf
Added missing FASTFLOAT_ALLOWS_LEADING_PLUS ifdef check in parse_infnan
2023-03-06 20:52:43 -05:00
filipecosta90
c8886eb31d Added missing FASTFLOAT_ALLOWS_LEADING_PLUS ifdef check in parse_infnan 2023-03-07 00:31:14 +00:00
Lenard Szolnoki
ffc3fd7cc7 Fix amalgamate.py script
This unfortunately puts fast_float.h as the second header,
possiby making the amalgamated header less readable.
2023-03-04 23:30:56 +00:00
Lenard Szolnoki
e05858a0f8 Work around clang bug.
https://godbolt.org/z/zedh7rrhc

This is similar to https://github.com/llvm/llvm-project/issues/47746,
except I needed to use a different workaround.
2023-03-04 23:02:15 +00:00
Lenard Szolnoki
6732e397d8 Add constexpr testing
When enabled, modify `verify` macro to also verify at compile time,
when the arguments are constant expressions.
2023-03-04 22:36:58 +00:00
Daniel Lemire
808cd24919
Merge pull request #181 from leni536/patch-1
Add my name to CONTRIBUTORS
2023-03-04 17:32:38 -05:00
Lenard Szolnoki
58798ee81f Fix failing test builds with -Werror=maybe-uninitialized on gcc
Apparently the added constexpr makes gcc's control flow analysis to go deeper.

If from_chars returns with error then the out parameter remains
uninitialized. As it's undefined behavior to use its value, it's better
to just skip the rest of the loop in this case.
2023-03-04 20:53:49 +00:00
Lenard Szolnoki
e4d4e43b21 Constexpr from_chars 2023-03-04 20:53:43 +00:00
Lenard Szolnoki
82ee3b1b5f Constexpr parse_number_string 2023-03-04 17:18:25 +00:00
Lénárd Szolnoki
88c3b362f1
Add my name to CONTRIBUTORS 2023-03-04 09:25:12 +00:00
Daniel Lemire
c487a69c1b
Merge pull request #180 from leni536/constexpr-big_int
Constexpr big_int
2023-03-03 19:26:29 -05:00
Lenard Szolnoki
5243dd97fe Constexpr bigint 2023-03-03 23:13:52 +00:00
Lenard Szolnoki
52618851fd Make all float_common.h functions constexpr in C++20 2023-03-03 22:43:52 +00:00
Daniel Lemire
a3e00eed59
Merge pull request #179 from leni536/simplify-to_float
Simplify to_float
2023-03-02 20:16:23 -05:00
Lenard Szolnoki
6d2fb68f5c Simplify to_float
* Use right-sized uint type for bit fiddling
** This removes the need to special casing on endianness
* Replace ternary with just shifting the sign at the right place
** This seems to improve codegen (less instructions, no cmov)
2023-03-01 23:39:01 +00:00
Daniel Lemire
b1d27734c5
Merge pull request #173 from fastfloat/nofallbacknecessary
We do not need a fallback.
2023-02-28 17:19:57 -05:00
Daniel Lemire
a64cfc960b
Merge pull request #178 from lemire/dlemire/adding_bloat_analysis
This adds bloat analysis to the tests.
2023-02-28 17:19:25 -05:00
Daniel Lemire
06333da7fe
Merge branch 'main' into dlemire/adding_bloat_analysis 2023-02-28 17:10:05 -05:00
Daniel Lemire
52fed52d94
Merge pull request #177 from leni536/easy-constexpr
Low-risk C++11 and C++14 constexpr functions
2023-02-28 17:08:37 -05:00
Daniel Lemire
7f1c4a2f7f This adds bloat analysis to the tests. 2023-02-28 16:42:48 -05:00
Daniel Lemire
3f3fc935e0
adding reference to upcoming paper 2023-02-28 10:17:08 -05:00
Lenard Szolnoki
e320512755 Fix power_of_five_128 to be usable in constexpr in MSVC, clang 2023-02-25 18:34:22 +00:00
Lenard Szolnoki
0e4b873d81 Fix space_lut so it's accepted by MSVC and clang 2023-02-25 18:27:10 +00:00
Lenard Szolnoki
a6991ea44f Add comment to the FASTFLOAT_CONSTEXPR14 macro definition 2023-02-25 11:11:09 +00:00
Lenard Szolnoki
be6084863c Low-risk C++14 constexpr functions 2023-02-25 10:50:45 +00:00
Daniel Lemire
3fd4c1b507 Bumping the version. 2023-02-06 17:41:15 -05:00
Daniel Lemire
f4efe3ccb0
Merge pull request #175 from fastfloat/dlemire/broader_alpine
Trying to extend alpine CI to more systems and fixing legacy issue (x86)
2023-02-06 17:39:27 -05:00
Daniel Lemire
252a1c9dce Minor fix. 2023-02-06 17:22:11 -05:00
Daniel Lemire
764a064c12 Adding back x86 2023-02-06 14:48:41 -05:00
Daniel Lemire
be2e6bb693 Typo. 2023-02-06 14:42:29 -05:00
Daniel Lemire
18349287f4 Trying to extend alpine. 2023-02-06 14:39:21 -05:00
Daniel Lemire
39e04ea766 We do not need a fallback. 2023-01-27 20:59:56 -05:00
Daniel Lemire
69e0ea6f8a
Merge pull request #172 from fastfloat/dlemire/adding_390
Adding big-endian runner
2023-01-27 11:32:33 -05:00
Daniel Lemire
7f86e5ad55 Fixing branch name 2023-01-26 23:09:12 -05:00
Daniel Lemire
57536631af Adding token 2023-01-26 23:01:41 -05:00
Daniel Lemire
033536dbba Adding big-endian runner 2023-01-26 22:48:05 -05:00
Daniel Lemire
7a6fe5ee79 Bumping the version. 2023-01-19 20:54:23 -05:00
Daniel Lemire
e84f7e7850
Merge pull request #170 from fastfloat/dlemire/rcpp
Support rccpfastfloat via macros
2023-01-19 20:50:46 -05:00
Daniel Lemire
3e2da540ef Support rccpfastfloat. 2023-01-19 20:28:10 -05:00
Daniel Lemire
34d443d6fc Bumping version. 2023-01-18 18:27:19 -05:00
Daniel Lemire
e7d25fb058
Merge pull request #169 from barracuda156/main
float_common.h: add support for ppc32
2023-01-18 18:26:07 -05:00
Sergey Fedorov
ff7fba01d0
float_common.h: add support for ppc32 2023-01-18 14:15:14 +08:00
Daniel Lemire
1ea4f27b2a
Merge pull request #166 from fastfloat/dlemire/verify_normal_subnormal_limit
Adding test.
2023-01-10 11:20:27 -05:00
Daniel Lemire
09f89895f1 Adding test. 2023-01-10 10:32:59 -05:00
Daniel Lemire
177ef91fc9
Merge pull request #165 from fastfloat/dlemire/guard_endian
Guard endian include
2023-01-09 13:42:11 -05:00
Daniel Lemire
c8aac4a63d Guard endian 2023-01-07 13:28:12 -05:00
Daniel Lemire
e92c63295c
Update CMakeLists.txt 2023-01-04 12:54:20 -05:00
jpmag
02a61971f8
Merge pull request #162 from biojppm/fix/testexes
Fix compile warning: implicit type conversion
2022-12-27 12:20:06 +01:00
Joao Paulo Magalhaes
7f7838b36a Fix compile warning: implicit double->float type conversion
With Intel 2021.1:
```
/home/runner/work/c4core/c4core/src/c4/ext/fast_float_all.h:319:49: error: implicit conversion between floating point types of different sizes [-Werror,-Wimplicit-float-size-conversion]
constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3,
1e4, 1e5,
```
2022-12-27 11:09:17 +00:00
Joao Paulo Magalhaes
cb4779df3d Fix workflow for Ubuntu 20.04 2022-12-27 11:09:17 +00:00
Joao Paulo Magalhaes
0ba57912ff Enable tests with cross compilation 2022-12-27 11:09:17 +00:00
jpmag
98d46aee9c
Merge pull request #163 from biojppm/suppress_float_equal_warning
Suppress compile warning from float comparison
2022-12-27 12:06:51 +01:00
Joao Paulo Magalhaes
ca13367ff7 Suppress warning when comparing floats 2022-12-27 01:39:41 +00:00
Daniel Lemire
43e5c35d84
Merge pull request #161 from fastfloat/dlemire/adding_gcc12_tests
Adding GCC 12 tests (CI)
2022-12-23 10:46:31 -05:00
Daniel Lemire
102e74891f
Merge pull request #160 from huangqinjin/uint64-to-bool
Fix compile warning of unit64_t to bool
2022-12-23 10:39:33 -05:00
Daniel Lemire
6d0423720f
Merge pull request #159 from huangqinjin/replace-utf8-chars
Replace utf8 chars.
2022-12-23 10:38:50 -05:00
Daniel Lemire
7040160143 Adding GCC 12 2022-12-23 10:37:04 -05:00
huangqinjin
293ca61c76 Fix compile warning of unit64_t to bool 2022-12-23 19:20:28 +08:00
huangqinjin
9c4c20dd7f Replace utf8 chars. 2022-12-18 11:04:16 +08:00
Daniel Lemire
a4f8c86f08
Merge pull request #158 from fastfloat/dlemire/cleaning_runners
Cleaning our CI a bit.
2022-12-13 17:10:23 -05:00
Daniel Lemire
6f9da45af3 Cleaning our CI a bit. 2022-12-13 17:05:04 -05:00
Daniel Lemire
ae028f1aea
Update README.md 2022-12-11 14:53:00 -05:00
Daniel Lemire
11223155ce
Merge pull request #157 from kou/add-missing-end-namespace-comment
Add missing namespace end comments
2022-12-02 09:49:33 -05:00
Sutou Kouhei
ff5855813f Add missing namespace end comments
Other files have it.
2022-12-02 11:42:38 +09:00
Daniel Lemire
08c47edf57 Preparing release 2022-11-25 22:22:14 -05:00
Daniel Lemire
be962361c6
Merge pull request #156 from fastfloat/dlemire/fix_issue_154
Fixing issue 154.
2022-11-25 16:35:07 -05:00
Daniel Lemire
76537e1695 Fixing issue 154. 2022-11-25 15:58:54 -05:00
Daniel Lemire
cda3946a50
Merge pull request #155 from fastfloat/dlemire/adding_ubuntu22
Adding two new Ubuntu 22 runners in CI
2022-11-25 15:57:05 -05:00
Daniel Lemire
b6f1567d0b Adding two new runners 2022-11-25 15:53:50 -05:00
Daniel Lemire
2ef9abbcf6 Merge branch 'main' of github.com:fastfloat/fast_float 2022-11-23 10:51:45 -05:00
Daniel Lemire
9e868b3d1a Preparing release 2022-11-23 10:51:27 -05:00
Daniel Lemire
8f092d2799
Merge pull request #153 from fastfloat/dlemire/renabling_clinger
Conditional Clinger's fast path
2022-11-23 10:36:55 -05:00
Daniel Lemire
968bd9d86e Renaming the test. 2022-11-21 09:56:21 -05:00
Daniel Lemire
eec504ae03 Adding a fast-math test. 2022-11-21 09:53:49 -05:00
Daniel Lemire
003a983188 Simplifying the justification. 2022-11-18 15:38:21 -05:00
Daniel Lemire
8b7a55a03c Minor optimization. 2022-11-18 15:33:44 -05:00
Daniel Lemire
3d0e448940 Added a remark. 2022-11-18 12:27:38 -05:00
Daniel Lemire
39ea41b84a Adopting proposal. 2022-11-18 11:28:34 -05:00
Daniel Lemire
bfc0478feb More tweaks. 2022-11-16 16:45:01 -05:00
Daniel Lemire
29b1a03d5b Make sure that macros have actual values when defined (makes debugging easier) 2022-11-16 15:49:09 -05:00
Daniel Lemire
8f27b7e921 More tuning. 2022-11-16 15:42:56 -05:00
Daniel Lemire
fd9d9effda More tweaking around clangcl 2022-11-16 15:25:03 -05:00
Daniel Lemire
559b89d34d 32-bit clangcl appears to be ridiculous. 2022-11-16 14:59:03 -05:00
Daniel Lemire
d225059873 Fix for Win32+ClangCL 2022-11-16 14:35:31 -05:00
Daniel Lemire
95321767ee More verbose error report. 2022-11-16 12:29:46 -05:00
Daniel Lemire
2c8e738950 Cleaning. 2022-11-16 12:06:33 -05:00
Daniel Lemire
6ceb29a7e4 We might reenable clinger. 2022-11-16 16:21:34 +00:00
Daniel Lemire
dac641ee13 Preparing release 2022-11-15 12:07:56 -05:00
Daniel Lemire
eddf6dff43
Merge pull request #152 from fastfloat/dlemire/reducing_eight_digit_optimization
Trimming out one eight-digit optimization.
2022-11-15 12:06:40 -05:00
Daniel Lemire
6484c73696 Trimming out one eight-digit optimization. 2022-11-15 11:38:06 -05:00
Daniel Lemire
662497742f
Merge pull request #150 from fastfloat/dlemire/simplified_clinger
Simplified Clinger's fast path for increased portability
2022-11-03 19:52:17 -04:00
Daniel Lemire
a2cf502395 Typo. 2022-11-03 19:41:30 -04:00
Daniel Lemire
3e29bf78c7 Nicer constants. 2022-11-03 19:40:05 -04:00
Daniel Lemire
e958ff4269 Simplified clinger. 2022-11-03 18:51:37 -04:00
Daniel Lemire
c56e595438
Update README.md 2022-11-02 12:40:09 -04:00
Daniel Lemire
c7286b8ad1
Merge pull request #143 from OlivierLDff/floatfloat-install
🔨 Introduce FASTFLOAT_INSTALL to make install optional
2022-11-02 10:14:57 -04:00
Daniel Lemire
7396b0f8fa
Merge pull request #144 from OlivierLDff/cxx-standard
🔨 Don't write to global CMAKE_CXX_STANDARD
2022-11-02 10:14:25 -04:00
Daniel Lemire
32a659e033
Merge pull request #147 from kou/mingw
Don't use __umulh() with MinGW on ARM64
2022-10-28 09:36:15 -04:00
Sutou Kouhei
5a71e5bc40 Don't use __umulh() with MinGW on ARM64 2022-10-28 15:33:37 +09:00
Daniel Lemire
b4d7cceb93 Added a couple of tests. 2022-10-02 11:09:08 -04:00
Daniel Lemire
e71137f71c
Merge pull request #141 from striezel-stash/gh-action-update
update actions/checkout in GitHub Actions to v3
2022-09-26 09:55:25 -04:00
Daniel Lemire
f1c69acec4
Merge pull request #145 from OlivierLDff/alias
🔨 add library ALIAS FastFloat::fast_float
2022-09-26 09:53:50 -04:00
Olivier Le Doeuff
ab406c7c0b 🔨 add library ALIAS FastFloat::fast_float
This give same target name if library is found with find_package, or added via add_subdirectory
2022-09-26 11:56:07 +02:00
Olivier Le Doeuff
696f5299f0 🔨 Don't write to global CMAKE_CXX_STANDARD
Instead use modern target_compile_features with cxx_std_11
2022-09-26 11:55:45 +02:00
Olivier Le Doeuff
3c1190cd85 🔨 Introduce FASTFLOAT_INSTALL to make install optional
When using fast_float as a PRIVATE dependency, it is not required to install it.
This flag give user using fastfloat with add_subdirectory the opportunity to disable install target
Default behavior is conserved since FASTFLOAT_INSTALL is ON
2022-09-26 11:54:03 +02:00
Daniel Lemire
bc3be12530
Merge pull request #140 from striezel-stash/fix-typos
Fix some typos
2022-08-31 10:29:48 -04:00
Dirk Stolle
3aaf22e14d update actions/checkout in GitHub Actions to v3 2022-08-30 23:03:10 +02:00
Dirk Stolle
3fddb89508 Fix some typos 2022-08-30 22:55:34 +02:00
Daniel Lemire
d4dd12f8cf
Update README.md 2022-08-12 11:36:35 -04:00
Daniel Lemire
6876616f0f
Update float_common.h 2022-08-04 15:05:22 -04:00
Daniel Lemire
ac81b01696
Added __EMSCRIPTEN__ patch 2022-08-04 13:58:48 -04:00
Daniel Lemire
a332ec1ca7 Saving. 2022-07-29 11:54:19 -04:00
Daniel Lemire
d7c1dbc889 Merge branch 'main' of github.com:fastfloat/fast_float 2022-07-29 11:42:31 -04:00
Daniel Lemire
d797a3a05d Adding example. 2022-07-29 11:42:20 -04:00
Daniel Lemire
6283cf76c5
Merge pull request #126 from fargies/main
python: set file encoding
2022-07-29 11:15:21 -04:00
Daniel Lemire
cfb44c2592
Update README.md 2022-06-29 14:05:21 -04:00
Daniel Lemire
f2082bf747
Update README.md 2022-03-31 10:18:12 -04:00
Daniel Lemire
8e53c41385
Merge pull request #130 from fastfloat/dlemire/dropping2016
Dropping Windows 2016.
2022-03-19 07:59:25 -04:00
Daniel Lemire
7a17c54269 Dropping it. 2022-03-19 00:29:57 -04:00
Daniel Lemire
708224bc3e Dropping Windows 2016. 2022-03-19 00:28:06 -04:00
Daniel Lemire
b467215b4d
Update README.md 2022-03-19 00:14:13 -04:00
Daniel Lemire
090e500618
Merge pull request #129 from xvitaly/fix-arch
Export CMake targets as architecture independent
2022-03-09 17:40:20 -05:00
Vitaly Zaitsev
5738f18952
Export CMake targets as architecture independent.
Signed-off-by: Vitaly Zaitsev <vitaly@easycoding.org>
2022-03-05 16:05:28 +01:00
Daniel Lemire
ebfbea4f07
Merge pull request #127 from xvitaly/fix-installation
Fixed installation on other than Ubuntu GNU/Linux distributions
2022-03-03 09:41:18 -05:00
Vitaly Zaitsev
17bcdcef02
Fixed installation on other than Ubuntu GNU/Linux distributions.
Signed-off-by: Vitaly Zaitsev <vitaly@easycoding.org>
2022-03-03 10:12:55 +01:00
Fargier Sylvain
78edcbb9e2 python: set file encoding
- on some containers LC_ALL is not set, python fallsback on ascii
  encoder, which fails on AUTHORS file
2022-03-03 08:27:21 +01:00
Daniel Lemire
632cf9b8c8
Merge pull request #128 from fastfloat/dlemire/upgradevs17
Trying to upgrade to vs17
2022-03-02 22:46:07 -05:00
Daniel Lemire
ca9be545c3 Trying to upgrade to vs17 2022-03-02 22:39:54 -05:00
Daniel Lemire
32d21dcecb
Merge pull request #122 from jwakely/patch-1
Fix deduction failure for std::min call
2022-01-18 14:30:38 -05:00
Daniel Lemire
d3b164eb52
Merge pull request #123 from jwakely/endian
Make endianness detection more portable
2022-01-18 14:29:42 -05:00
Jonathan Wakely
61f4840188 Make endianness detection more portable
The current check for endianness fails on platforms using newlib as the
C library, because it provides <machine/endian.h> not <endian.h>. This
could be fixed by adding `|| defined(__NEWLIB__)` to the check for
targets that provide <machine/endian.h> (i.e. BSD-like targets).

A more portable solution is to just check if the compiler has already
defined the necessary macros (which is true for GCC and Clang and Intel,
at least). Then no header is needed, and it works for platforms that
aren't explicitly listed in the conditionals.
2022-01-18 10:17:01 +00:00
Jonathan Wakely
1ccabed64c Fix deduction failure for std::min call
This assumes that the literal `64` of type `int` has the same type as
the `int32_t` typedef, which is never true for targets with 16-bit
`int`, and isn't guaranteed to be true even with 32-bit `int`.
2022-01-18 10:12:57 +00:00
Daniel Lemire
b15abc9238
Update README.md 2022-01-16 21:04:50 -05:00
Daniel Lemire
b7f9d6ca39 Version 3.4.0 2021-11-30 15:53:33 -05:00
Daniel Lemire
62a8dba55a
Merge pull request #118 from pitrou/issue-117
Fix #117: compilation warning with gcc 6.3.0
2021-11-30 14:58:33 -05:00
Antoine Pitrou
133099ab4e Fix #117: compilation warning with gcc 6.3.0
Fix the following warning:
```
/arrow/cpp/src/arrow/vendored/fast_float/digit_comparison.h:62:50: error: right shift count >= width of type [-Werror=shift-count-overflow]
       am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
                           ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
2021-11-30 20:35:09 +01:00
Daniel Lemire
d35368cae6
Update README.md 2021-10-23 18:04:22 -04:00
Daniel Lemire
b9861e4190 Version 3.3 2021-10-19 14:45:19 -04:00
Daniel Lemire
a2fa7863fd
Merge pull request #116 from SamuelLongchamps/licenses_compliance
Added dual license option, proper copyright
2021-10-19 14:11:50 -04:00
Samuel Longchamps
38b5900fd2 Added dual license option, proper copyright
Fixed #115
2021-10-18 22:41:57 -04:00
Daniel Lemire
052975dd5f
Update README.md 2021-09-21 11:50:07 -04:00
Daniel Lemire
d35f404a15 Version bump. 2021-09-20 09:55:14 -04:00
Daniel Lemire
6d768256f1
Merge pull request #112 from fastfloat/dlemire/remove_cxx20
Removing CXX20 support
2021-09-20 09:54:00 -04:00
Daniel Lemire
d148241404 Removing CXX20 support 2021-09-20 09:49:23 -04:00
Daniel Lemire
dd04b5ebd7
Merge pull request #107 from mumbleskates/main
Testing changes and fixes
2021-09-15 21:26:17 -04:00
Kent Ross
1ad817ef54 Include What You Use 2021-09-15 17:38:54 -07:00
Daniel Lemire
1663effbcb
Merge pull request #108 from mumbleskates/cleanup-dtoa
Delete unused dtoa.c
2021-09-15 10:25:55 -04:00
Kent Ross
99d475ba9b Delete unused dtoa.c 2021-09-14 22:33:36 -07:00
Kent Ross
6ec3ace497 Fix long_test and add it to the non-exhaustive test suite 2021-09-14 22:22:52 -07:00
Kent Ross
3219e3ce4c Move fast tests to the non-exhaustive test set 2021-09-14 22:22:52 -07:00
Daniel Lemire
af4e24a30f Bump version. 2021-09-14 21:32:14 -04:00
Daniel Lemire
b334317dd2 Minor fixes 2021-09-14 21:31:34 -04:00
Daniel Lemire
1b9150913e Updating. 2021-09-13 22:04:26 -04:00
Daniel Lemire
5c85d38eda
Merge pull request #104 from fastfloat/dlemire/bigint
Adopting Alexhuszagh's decimal comparison approach for long input strings
2021-09-13 22:03:37 -04:00
Daniel Lemire
3f0ba09a95
Merge pull request #96 from Alexhuszagh/bigint
Implement the big-integer arithmetic algorithm.
2021-09-13 21:23:14 -04:00
Alex Huszagh
fc0c8680a5 Implement the big-integer arithmetic algorithm.
Replaces the existing decimal implementation, for substantial
performance improvements with near-halfway cases. This is especially
fast with a large number of digits.

**Big Integer Implementation**

A small subset of big-integer arithmetic has been added, with the
`bigint` struct. It uses a stack-allocated vector with enough bits to
store the float with the large number of significant digits. This is
log2(10^(769 + 342)), to account for the largest possible magnitude
exponent, and number of digits (3600 bits), and then rounded up to 4k bits.

The limb size is determined by the architecture: most 64-bit
architectures have efficient 128-bit multiplication, either by a single
hardware instruction or 2 native multiplications for the high and low
bits. This includes x86_64, mips64, s390x, aarch64, powerpc64, riscv64,
and the only known exception is sparcv8 and sparcv9. Therefore, we
define a limb size of 64-bits on 64-bit architectures except SPARC,
otherwise we fallback to 32-bit limbs.

A simple stackvector is used, which just has operations to add elements,
index, and truncate the vector.

`bigint` is then just a wrapper around this, with methods for
big-integer arithmetic. For our algorithms, we just need multiplication
by a power (x * b^N), multiplication by a bigint or scalar value, and
addition by a bigint or scalar value. Scalar addition and multiplication
uses compiler extensions when possible (__builtin_add_overflow and
__uint128_t), if not, then we implement simple logic shown to optimize
well on MSVC. Big-integer multiplication is done via grade school
multiplication, which is more efficient than any asymptotically faster
algorithms. Multiplication by a power is then done via bitshifts for
powers-of-two, and by iterative multiplications of a large and then
scalar value for powers-of-5.

**compute_float**

Compute float has been slightly modified so if the algorithm cannot
round correctly, it returns a normalized, extended-precision adjusted
mantissa with the power2 shifted by INT16_MIN so the exponent is always
negative. `compute_error` and `compute_error_scaled` have been added.

**Digit Optimiations**

To improve performance for numbers with many digits,
`parse_eight_digits_unrolled` is used for both integers and fractions,
and uses a while loop than two nested if statements. This adds no
noticeable performance cost for common floats, but dramatically improves
performance for numbers with large digits (without these optimizations,
~65% of the total runtime cost is in parse_number_string).

**Parsed Number**

Two fields have been added to `parsed_number_string`, which contains a
slice of the integer and fraction digits. This is extremely cheap, since
the work is already done, and the strings are pre-tokenized during
parsing. This allows us on overflow to re-parse these tokenized strings,
without checking if each character is an integer. Likewise, for the
big-integer algorithms, we can merely re-parse the pre-tokenized
strings.

**Slow Algorithm**

The new algorithm is `digit_comp`, which takes the parsed number string
and the `adjusted_mantissa` from `compute_float`. The significant digits
are parsed into a big integer, and the exponent relative to the
significant digits is calculated. If the exponent is >= 0, we use
`positive_digit_comp`, otherwise, we use `negative_digit_comp`.

`positive_digit_comp` is quite simple: we scale the significant digits
to the exponent, and then we get the high 64-bits for the native float,
determine if any lower bits were truncated, and use that to direct
rounding.

`negative_digit_comp` is a little more complex, but also quite trivial:
we use the parsed significant digits as the real digits, and calculate
the theoretical digits from `b+h`, the halfway point between `b` and
`b+u`, the next-positive float. To get `b`, we round the adjusted
mantissa down, create an extended-precision representation, and
calculate the halfway point. We now have a base-10 exponent for the real
digits, and a base-2 exponent for the theoretical digits. We scale these
two to the same exponent by multiplying the theoretixal digits by
`5**-real_exp`. We then get the base-2 exponent as `theor_exp -
real_exp`, and if this is positive, we multipy the theoretical digits by
it, otherwise, we multiply the real digits by it. Now, both are scaled
to the same magnitude, and we simply compare the digits in the big
integer, and use that to direct rounding.

**Rust-Isms**

A few Rust-isms have been added, since it simplifies logic assertions.
These can be trivially removed or reworked, as needed.

- a `slice` type has been added, which is a pointer and length.
- `FASTFLOAT_ASSERT`, `FASTFLOAT_DEBUG_ASSERT`, and `FASTFLOAT_TRY` have
  been added
  - `FASTFLOAT_ASSERT` aborts, even in release builds, if the condition
    fails.
  - `FASTFLOAT_DEBUG_ASSERT` defaults to `assert`, for logic errors.
  - `FASTFLOAT_TRY` is like a Rust `Option` type, which propagates
    errors.

Specifically, `FASTFLOAT_TRY` is useful in combination with
`FASTFLOAT_ASSERT` to ensure there are no memory corruption errors
possible in the big-integer arithmetic. Although the `bigint` type
ensures we have enough storage for all valid floats, memory issues are
quite a severe class of vulnerabilities, and due to the low performance
cost of checks, we abort if we would have out-of-bounds writes. This can
only occur when we are adding items to the vector, which is a very small
number of steps. Therefore, we abort if our memory safety guarantees
ever fail. lexical has never aborted, so it's unlikely we will ever fail
these guarantees.
2021-09-10 18:53:53 -05:00
Daniel Lemire
25b240a02d
Merge pull request #102 from jrahlf/reduce_includes
Remove unneeded includes
2021-09-05 19:04:31 -04:00
Jonas Rahlf
162a37b25a remove cstdio includes, remove cassert include, add asthetic newlines 2021-09-05 23:13:41 +02:00
Daniel Lemire
8c4405e76e
Merge pull request #101 from fastfloat/dlemire/const
C++ 20 support and tests
2021-09-03 18:54:52 -04:00
Daniel Lemire
f74d505615 Adding C++20 tests. 2021-09-03 17:57:44 -04:00
Daniel Lemire
1a56fe5f64
Merge pull request #100 from jrahlf/constexpr
constexpr for c++20 compliant compilers
2021-09-03 16:36:49 -04:00
Jonas Rahlf
4e13ec151b check for HAS_CXX20_CONSTEXPR before attempting to do c++20 stuff 2021-09-02 23:20:28 +02:00
Jonas Rahlf
e5d5e576a6 use #if defined __has_include properly 2021-09-02 22:22:03 +02:00
Jonas Rahlf
b17eafd06f chnage compiler check for bit_cast so it compiles with older compilers 2021-09-02 22:00:57 +02:00
Jonas Rahlf
d8ee88e7f6 initial version with working constexpr for c++20 compliant compilers 2021-09-01 00:52:25 +02:00
Daniel Lemire
898f54f30a
Merge pull request #95 from Alexhuszagh/ptr
Fixes #94, with unspecified behavior in pointer comparisons.
2021-08-21 14:27:35 -04:00
Alex Huszagh
3e74ed313a Fixes #94, with unspecified behavior in pointer comparisons. 2021-08-21 13:07:57 -05:00
109 changed files with 13091 additions and 9024 deletions

1
.bazelrc Normal file
View File

@ -0,0 +1 @@
build --cxxopt="--std=c++17"

4
.clang-format Normal file
View File

@ -0,0 +1,4 @@
BasedOnStyle: LLVM
SortIncludes: false
SeparateDefinitionBlocks: Always
MaxEmptyLinesToKeep: 1

13
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# Keep GitHub Actions up to date with GitHub's Dependabot...
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
version: 2
updates:
- package-ecosystem: github-actions
directory: /
groups:
github-actions:
patterns:
- "*" # Group all Actions updates into a single larger pull request
schedule:
interval: weekly

12
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,12 @@
We have benchmarks, please consider running them: [see our README for details](https://github.com/fastfloat/fast_float/blob/main/README.md#benchmarking). We expect you to run our benchmarks if you make claims with respect to the performance.
Our CI tests check formatting automating. If such a test fails, please consider running the bash script:
```bash
bash script/run-clangcldocker.sh
```
Make sure that you have [docker installed and running](https://docs.docker.com/engine/install/) on your system. Most Linux distributions support docker though some (like RedHat) have the equivalent (Podman). Users of Apple systems may want to [consider OrbStack](https://orbstack.dev). You do not need to familiar with docker, you just need to make sure that you are have it running.
If you are unable to format the code, we may format it for you.

View File

@ -1,27 +1,47 @@
name: Alpine Linux
'on':
on:
- push
- pull_request
jobs:
ubuntu-build:
build:
name: Build on Alpine ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
matrix:
arch:
- x86_64
- x86
- aarch64
- armv7
- ppc64le
- riscv64
steps:
- uses: actions/checkout@v2
- name: start docker
- name: Checkout repository
uses: actions/checkout@v5
- name: Install latest Alpine Linux for ${{ matrix.arch }}
uses: jirutka/setup-alpine@v1
with:
arch: ${{ matrix.arch }}
branch: ${{ matrix.arch == 'riscv64' && 'edge' || 'latest-stable' }}
packages: >
build-base
cmake
g++
linux-headers
git
bash
build-base
- name: Prepare
run: |
docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest
echo 'docker exec alpine "$@";' > ./alpine.sh
chmod +x ./alpine.sh
- name: install packages
cmake -DFASTFLOAT_TEST=ON -B build
shell: alpine.sh {0}
- name: Build
run: |
./alpine.sh apk update
./alpine.sh apk add build-base cmake g++ linux-headers git bash
- name: cmake
cmake --build build
shell: alpine.sh {0}
- name: Test
run: |
./alpine.sh cmake -DFASTFLOAT_TEST=ON -B build_for_alpine
- name: build
run: |
./alpine.sh cmake --build build_for_alpine
- name: test
run: |
./alpine.sh bash -c "cd build_for_alpine && ctest -R basictest"
ctest --test-dir build -R basictest
shell: alpine.sh {0}

View File

@ -1,25 +0,0 @@
name: Amalgamate Ubuntu 20.04 CI (GCC 9, 8)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
include:
# Legacy/x86 compilers cause CI failures.
#- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: }
- {cxx: , arch: } # default=gcc9
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9
steps:
- uses: actions/checkout@v2
- name: Compile with amalgamation
run: |
mkdir build &&
mkdir build/fast_float &&
python3 ./script/amalgamate.py > build/fast_float/fast_float.h &&
cp tests/string_test.cpp build/ &&
cd build &&
g++ string_test.cpp

View File

@ -0,0 +1,17 @@
name: Amalgamate Ubuntu 24.04 CI
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: Compile with amalgamation
run: |
mkdir build &&
mkdir build/fast_float &&
python3 ./script/amalgamate.py > build/fast_float/fast_float.h &&
cp tests/string_test.cpp build/ &&
cd build &&
g++ string_test.cpp

35
.github/workflows/cifuzz.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: CIFuzz
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Build Fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'fast_float'
language: c++
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'fast_float'
language: c++
fuzz-seconds: 300
output-sarif: true
- name: Upload Crash
uses: actions/upload-artifact@v5
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts
- name: Upload Sarif
if: always() && steps.build.outcome == 'success'
uses: github/codeql-action/upload-sarif@v4
with:
# Path to SARIF file relative to the root of the repository
sarif_file: cifuzz-sarif/results.sarif
checkout_path: cifuzz-sarif
category: CIFuzz

17
.github/workflows/emscripten.yml vendored Normal file
View File

@ -0,0 +1,17 @@
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.2.2
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
- name: Verify
run: emcc -v
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v3.6.0
- name: Configure
run: emcmake cmake -B build
- name: Build # We build but do not test
run: cmake --build build

View File

@ -0,0 +1,33 @@
name: Lint and format
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- '**.md'
- 'docs/**'
push:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
- name: Run clang-format
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # v4.15.0
with:
clang-format-version: '17'

View File

@ -23,7 +23,7 @@ jobs:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v5
- uses: msys2/setup-msys2@v2
with:
update: true

View File

@ -29,7 +29,7 @@ jobs:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v5
- uses: msys2/setup-msys2@v2
with:
update: true

41
.github/workflows/on-release.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: On Release
# By default, a workflow only has read permissions.
# Add the needed permission to write release assets
permissions:
contents: write
on:
release:
types:
- published
jobs:
build:
name: Add Release Assets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Amalgamate fast_float.h
run: |
mkdir build
mkdir build/fast_float
python3 ./script/amalgamate.py > build/fast_float/fast_float.h
- name: Test Amalgamation
run: |
cp tests/string_test.cpp build/
cd build
g++ string_test.cpp
- name: Upload Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: build/fast_float/fast_float.h
asset_name: fast_float.h
asset_content_type: text/plain

23
.github/workflows/risc.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Ubuntu RISC-V rvv VLEN=128 (clang 17)
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-17
- name: Build
run: |
CXX=clang++-17 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=128
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,vlen=128,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)

30
.github/workflows/s390x.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Ubuntu s390x (GCC 11)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: uraimo/run-on-arch-action@v3
name: Test
id: runcmd
with:
arch: s390x
githubToken: ${{ github.token }}
distro: ubuntu_latest
install: |
apt-get update -q -y
apt-get install -y cmake make g++
run: |
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build -j=2
ctest --output-on-failure --test-dir build

View File

@ -1,35 +0,0 @@
name: Ubuntu 18.04 CI (GCC 7, 6, 5)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
include:
# Legacy/x86 compilers cause CI failures.
#- {cxx: -DCMAKE_CXX_COMPILER=g++-5, arch: }
#- {cxx: -DCMAKE_CXX_COMPILER=g++-6, arch: }
- {cxx: , arch: } # default=gcc7
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc7
steps:
- uses: actions/checkout@v2
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.4
with:
cmake-version: '3.11.x'
#- name: Install older compilers
# run: |
# sudo -E dpkg --add-architecture i386
# sudo -E apt-get update
# sudo -E apt-get install -y --force-yes g++-5 g++-6 g++-5-multilib g++-6-multilib g++-multilib linux-libc-dev:i386 libc6:i386 libc6-dev:i386 libc6-dbg:i386
- name: Prepare build dir
run: mkdir build
- name: Configure
run: cd build && cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON ..
- name: Build
run: cmake --build build
- name: Run basic tests
run: cd build && ctest --output-on-failure -R basictest

View File

@ -1,29 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9, 8)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
include:
# Legacy/x86 compilers cause CI failures.
#- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: }
- {cxx: , arch: } # default=gcc9
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9
steps:
- uses: actions/checkout@v2
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure &&
cmake --install . &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . &&
cd ../../issue72_installation &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .

25
.github/workflows/ubuntu22-clang.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Ubuntu 22.04 CI (clang 14)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- name: Install clang++-14
run: sudo apt-get install -y clang++-14
- name: Use cmake
run: |
mkdir build &&
cd build &&
CXX=clang++-14 cmake -DFASTFLOAT_TEST=ON .. &&
cmake --build . &&
ctest --output-on-failure
- name: Use cmake CXX20
run: |
mkdir build20 &&
cd build20 &&
CXX=clang++-14 cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. &&
cmake --build . &&
ctest --output-on-failure

23
.github/workflows/ubuntu22-gcc12.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Ubuntu 22.04 CI (GCC 12)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- name: Use cmake
run: |
mkdir build &&
cd build &&
CXX=g++-12 CXXFLAGS=-Werror cmake -DFASTFLOAT_TEST=ON .. &&
cmake --build . &&
ctest --output-on-failure
- name: Use cmake CXX20
run: |
mkdir build20 &&
cd build20 &&
CXX=g++-12 CXXFLAGS=-Werror cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. &&
cmake --build . &&
ctest --output-on-failure

16
.github/workflows/ubuntu22-sanitize.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Ubuntu 22.04 CI Sanitized (GCC 11)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DFASTFLOAT_TEST=ON -D FASTFLOAT_SANITIZE=ON .. &&
cmake --build . &&
ctest --output-on-failure

16
.github/workflows/ubuntu22.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Ubuntu 22.04 CI (GCC 11)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DFASTFLOAT_TEST=ON .. &&
cmake --build . &&
ctest --output-on-failure

19
.github/workflows/ubuntu24-cxx20.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Ubuntu 24.04 CI
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure &&
cmake --install .

29
.github/workflows/ubuntu24.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Ubuntu 24.04 CI (GCC 13)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: Use cmake
run: |
set -xe
cmake -B build \
-DFASTFLOAT_TEST=ON \
-DFASTFLOAT_BENCHMARKS=ON \
-DCMAKE_CXX_FLAGS=' -Werror -Wundef '
cmake --build build --parallel
( cd build ; ctest --output-on-failure )
- name: Use cmake CXX23
run: |
set -xe
cmake -B build20 \
-DFASTFLOAT_TEST=ON \
-DFASTFLOAT_CONSTEXPR_TESTS=ON \
-DFASTFLOAT_FIXEDWIDTH_TESTS=ON \
-DFASTFLOAT_CXX_STANDARD=23 \
-DCMAKE_CXX_FLAGS=' -Werror -Wundef '
cmake --build build20 --parallel
( cd build20 ; ctest --output-on-failure )

View File

@ -1,29 +0,0 @@
name: VS15-CI
on: [push, pull_request]
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs15
runs-on: windows-2016
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 15 2017, arch: Win32}
- {gen: Visual Studio 15 2017, arch: x64}
steps:
- uses: actions/checkout@v2
- name: Configure
run: |
mkdir build
cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON ..
- name: Build
run: cmake --build build --verbose --config Release --parallel
- name: 'Run CTest'
run: |
cd build
ctest -C Release --output-on-failure -R basictest

View File

@ -1,21 +0,0 @@
name: VS16-ARM-CI
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {arch: ARM}
- {arch: ARM64}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Use cmake
run: |
cmake -A ${{ matrix.arch }} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON -B build &&
cmake --build build --verbose

View File

@ -1,29 +0,0 @@
name: VS16-CI
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 16 2019, arch: Win32}
- {gen: Visual Studio 16 2019, arch: x64}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --verbose &&
ctest --output-on-failure &&
cmake --install . &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . --verbose
cd ../../issue72_installation &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . --verbose

View File

@ -1,27 +0,0 @@
name: VS16-CLANG-CI
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 16 2019, arch: Win32}
- {gen: Visual Studio 16 2019, arch: x64}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
mkdir build
cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON ..
- name: Build
run: cmake --build build --config Release --parallel
- name: Run basic tests
run: |
cd build
ctest -C Release --output-on-failure -R basictest

28
.github/workflows/vs17-arm-ci.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: VS17-ARM-CI
on: [push, pull_request]
jobs:
ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Release}
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
- name: configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON
- name: build
run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
# disabled because it requires a toolchain
#- name: test
# run: |
# cd build &&
# ctest --output-on-failure -C ${{matrix.cfg}}

43
.github/workflows/vs17-ci.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: VS17-CI
on: [push, pull_request]
jobs:
ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
#- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
- name: configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination
- name: build
run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
- name: test
run: |
cd build &&
ctest --output-on-failure -C ${{matrix.cfg}}
- name: install
run: |
cd build &&
cmake --install .
- name: test install (find)
run: |
cd tests/installation_tests/find &&
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination &&
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
- name: test install (issue 72)
run: |
cd tests/installation_tests/issue72_installation &&
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination &&
cmake --build build --verbose --config ${{matrix.cfg}} --parallel

28
.github/workflows/vs17-clang-ci.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: VS17-CLANG-CI
on: [push, pull_request]
jobs:
ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
- name: Configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -T ClangCL -DFASTFLOAT_TEST=ON
- name: Build
run: cmake --build build --config ${{matrix.cfg}} --parallel --verbose
- name: Run basic tests
run: |
cd build
ctest -C ${{matrix.cfg}} --output-on-failure -R basictest

33
.github/workflows/vs17-cxx20.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: VS17-CI C++20
on: [push, pull_request]
jobs:
ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
- name: configure
run: >-
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}
-DFASTFLOAT_CXX_STANDARD=20
-DFASTFLOAT_TEST=ON
-DFASTFLOAT_CONSTEXPR_TESTS=ON
-DCMAKE_INSTALL_PREFIX:PATH=destination
- name: build
run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
- name: test
run: |
cd build &&
ctest --output-on-failure -C ${{matrix.cfg}}

20
.gitignore vendored
View File

@ -2,3 +2,23 @@ build/*
Testing/*
.cache/
compile_commands.json
bazel-*
# Visual studio
.vs/
Debug/
Release/
/out/
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
*.psess
*.vspx
*.vsp
*.diagsession
*.hint
# VS CMake
/out/
/CMakeSettings.json

6
BUILD.bazel Normal file
View File

@ -0,0 +1,6 @@
cc_library(
name = "fast_float",
hdrs = glob(["include/fast_float/*.h"]),
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)

View File

@ -1,9 +1,11 @@
cmake_minimum_required(VERSION 3.9)
cmake_minimum_required(VERSION 3.14)
project(fast_float VERSION 2.0.0 LANGUAGES CXX)
project(fast_float VERSION 8.1.0 LANGUAGES CXX)
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
option(FASTFLOAT_TEST "Enable tests" OFF)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(FASTFLOAT_TEST)
enable_testing()
add_subdirectory(tests)
@ -22,14 +24,31 @@ if (NOT CMAKE_BUILD_TYPE)
endif()
endif()
option(FASTFLOAT_INSTALL "Enable install" ON)
if(FASTFLOAT_INSTALL)
include(GNUInstallDirs)
endif()
add_library(fast_float INTERFACE)
option(FASTFLOAT_BENCHMARKS "Enable benchmarks" OFF)
if(FASTFLOAT_BENCHMARKS)
add_subdirectory(benchmarks)
else(FASTFLOAT_BENCHMARKS)
message(STATUS "Benchmarks are disabled. Set FASTFLOAT_BENCHMARKS to ON to build benchmarks (assumes C++17).")
endif(FASTFLOAT_BENCHMARKS)
add_library(FastFloat::fast_float ALIAS fast_float)
target_include_directories(
fast_float
INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_features(fast_float INTERFACE cxx_std_11)
if(FASTFLOAT_SANITIZE)
target_compile_options(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
@ -37,29 +56,39 @@ if(FASTFLOAT_SANITIZE)
target_link_libraries(fast_float INTERFACE -fuse-ld=gold)
endif()
endif()
if(MSVC_VERSION GREATER 1910)
include(CheckCXXCompilerFlag)
unset(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
CHECK_CXX_COMPILER_FLAG(/permissive- FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
if(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
target_compile_options(fast_float INTERFACE /permissive-)
endif()
if(FASTFLOAT_INSTALL)
include(CMakePackageConfigHelpers)
include(CMakePackageConfigHelpers)
set(FASTFLOAT_VERSION_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfigVersion.cmake")
set(FASTFLOAT_PROJECT_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfig.cmake")
set(FASTFLOAT_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake/FastFloat")
set(FASTFLOAT_VERSION_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfigVersion.cmake")
set(FASTFLOAT_PROJECT_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfig.cmake")
set(FASTFLOAT_INSTALL_DIR "share/FastFloat")
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
configure_package_config_file("cmake/config.cmake.in"
if(${CMAKE_VERSION} VERSION_LESS "3.14")
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
else()
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT)
endif()
configure_package_config_file("cmake/config.cmake.in"
"${FASTFLOAT_PROJECT_CONFIG}"
INSTALL_DESTINATION "${FASTFLOAT_INSTALL_DIR}")
INSTALL_DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/fast_float" DESTINATION "include")
install(FILES "${FASTFLOAT_PROJECT_CONFIG}" "${FASTFLOAT_VERSION_CONFIG}" DESTINATION "${FASTFLOAT_INSTALL_DIR}")
install(EXPORT ${PROJECT_NAME}-targets NAMESPACE FastFloat:: DESTINATION "${FASTFLOAT_INSTALL_DIR}")
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/fast_float" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
install(FILES "${FASTFLOAT_PROJECT_CONFIG}" "${FASTFLOAT_VERSION_CONFIG}" DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
install(EXPORT ${PROJECT_NAME}-targets NAMESPACE FastFloat:: DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
install(TARGETS fast_float
install(TARGETS fast_float
EXPORT ${PROJECT_NAME}-targets
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
)
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()

View File

@ -4,3 +4,8 @@ Marcin Wojdyr
Neal Richardson
Tim Paine
Fabio Pellacini
Lénárd Szolnoki
Jan Pharago
Maya Warrier
Taha Khokhar
Anders Dalvander

View File

@ -175,18 +175,7 @@
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 2020 The fast_float authors
Copyright 2021 The fast_float authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

23
LICENSE-BOOST Normal file
View File

@ -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.

View File

@ -1,3 +1,7 @@
MIT License
Copyright (c) 2021 The fast_float authors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the

9
MODULE.bazel Normal file
View File

@ -0,0 +1,9 @@
"""fast_float number parsing library: 4x faster than strtod"""
module(
name = "fast_float",
version = "6.1.6",
compatibility_level = 6,
)
bazel_dep(name = "doctest", version = "2.4.11", dev_dependency = True)

610
README.md
View File

@ -1,127 +1,502 @@
## fast_float number parsing library: 4x faster than strtod
![Ubuntu 20.04 CI (GCC 9)](https://github.com/lemire/fast_float/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)
![Ubuntu 18.04 CI (GCC 7)](https://github.com/lemire/fast_float/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg)
![Alpine Linux](https://github.com/lemire/fast_float/workflows/Alpine%20Linux/badge.svg)
![MSYS2-CI](https://github.com/lemire/fast_float/workflows/MSYS2-CI/badge.svg)
![VS16-CLANG-CI](https://github.com/lemire/fast_float/workflows/VS16-CLANG-CI/badge.svg)
[![VS16-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml)
[![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
The fast_float library provides fast header-only implementations for the C++ from_chars
functions for `float` and `double` types. These functions convert ASCII strings representing
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries.
The fast_float library provides fast header-only implementations for the C++
from_chars functions for `float` and `double` types as well as integer types.
These functions convert ASCII strings representing decimal values (e.g.,
`1.3e10`) into binary types. We provide exact rounding (including round to
even). In our experience, these `fast_float` functions many times faster than
comparable number-parsing functions from existing C++ standard libraries.
Specifically, `fast_float` provides the following two functions with a C++17-like syntax (the library itself only requires C++11):
Specifically, `fast_float` provides the following two functions to parse
floating-point numbers with a C++17-like syntax (the library itself only
requires C++11):
```C++
from_chars_result from_chars(const char* first, const char* last, float& value, ...);
from_chars_result from_chars(const char* first, const char* last, double& value, ...);
from_chars_result from_chars(char const *first, char const *last, float &value, ...);
from_chars_result from_chars(char const *first, char const *last, double &value, ...);
```
You can also parse integer types:
```C++
from_chars_result from_chars(char const *first, char const *last, int &value, ...);
from_chars_result from_chars(char const *first, char const *last, unsigned &value, ...);
```
The return type (`from_chars_result`) is defined as the struct:
```C++
struct from_chars_result {
const char* ptr;
char const *ptr;
std::errc ec;
};
```
It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
a locale-independent format equivalent to what is used by `std::strtod` in the default ("C") locale.
The resulting floating-point value is the closest floating-point values (using either float or double),
using the "round to even" convention for values that would otherwise fall right in-between two values.
That is, we provide exact parsing according to the IEEE standard.
It parses the character sequence `[first, last)` for a number. It parses
floating-point numbers expecting a locale-independent format equivalent to the
C++17 from_chars function. The resulting floating-point value is the closest
floating-point values (using either `float` or `double`), using the "round to
even" convention for values that would otherwise fall right in-between two
values. That is, we provide exact parsing according to the IEEE standard.
Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
Given a successful parse, the pointer (`ptr`) in the returned value is set to
point right after the parsed number, and the `value` referenced is set to the
parsed value. In case of error, the returned `ec` contains a representative
error, otherwise the default (`std::errc()`) value is stored.
The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
The implementation does not throw and does not allocate memory (e.g., with `new`
or `malloc`).
It will parse infinity and nan values.
Example:
``` C++
```C++
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
int main() {
const std::string input = "3.1416 xyz ";
std::string input = "3.1416 xyz ";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
Though the C++17 standard has you do a comparison with `std::errc()` to check whether the conversion worked, you can avoid it by casting the result to a `bool` like so:
Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
the type `fast_float::chars_format`. It is a bitset value: we check whether
`fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
to determine whether we allow the fixed point and scientific notation respectively.
The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
```cpp
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
The library seeks to follow the C++17 (see 20.19.3.(7.1)) specification. In particular, it forbids leading spaces and the leading '+' sign.
int main() {
std::string input = "3.1416 xyz ";
double result;
if(auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result)) {
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
std::cerr << "failed to parse " << result << std::endl;
return EXIT_FAILURE;
}
```
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
You can parse delimited numbers:
## Using commas as decimal separator
```C++
std::string input = "234532.3426362,7869234.9823,324562.645";
double result;
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) {
// check error
}
// we have result == 234532.3426362.
if (answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result);
if (answer.ec != std::errc()) {
// check error
}
// we have result == 7869234.9823.
if (answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result);
if (answer.ec != std::errc()) {
// check error
}
// we have result == 324562.645.
```
Like the C++17 standard, the `fast_float::from_chars` functions take an optional
last argument of the type `fast_float::chars_format`. It is a bitset value: we
check whether `fmt & fast_float::chars_format::fixed` and `fmt &
fast_float::chars_format::scientific` are set to determine whether we allow the
fixed point and scientific notation respectively. The default is
`fast_float::chars_format::general` which allows both `fixed` and `scientific`.
The C++ standard stipulate that `from_chars` has to be locale-independent. In
particular, the decimal separator has to be the period (`.`). However,
some users still want to use the `fast_float` library with in a locale-dependent
manner. Using a separate function called `from_chars_advanced`, we allow the users
to pass a `parse_options` instance which contains a custom decimal separator (e.g.,
the comma). You may use it as follows.
The library seeks to follow the C++17 (see
[28.2.3.(6.1)](https://eel.is/c++draft/charconv.from.chars#6.1)) specification.
* The `from_chars` function does not skip leading white-space characters (unless
`fast_float::chars_format::skip_white_space` is set).
* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is
forbidden (unless `fast_float::chars_format::allow_leading_plus` is set).
* It is generally impossible to represent a decimal value exactly as binary
floating-point number (`float` and `double` types). We seek the nearest value.
We round to an even mantissa when we are in-between two binary floating-point
numbers.
Furthermore, we have the following restrictions:
* We support `float` and `double`, but not `long double`. We also support
fixed-width floating-point types such as `std::float64_t`, `std::float32_t`,
`std::float16_t`, and `std::bfloat16_t`.
* We only support the decimal format: we do not support hexadecimal strings.
* For values that are either very large or very small (e.g., `1e9999`), we
represent it using the infinity or negative infinity value and the returned
`ec` is set to `std::errc::result_out_of_range`.
We support Visual Studio, macOS, Linux, freeBSD. We support big and little
endian. We support 32-bit and 64-bit systems.
We assume that the rounding mode is set to nearest (`std::fegetround() ==
FE_TONEAREST`).
## Integer types
You can also parse integer types using different bases (e.g., 2, 10, 16). The
following code will print the number 22250738585072012 three times:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "3,1416 xyz ";
uint64_t i;
std::string str = "22250738585072012";
auto answer = fast_float::from_chars(str.data(), str.data() + str.size(), i);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number " << i << std::endl;
std::string binstr = "1001111000011001110110111001001010110100111000110001100";
answer = fast_float::from_chars(binstr.data(), binstr.data() + binstr.size(), i, 2);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number " << i << std::endl;
std::string hexstr = "4f0cedc95a718c";
answer = fast_float::from_chars(hexstr.data(), hexstr.data() + hexstr.size(), i, 16);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number " << i << std::endl;
return EXIT_SUCCESS;
}
```
## Behavior of result_out_of_range
When parsing floating-point values, the numbers can sometimes be too small
(e.g., `1e-1000`) or too large (e.g., `1e1000`). The C language established the
precedent that these small values are out of range. In such cases, it is
customary to parse small values to zero and large values to infinity. That is
the behaviour of the C language (e.g., `stdtod`). That is the behaviour followed
by the fast_float library.
Specifically, we follow Jonathan Wakely's interpretation of the standard:
> In any case, the resulting value is one of at most two floating-point values
> closest to the value of the string matching the pattern.
It is also the approach taken by the [Microsoft C++
library](https://github.com/microsoft/STL/blob/62205ab155d093e71dd9588a78f02c5396c3c14b/tests/std/tests/P0067R5_charconv/test.cpp#L943-L946).
Hence, we have the following examples:
```cpp
double result = -1;
std::string str = "3e-1000";
auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
// r.ec == std::errc::result_out_of_range
// r.ptr == str.data() + 7
// result == 0
```
```cpp
double result = -1;
std::string str = "3e1000";
auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
// r.ec == std::errc::result_out_of_range
// r.ptr == str.data() + 6
// result == std::numeric_limits<double>::infinity()
```
Users who wish for the value to be left unmodified given
`std::errc::result_out_of_range` may do so by adding two lines of code:
```cpp
double old_result = result; // make copy
auto r = fast_float::from_chars(start, end, result);
if (r.ec == std::errc::result_out_of_range) { result = old_result; }
```
## C++20: compile-time evaluation (constexpr)
In C++20, you may use `fast_float::from_chars` to parse strings at compile-time,
as in the following example:
```C++
// consteval forces compile-time evaluation of the function in C++20.
consteval double parse(std::string_view input) {
double result;
fast_float::parse_options options{fast_float::chars_format::general, ','};
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) { return -1.0; }
return result;
}
// This function should compile to a function which
// merely returns 3.1415.
constexpr double constexptest() {
return parse("3.1415 input");
}
```
## C++23: Fixed width floating-point types
The library also supports fixed-width floating-point types such as
`std::float64_t`, `std::float32_t`, `std::float16_t`, and `std::bfloat16_t`.
E.g., you can write:
```C++
std::float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
```
## Non-ASCII Inputs
We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the
following example:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
std::u16string input = u"3.1416 xyz ";
double result;
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
## Advanced options: using commas as decimal separator, JSON and Fortran
## Reference
The C++ standard stipulate that `from_chars` has to be locale-independent. In
particular, the decimal separator has to be the period (`.`). However, some
users still want to use the `fast_float` library with in a locale-dependent
manner. Using a separate function called `from_chars_advanced`, we allow the
users to pass a `parse_options` instance which contains a custom decimal
separator (e.g., the comma). You may use it as follows.
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021.
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
std::string input = "3,1416 xyz ";
double result;
fast_float::parse_options options{fast_float::chars_format::general, ','};
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
if ((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
### You can also parse Fortran-like inputs
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
std::string input = "1d+4";
double result;
fast_float::parse_options options{fast_float::chars_format::fortran};
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
if ((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
### You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6))
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
std::string input = "+.1"; // not valid
double result;
fast_float::parse_options options{fast_float::chars_format::json};
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
```
By default the JSON format does not allow `inf`:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
std::string input = "inf"; // not valid in JSON
double result;
fast_float::parse_options options{fast_float::chars_format::json};
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
```
You can allow it with a non-standard `json_or_infnan` variant:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
double result;
fast_float::parse_options options{fast_float::chars_format::json_or_infnan};
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
if (answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
```
## Multiplication of an integer by a power of 10
An integer `W` can be multiplied by a power of ten `10^Q` and
converted to `double` with correctly rounded value
(in "round to nearest, tie to even" fashion) using
`fast_float::integer_times_pow10()`, e.g.:
```C++
const uint64_t W = 12345678901234567;
const int Q = 23;
const double result = fast_float::integer_times_pow10(W, Q);
std::cout.precision(17);
std::cout << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
```
outputs
```
12345678901234567 * 10^23 = 1.2345678901234567e+39 (==expected)
```
`fast_float::integer_times_pow10()` gives the same result as
using `fast_float::from_chars()` when parsing the string `"WeQ"`
(in this example `"12345678901234567e23"`),
except `fast_float::integer_times_pow10()` does not report out-of-range errors, and
underflows to zero or overflows to infinity when the resulting value is
out of range.
You can use template overloads to get the result converted to different
supported floating-point types: `float`, `double`, etc.
For example, to get result as `float` use
`fast_float::integer_times_pow10<float>()` specialization:
```C++
const uint64_t W = 12345678;
const int Q = 23;
const float result = fast_float::integer_times_pow10<float>(W, Q);
std::cout.precision(9);
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
```
outputs
```
float: 12345678 * 10^23 = 1.23456782e+30 (==expected)
```
Overloads of `fast_float::integer_times_pow10()` are provided for
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
## Users and Related Work
The fast_float library is part of:
* GCC (as of version 12): the `from_chars` function in GCC relies on fast_float,
* [Chromium](https://github.com/Chromium/Chromium), the engine behind Google
Chrome, Microsoft Edge, and Opera,
* Boost JSON, MySQL, etc.
* Blender
* [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's
web browser),
* [DuckDB](https://duckdb.org),
* [Redis](https://github.com/redis/redis) and [Valkey](https://github.com/valkey-io/valkey),
* [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied
the number parsing speed by two or three times,
* [Google Jsonnet](https://github.com/google/jsonnet),
* [ClickHouse](https://github.com/ClickHouse/ClickHouse).
The fastfloat algorithm is part of the [LLVM standard
libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
There is a [derived implementation part of
AdaCore](https://github.com/AdaCore/VSS). The [SerenityOS operating
system](https://github.com/SerenityOS/serenity/commit/53b7f5e6a11e663c83df8030c3171c5945cb75ec)
has a derived implementation that is inherited by the [Ladybird
Browser](https://github.com/LadybirdBrowser/ladybird).
The fast_float library provides a performance similar to that of the
[fast_double_parser](https://github.com/lemire/fast_double_parser) library but
using an updated algorithm reworked from the ground up, and while offering an
API more in line with the expectations of C++ programmers. The
fast_double_parser library is part of the [Microsoft LightGBM machine-learning
framework](https://github.com/microsoft/LightGBM).
Packages
------
[![Packaging status](https://repology.org/badge/vertical-allrepos/fast-float.svg)](https://repology.org/project/fast-float/versions)
## References
* Daniel Lemire, [Number Parsing at a Gigabyte per
Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience
51 (8), 2021.
* Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without
Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience
53 (7), 2023.
## Other programming languages
- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`.
- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`.
- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
## Relation With Other Work
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
## Users
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet).
* [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called
`rcppfastfloat`.
* [There is a Rust port of the fast_float
library](https://github.com/aldanor/fast-float-rust/) called
`fast-float-rust`.
* [There is a Java port of the fast_float
library](https://github.com/wrandelshofer/FastDoubleParser) called
`FastDoubleParser`. It used for important systems such as
[Jackson](https://github.com/FasterXML/jackson-core).
* [There is a C# port of the fast_float
library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
## How fast is it?
It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations.
It can parse random floating-point numbers at a speed of 1 GB/s on some systems.
We find that it is often twice as fast as the best available competitor, and
many times faster than many standard-library implementations.
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
<img src="https://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png"
width="400" alt="fast_float is many times faster than many standard-library
implementations">
```
```bash
$ ./build/benchmarks/benchmark
# parsing random integers in the range [0,1)
volume = 2.09808 MB
@ -132,74 +507,131 @@ abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfl
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
```
See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
See the [Benchmarking](#benchmarking) section for instructions on how to run our benchmarks.
## Video
[![Go Systems 2020](http://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br />
[![Go Systems 2020](https://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](https://www.youtube.com/watch?v=AVXgvlMeIm4)
## Using as a CMake dependency
This library is header-only by design. The CMake file provides the `fast_float` target
which is merely a pointer to the `include` directory.
This library is header-only by design. The CMake file provides the `fast_float`
target which is merely a pointer to the `include` directory.
If you drop the `fast_float` repository in your CMake project, you should be able to use
it in this manner:
If you drop the `fast_float` repository in your CMake project, you should be
able to use it in this manner:
```cmake
add_subdirectory(fast_float)
target_link_libraries(myprogram PUBLIC fast_float)
```
Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least):
Or you may want to retrieve the dependency automatically if you have a
sufficiently recent version of CMake (3.11 or better at least):
```cmake
FetchContent_Declare(
fast_float
GIT_REPOSITORY https://github.com/lemire/fast_float.git
GIT_TAG tags/v1.1.2
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
GIT_TAG tags/v8.1.0
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float)
target_link_libraries(myprogram PUBLIC fast_float)
```
You should change the `GIT_TAG` line so that you recover the version you wish to use.
You should change the `GIT_TAG` line so that you recover the version you wish to
use.
You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
```cmake
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v8.1.0)
```
## Using as single header
The script `script/amalgamate.py` may be used to generate a single header
version of the library if so desired.
Just run the script from the root directory of this repository.
You can customize the license type and output file if desired as described in
the command line help.
version of the library if so desired. Just run the script from the root
directory of this repository. You can customize the license type and output file
if desired as described in the command line help.
You may directly download automatically generated single-header files:
https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h
<https://github.com/fastfloat/fast_float/releases/download/v8.1.0/fast_float.h>
## Benchmarking
The project has its own benchmarks with realistic data inputs. Under Linux or macOS,
you can use it as follows if your system supports C++17:
```
cmake -B build -D FASTFLOAT_BENCHMARKS=ON
cmake --build build
./build/benchmarks/realbenchmark
```
Importantly, by default, the benchmark is built in Release mode.
The instructions are similar under Windows.
Under Linux and macOS, it is recommended to run the benchmarks in a privileged manner to get access
to hardware performance counters. You may be able to do so with the `sudo` command
in some cases:
```
sudo ./build/benchmarks/realbenchmark
```
If you have a text file containing one number per line (`myfile.txt`), you can run a benchmark over it like so:
```
cmake -B build -D FASTFLOAT_BENCHMARKS=ON
cmake --build build
./build/benchmarks/realbenchmark myfile.txt
```
## Packages
* The fast_float library is part of the [Conan package
manager](https://conan.io/center/recipes/fast_float).
* It is part of the [brew package
manager](https://formulae.brew.sh/formula/fast_float).
* fast_float is available on [xmake](https://xmake.io) repository.
* Some Linux distribution like Fedora include fast_float (e.g., as
`fast_float-devel`).
## Credit
Though this work is inspired by many different people, this work benefited especially from exchanges with
Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided
invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
Though this work is inspired by many different people, this work benefited
especially from exchanges with Michael Eisel, who motivated the original
research with his key insights, and with Nigel Tao who provided invaluable
feedback. Rémy Oudompheng first implemented a fast path we use in the case of
long digits.
The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
under the Apache 2.0 license.
The library includes code adapted from Google Wuffs (written by Nigel Tao) which
was originally published under the Apache 2.0 license.
## Stars
[![Star History Chart](https://api.star-history.com/svg?repos=fastfloat/fast_float&type=Date)](https://www.star-history.com/#fastfloat/fast_float&Date)
## License
<sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
2.0</a> or <a href="LICENSE-MIT">MIT license</a> or <a
href="LICENSE-BOOST">BOOST license</a>.
</sup>
<br>
<br/>
<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this repository by you, as defined in the Apache-2.0 license,
shall be dual licensed as above, without any additional terms or conditions.
shall be triple licensed as above, without any additional terms or conditions.
</sub>

7
SECURITY.md Normal file
View File

@ -0,0 +1,7 @@
# Security Policy
## Reporting a Vulnerability
Please use the following contact information for reporting a vulnerability:
- [Daniel Lemire](https://github.com/lemire) - daniel@lemire.me

26
benchmarks/CMakeLists.txt Normal file
View File

@ -0,0 +1,26 @@
add_executable(realbenchmark benchmark.cpp)
set_property(
TARGET realbenchmark
PROPERTY CXX_STANDARD 17)
target_link_libraries(realbenchmark PUBLIC fast_float)
include(ExternalProject)
# Define the external project
ExternalProject_Add(simple_fastfloat_benchmark
GIT_REPOSITORY https://github.com/lemire/simple_fastfloat_benchmark.git
GIT_TAG master # or specify a particular commit/tag/branch
SOURCE_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark
BINARY_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark-build
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
set(DATA_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark/data)
add_custom_target(CopyData ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory ${DATA_DIR} ${CMAKE_CURRENT_BINARY_DIR}/data
DEPENDS simple_fastfloat_benchmark
)
add_dependencies(realbenchmark CopyData)
target_compile_definitions(realbenchmark PUBLIC BENCHMARK_DATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data")

File diff suppressed because it is too large Load Diff

246
benchmarks/benchmark.cpp Normal file
View File

@ -0,0 +1,246 @@
#if defined(__linux__) || (__APPLE__ && __aarch64__)
#define USING_COUNTERS
#endif
#include "event_counter.h"
#include <algorithm>
#include "fast_float/fast_float.h"
#include <chrono>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctype.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>
#include <stdio.h>
#include <string>
#include <vector>
#include <locale.h>
template <typename CharT>
double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
double answer = 0;
double x = 0;
for (auto &st : s) {
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
if (p == st.data()) {
throw std::runtime_error("bug in findmax_fastfloat");
}
answer = answer > x ? answer : x;
}
return answer;
}
template <typename CharT>
double findmax_fastfloat32(std::vector<std::basic_string<CharT>> &s) {
float answer = 0;
float x = 0;
for (auto &st : s) {
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
if (p == st.data()) {
throw std::runtime_error("bug in findmax_fastfloat");
}
answer = answer > x ? answer : x;
}
return answer;
}
event_collector collector{};
#ifdef USING_COUNTERS
template <class T, class CharT>
std::vector<event_count>
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
size_t repeat) {
std::vector<event_count> aggregate;
bool printed_bug = false;
for (size_t i = 0; i < repeat; i++) {
collector.start();
double ts = function(lines);
if (ts == 0 && !printed_bug) {
printf("bug\n");
printed_bug = true;
}
aggregate.push_back(collector.end());
}
return aggregate;
}
void pretty_print(double volume, size_t number_of_floats, std::string name,
std::vector<event_count> events) {
double volumeMB = volume / (1024. * 1024.);
double average_ns{0};
double min_ns{DBL_MAX};
double cycles_min{DBL_MAX};
double instructions_min{DBL_MAX};
double cycles_avg{0};
double instructions_avg{0};
double branches_min{0};
double branches_avg{0};
double branch_misses_min{0};
double branch_misses_avg{0};
for (event_count e : events) {
double ns = e.elapsed_ns();
average_ns += ns;
min_ns = min_ns < ns ? min_ns : ns;
double cycles = e.cycles();
cycles_avg += cycles;
cycles_min = cycles_min < cycles ? cycles_min : cycles;
double instructions = e.instructions();
instructions_avg += instructions;
instructions_min =
instructions_min < instructions ? instructions_min : instructions;
double branches = e.branches();
branches_avg += branches;
branches_min = branches_min < branches ? branches_min : branches;
double branch_misses = e.missed_branches();
branch_misses_avg += branch_misses;
branch_misses_min =
branch_misses_min < branch_misses ? branch_misses_min : branch_misses;
}
cycles_avg /= events.size();
instructions_avg /= events.size();
average_ns /= events.size();
branches_avg /= events.size();
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
volumeMB * 1000000000 / min_ns,
(average_ns - min_ns) * 100.0 / average_ns);
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / min_ns);
if (instructions_min > 0) {
printf(" %8.2f i/B %8.2f i/f (+/- %.1f %%) ", instructions_min / volume,
instructions_min / number_of_floats,
(instructions_avg - instructions_min) * 100.0 / instructions_avg);
printf(" %8.2f c/B %8.2f c/f (+/- %.1f %%) ", cycles_min / volume,
cycles_min / number_of_floats,
(cycles_avg - cycles_min) * 100.0 / cycles_avg);
printf(" %8.2f i/c ", instructions_min / cycles_min);
printf(" %8.2f b/f ", branches_avg / number_of_floats);
printf(" %8.2f bm/f ", branch_misses_avg / number_of_floats);
printf(" %8.2f GHz ", cycles_min / min_ns);
}
printf("\n");
}
#else
template <class T, class CharT>
std::pair<double, double>
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
size_t repeat) {
std::chrono::high_resolution_clock::time_point t1, t2;
double average = 0;
double min_value = DBL_MAX;
bool printed_bug = false;
for (size_t i = 0; i < repeat; i++) {
t1 = std::chrono::high_resolution_clock::now();
double ts = function(lines);
if (ts == 0 && !printed_bug) {
printf("bug\n");
printed_bug = true;
}
t2 = std::chrono::high_resolution_clock::now();
double dif =
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
average += dif;
min_value = min_value < dif ? min_value : dif;
}
average /= repeat;
return std::make_pair(min_value, average);
}
void pretty_print(double volume, size_t number_of_floats, std::string name,
std::pair<double, double> result) {
double volumeMB = volume / (1024. * 1024.);
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
volumeMB * 1000000000 / result.first,
(result.second - result.first) * 100.0 / result.second);
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / result.first);
printf(" %8.2f ns/f \n", double(result.first) / number_of_floats);
}
#endif
// this is okay, all chars are ASCII
inline std::u16string widen(std::string line) {
std::u16string u16line;
u16line.resize(line.size());
for (size_t i = 0; i < line.size(); ++i) {
u16line[i] = char16_t(line[i]);
}
return u16line;
}
std::vector<std::u16string> widen(const std::vector<std::string> &lines) {
std::vector<std::u16string> u16lines;
u16lines.reserve(lines.size());
for (auto const &line : lines) {
u16lines.push_back(widen(line));
}
return u16lines;
}
void process(std::vector<std::string> &lines, size_t volume) {
size_t repeat = 1000;
double volumeMB = volume / (1024. * 1024.);
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)",
time_it_ns(lines, findmax_fastfloat64<char>, repeat));
pretty_print(volume, lines.size(), "fastfloat (32)",
time_it_ns(lines, findmax_fastfloat32<char>, repeat));
std::vector<std::u16string> lines16 = widen(lines);
volume = 2 * volume;
volumeMB = volume / (1024. * 1024.);
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)",
time_it_ns(lines16, findmax_fastfloat64<char16_t>, repeat));
pretty_print(volume, lines.size(), "fastfloat (32)",
time_it_ns(lines16, findmax_fastfloat32<char16_t>, repeat));
}
void fileload(std::string filename) {
std::ifstream inputfile(filename);
if (!inputfile) {
std::cerr << "can't open " << filename << std::endl;
return;
}
std::cout << "#### " << std::endl;
std::cout << "# reading " << filename << std::endl;
std::cout << "#### " << std::endl;
std::string line;
std::vector<std::string> lines;
lines.reserve(10000); // let us reserve plenty of memory.
size_t volume = 0;
while (getline(inputfile, line)) {
volume += line.size();
lines.push_back(line);
}
std::cout << "# read " << lines.size() << " lines " << std::endl;
process(lines, volume);
}
int main(int argc, char **argv) {
if (collector.has_events()) {
std::cout << "# Using hardware counters" << std::endl;
} else {
#if defined(__linux__) || (__APPLE__ && __aarch64__)
std::cout << "# Hardware counters not available, try to run in privileged "
"mode (e.g., sudo)."
<< std::endl;
#endif
}
if (argc > 1) {
fileload(argv[1]);
return EXIT_SUCCESS;
}
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
return EXIT_SUCCESS;
}

181
benchmarks/event_counter.h Normal file
View File

@ -0,0 +1,181 @@
#ifndef __EVENT_COUNTER_H
#define __EVENT_COUNTER_H
#include <cctype>
#ifndef _MSC_VER
#include <dirent.h>
#endif
#include <cinttypes>
#include <cstring>
#include <chrono>
#include <vector>
#include "linux-perf-events.h"
#ifdef __linux__
#include <libgen.h>
#endif
#if (defined(__APPLE__) && __APPLE__) && (defined(__aarch64__) && __aarch64__)
#include "apple_arm_events.h"
#endif
struct event_count {
std::chrono::duration<double> elapsed;
std::vector<unsigned long long> event_counts;
event_count() : elapsed(0), event_counts{0, 0, 0, 0, 0} {}
event_count(const std::chrono::duration<double> _elapsed,
const std::vector<unsigned long long> _event_counts)
: elapsed(_elapsed), event_counts(_event_counts) {}
event_count(const event_count &other)
: elapsed(other.elapsed), event_counts(other.event_counts) {}
// The types of counters (so we can read the getter more easily)
enum event_counter_types {
CPU_CYCLES = 0,
INSTRUCTIONS = 1,
BRANCHES = 2,
MISSED_BRANCHES = 3
};
double elapsed_sec() const {
return std::chrono::duration<double>(elapsed).count();
}
double elapsed_ns() const {
return std::chrono::duration<double, std::nano>(elapsed).count();
}
double cycles() const {
return static_cast<double>(event_counts[CPU_CYCLES]);
}
double instructions() const {
return static_cast<double>(event_counts[INSTRUCTIONS]);
}
double branches() const {
return static_cast<double>(event_counts[BRANCHES]);
}
double missed_branches() const {
return static_cast<double>(event_counts[MISSED_BRANCHES]);
}
event_count &operator=(const event_count &other) {
this->elapsed = other.elapsed;
this->event_counts = other.event_counts;
return *this;
}
event_count operator+(const event_count &other) const {
return event_count(elapsed + other.elapsed,
{
event_counts[0] + other.event_counts[0],
event_counts[1] + other.event_counts[1],
event_counts[2] + other.event_counts[2],
event_counts[3] + other.event_counts[3],
event_counts[4] + other.event_counts[4],
});
}
void operator+=(const event_count &other) { *this = *this + other; }
};
struct event_aggregate {
bool has_events = false;
int iterations = 0;
event_count total{};
event_count best{};
event_count worst{};
event_aggregate() = default;
void operator<<(const event_count &other) {
if (iterations == 0 || other.elapsed < best.elapsed) {
best = other;
}
if (iterations == 0 || other.elapsed > worst.elapsed) {
worst = other;
}
iterations++;
total += other;
}
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
double cycles() const { return total.cycles() / iterations; }
double instructions() const { return total.instructions() / iterations; }
double branches() const { return total.branches() / iterations; }
double missed_branches() const {
return total.missed_branches() / iterations;
}
};
struct event_collector {
event_count count{};
std::chrono::time_point<std::chrono::steady_clock> start_clock{};
#if defined(__linux__)
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
event_collector()
: linux_events(std::vector<int>{
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions
PERF_COUNT_HW_BRANCH_MISSES}) {}
bool has_events() { return linux_events.is_working(); }
#elif __APPLE__ && __aarch64__
performance_counters diff;
event_collector() : diff(0) { setup_performance_counters(); }
bool has_events() { return setup_performance_counters(); }
#else
event_collector() {}
bool has_events() { return false; }
#endif
inline void start() {
#if defined(__linux)
linux_events.start();
#elif __APPLE__ && __aarch64__
if (has_events()) {
diff = get_counters();
}
#endif
start_clock = std::chrono::steady_clock::now();
}
inline event_count &end() {
const auto end_clock = std::chrono::steady_clock::now();
#if defined(__linux)
linux_events.end(count.event_counts);
#elif __APPLE__ && __aarch64__
if (has_events()) {
performance_counters end = get_counters();
diff = end - diff;
}
count.event_counts[0] = diff.cycles;
count.event_counts[1] = diff.instructions;
count.event_counts[2] = diff.branches;
count.event_counts[3] = diff.missed_branches;
count.event_counts[4] = 0;
#endif
count.elapsed = end_clock - start_clock;
return count;
}
};
#endif

View File

@ -0,0 +1,104 @@
#pragma once
#ifdef __linux__
#include <asm/unistd.h> // for __NR_perf_event_open
#include <linux/perf_event.h> // for perf event constants
#include <sys/ioctl.h> // for ioctl
#include <unistd.h> // for syscall
#include <cerrno> // for errno
#include <cstring> // for memset
#include <stdexcept>
#include <iostream>
#include <vector>
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
int fd;
bool working;
perf_event_attr attribs{};
size_t num_events{};
std::vector<uint64_t> temp_result_vec{};
std::vector<uint64_t> ids{};
public:
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
memset(&attribs, 0, sizeof(attribs));
attribs.type = TYPE;
attribs.size = sizeof(attribs);
attribs.disabled = 1;
attribs.exclude_kernel = 1;
attribs.exclude_hv = 1;
attribs.sample_period = 0;
attribs.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
const int pid = 0; // the current process
const int cpu = -1; // all CPUs
const unsigned long flags = 0;
int group = -1; // no group
num_events = config_vec.size();
ids.resize(config_vec.size());
uint32_t i = 0;
for (auto config : config_vec) {
attribs.config = config;
int _fd = static_cast<int>(
syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
if (_fd == -1) {
report_error("perf_event_open");
}
ioctl(_fd, PERF_EVENT_IOC_ID, &ids[i++]);
if (group == -1) {
group = _fd;
fd = _fd;
}
}
temp_result_vec.resize(num_events * 2 + 1);
}
~LinuxEvents() {
if (fd != -1) {
close(fd);
}
}
inline void start() {
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_RESET)");
}
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_ENABLE)");
}
}
}
inline void end(std::vector<unsigned long long> &results) {
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
}
if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) {
report_error("read");
}
}
// our actual results are in slots 1,3,5, ... of this structure
for (uint32_t i = 1; i < temp_result_vec.size(); i += 2) {
results[i / 2] = temp_result_vec[i];
}
for (uint32_t i = 2; i < temp_result_vec.size(); i += 2) {
if (ids[i / 2 - 1] != temp_result_vec[i]) {
report_error("event mismatch");
}
}
}
bool is_working() { return working; }
private:
void report_error(const std::string &) { working = false; }
};
#endif

0
clang-format-ignore.txt Normal file
View File

View File

@ -0,0 +1,4 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR riscv64)
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-riscv64-static")

8
fuzz/build.sh Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
$CXX $CFLAGS $CXXFLAGS \
-I $SRC/fast_float/include \
-c $SRC/fast_float/fuzz/from_chars.cc -o from_chars.o
$CXX $CFLAGS $CXXFLAGS $LIB_FUZZING_ENGINE from_chars.o \
-o $OUT/from_chars

38
fuzz/from_chars.cc Normal file
View File

@ -0,0 +1,38 @@
#include "fast_float/fast_float.h"
#include <fuzzer/FuzzedDataProvider.h>
#include <string>
#include <system_error>
fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) {
using fast_float::chars_format;
switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
case 0:
return chars_format::scientific;
break;
case 1:
return chars_format::fixed;
break;
case 2:
return chars_format::fixed;
break;
}
return chars_format::general;
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) {
FuzzedDataProvider fdp(data, size);
fast_float::chars_format format = arbitrary_format(fdp);
double result_d = 0.0;
std::string input_d = fdp.ConsumeRandomLengthString(128);
auto answer = fast_float::from_chars(
input_d.data(), input_d.data() + input_d.size(), result_d, format);
std::string input_f = fdp.ConsumeRandomLengthString(128);
float result_f = 0.0;
answer = fast_float::from_chars(
input_f.data(), input_f.data() + input_f.size(), result_f, format);
int result_i = 0;
std::string input_i = fdp.ConsumeRandomLengthString(128);
answer = fast_float::from_chars(input_i.data(),
input_i.data() + input_i.size(), result_i);
return 0;
}

View File

@ -1,31 +1,59 @@
#ifndef FASTFLOAT_ASCII_NUMBER_H
#define FASTFLOAT_ASCII_NUMBER_H
#include <cstdio>
#include <cctype>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <limits>
#include <type_traits>
#include "float_common.h"
#ifdef FASTFLOAT_SSE2
#include <emmintrin.h>
#endif
#ifdef FASTFLOAT_NEON
#include <arm_neon.h>
#endif
namespace fast_float {
template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
#ifdef FASTFLOAT_HAS_SIMD
return std::is_same<UC, char16_t>::value;
#else
return false;
#endif
}
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24
| (val & 0x000000FF00000000) >> 8
| (val & 0x00000000FF000000) << 8
| (val & 0x0000000000FF0000) << 24
| (val & 0x000000000000FF00) << 40
| (val & 0x00000000000000FF) << 56;
template <typename UC>
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
return !(c > UC('9') || c < UC('0'));
}
fastfloat_really_inline uint64_t read_u64(const char *chars) {
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
(val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
(val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
}
// Read 8 UC into a u64. Truncates UC if not char.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
read8_to_u64(UC const *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
uint64_t val = 0;
for (int i = 0; i < 8; ++i) {
val |= uint64_t(uint8_t(*chars)) << (i * 8);
++chars;
}
return val;
}
uint64_t val;
::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1
@ -35,70 +63,258 @@ fastfloat_really_inline uint64_t read_u64(const char *chars) {
return val;
}
fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#ifdef FASTFLOAT_SSE2
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
__m128i const packed = _mm_packus_epi16(data, data);
#ifdef FASTFLOAT_64BIT
return uint64_t(_mm_cvtsi128_si64(packed));
#else
uint64_t value;
// Visual Studio + older versions of GCC don't support _mm_storeu_si64
_mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
return value;
#endif
::memcpy(chars, &val, sizeof(uint64_t));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64(
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#elif defined(FASTFLOAT_NEON)
fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
uint8x8_t utf8_packed = vmovn_u16(data);
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64(
vld1q_u16(reinterpret_cast<uint16_t const *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#endif // FASTFLOAT_SSE2
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif
// dummy for compile
uint64_t simd_read8_to_u64(UC const *) {
return 0;
}
// credit @aqrit
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
parse_eight_digits_unrolled(uint64_t val) {
uint64_t const mask = 0x000000FF000000FF;
uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
return uint32_t(val);
}
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
return parse_eight_digits_unrolled(read_u64(chars));
// Call this if chars are definitely 8 digits.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
parse_eight_digits_unrolled(UC const *chars) noexcept {
if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
}
return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
}
// credit @aqrit
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
fastfloat_really_inline constexpr bool
is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080));
}
fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
return is_made_of_eight_digits_fast(read_u64(chars));
#ifdef FASTFLOAT_HAS_SIMD
// Call this if chars might not be 8 digits.
// Using this style (instead of is_made_of_eight_digits_fast() then
// parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
simd_parse_if_eight_digits_unrolled(char16_t const *chars,
uint64_t &i) noexcept {
if (cpp20_and_in_constexpr()) {
return false;
}
#ifdef FASTFLOAT_SSE2
FASTFLOAT_SIMD_DISABLE_WARNINGS
__m128i const data =
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
// (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html
__m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
__m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
if (_mm_movemask_epi8(t1) == 0) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true;
} else
return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS
#elif defined(FASTFLOAT_NEON)
FASTFLOAT_SIMD_DISABLE_WARNINGS
uint16x8_t const data = vld1q_u16(reinterpret_cast<uint16_t const *>(chars));
// (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html
uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0'));
uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true;
} else
return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS
#else
(void)chars;
(void)i;
return false;
#endif // FASTFLOAT_SSE2
}
struct parsed_number_string {
int64_t exponent;
uint64_t mantissa;
const char *lastmatch;
bool negative;
bool valid;
bool too_many_digits;
#endif // FASTFLOAT_HAS_SIMD
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif
// dummy for compile
bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
return 0;
}
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) {
if (!has_simd_opt<UC>()) {
return;
}
while ((std::distance(p, pend) >= 8) &&
simd_parse_if_eight_digits_unrolled(
p, i)) { // in rare cases, this will overflow, but that's ok
p += 8;
}
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
loop_parse_if_eight_digits(char const *&p, char const *const pend,
uint64_t &i) {
// optimizes better than parse_if_eight_digits_unrolled() for UC = char.
while ((std::distance(p, pend) >= 8) &&
is_made_of_eight_digits_fast(read8_to_u64(p))) {
i = i * 100000000 +
parse_eight_digits_unrolled(read8_to_u64(
p)); // in rare cases, this will overflow, but that's ok
p += 8;
}
}
enum class parse_error {
no_error,
// [JSON-only] The minus sign must be followed by an integer.
missing_integer_after_sign,
// A sign must be followed by an integer or dot.
missing_integer_or_dot_after_sign,
// [JSON-only] The integer part must not have leading zeros.
leading_zeros_in_integer_part,
// [JSON-only] The integer part must have at least one digit.
no_digits_in_integer_part,
// [JSON-only] If there is a decimal point, there must be digits in the
// fractional part.
no_digits_in_fractional_part,
// The mantissa must have at least one digit.
no_digits_in_mantissa,
// Scientific notation requires an exponential part.
missing_exponential_part,
};
template <typename UC> struct parsed_number_string_t {
int64_t exponent{0};
uint64_t mantissa{0};
UC const *lastmatch{nullptr};
bool negative{false};
bool valid{false};
bool too_many_digits{false};
// contains the range of the significant digits
span<UC const> integer{}; // non-nullable
span<UC const> fraction{}; // nullable
parse_error error{parse_error::no_error};
};
using byte_span = span<char const>;
using parsed_number_string = parsed_number_string_t<char>;
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
report_parse_error(UC const *p, parse_error error) {
parsed_number_string_t<UC> answer;
answer.valid = false;
answer.lastmatch = p;
answer.error = error;
return answer;
}
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
fastfloat_really_inline
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
const chars_format fmt = options.format;
const char decimal_point = options.decimal_point;
template <bool basic_json_fmt, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parse_number_string(UC const *p, UC const *pend,
parse_options_t<UC> options) noexcept {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
UC const decimal_point = options.decimal_point;
parsed_number_string answer;
parsed_number_string_t<UC> answer;
answer.valid = false;
answer.too_many_digits = false;
answer.negative = (*p == '-');
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
// assume p < pend, so dereference without checks;
answer.negative = (*p == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
!basic_json_fmt && *p == UC('+'))) {
++p;
if (p == pend) {
return answer;
return report_parse_error<UC>(
p, parse_error::missing_integer_or_dot_after_sign);
}
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
return answer;
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
if (!is_integer(*p)) { // a sign must be followed by an integer
return report_parse_error<UC>(p,
parse_error::missing_integer_after_sign);
}
}
const char *const start_digits = p;
else {
if (!is_integer(*p) &&
(*p !=
decimal_point)) { // a sign must be followed by an integer or the dot
return report_parse_error<UC>(
p, parse_error::missing_integer_or_dot_after_sign);
}
}
}
UC const *const start_digits = p;
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
@ -106,67 +322,100 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i +
uint64_t(*p - '0'); // might overflow, we will handle the overflow later
uint64_t(*p -
UC('0')); // might overflow, we will handle the overflow later
++p;
}
const char *const end_of_integer_part = p;
UC const *const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = span<UC const>(start_digits, size_t(digit_count));
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
// at least 1 digit in integer part, without leading zeros
if (digit_count == 0) {
return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
}
if ((start_digits[0] == UC('0') && digit_count > 1)) {
return report_parse_error<UC>(start_digits,
parse_error::leading_zeros_in_integer_part);
}
}
int64_t exponent = 0;
if ((p != pend) && (*p == decimal_point)) {
bool const has_decimal_point = (p != pend) && (*p == decimal_point);
if (has_decimal_point) {
++p;
// Fast approach only tested under little endian systems
if ((p + 8 <= pend) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
if ((p + 8 <= pend) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
}
}
UC const *before = p;
// can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck.
loop_parse_if_eight_digits(p, pend, i);
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
uint8_t digit = uint8_t(*p - UC('0'));
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
exponent = end_of_integer_part + 1 - p;
exponent = before - p;
answer.fraction = span<UC const>(before, size_t(p - before));
digit_count -= exponent;
}
// we must have encountered at least one integer!
if (digit_count == 0) {
return answer;
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
// at least 1 digit in fractional part
if (has_decimal_point && exponent == 0) {
return report_parse_error<UC>(p,
parse_error::no_digits_in_fractional_part);
}
}
else if (digit_count == 0) { // we must have encountered at least one integer!
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
}
int64_t exp_number = 0; // explicit exponential part
if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
const char * location_of_e = p;
if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
((UC('e') == *p) || (UC('E') == *p))) ||
(uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) &&
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
(UC('D') == *p)))) {
UC const *location_of_e = p;
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
(UC('D') == *p)) {
++p;
}
bool neg_exp = false;
if ((p != pend) && ('-' == *p)) {
if ((p != pend) && (UC('-') == *p)) {
neg_exp = true;
++p;
} else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
} else if ((p != pend) &&
(UC('+') ==
*p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
if ((p == pend) || !is_integer(*p)) {
if(!(fmt & chars_format::fixed)) {
// We are in error.
return answer;
if (!uint64_t(fmt & chars_format::fixed)) {
// The exponential part is invalid for scientific notation, so it must
// be a trailing token for fixed notation. However, fixed notation is
// disabled, so report a scientific notation error.
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
if (exp_number < 0x10000) {
uint8_t digit = uint8_t(*p - UC('0'));
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
if(neg_exp) { exp_number = - exp_number; }
if (neg_exp) {
exp_number = -exp_number;
}
exponent += exp_number;
}
} else {
// If it scientific and not fixed, we have to bail out.
if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
if (uint64_t(fmt & chars_format::scientific) &&
!uint64_t(fmt & chars_format::fixed)) {
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
}
}
answer.lastmatch = p;
answer.valid = true;
@ -181,31 +430,37 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
const char *start = start_digits;
while ((start != pend) && (*start == '0' || *start == decimal_point)) {
if(*start == '0') { digit_count --; }
UC const *start = start_digits;
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
if (*start == UC('0')) {
digit_count--;
}
start++;
}
if (digit_count > 19) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to call if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
p = start_digits;
const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
while((i < minimal_nineteen_digit_integer) && (p != pend) && is_integer(*p)) {
i = i * 10 + uint64_t(*p - '0');
p = answer.integer.ptr;
UC const *int_end = p + answer.integer.len();
uint64_t const minimal_nineteen_digit_integer{1000000000000000000};
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
if (i >= minimal_nineteen_digit_integer) { // We have a big integer
exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component.
p++; // skip the dot
const char *first_after_period = p;
while((i < minimal_nineteen_digit_integer) && (p != pend) && is_integer(*p)) {
i = i * 10 + uint64_t(*p - '0');
p = answer.fraction.ptr;
UC const *frac_end = p + answer.fraction.len();
while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
exponent = first_after_period - p + exp_number;
exponent = answer.fraction.ptr - p + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
}
@ -215,108 +470,119 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
return answer;
}
template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
parse_int_string(UC const *p, UC const *pend, T &value,
parse_options_t<UC> options) {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
// This should always succeed since it follows a call to parse_number_string
// This function could be optimized. In particular, we could stop after 19 digits
// and try to bail out. Furthermore, we should be able to recover the computed
// exponent from the pass in parse_number_string.
fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend, parse_options options) noexcept {
const char decimal_point = options.decimal_point;
from_chars_result_t<UC> answer;
decimal answer;
answer.num_digits = 0;
answer.decimal_point = 0;
answer.truncated = false;
answer.negative = (*p == '-');
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
++p;
}
// skip leading zeroes
while ((p != pend) && (*p == '0')) {
++p;
}
while ((p != pend) && is_integer(*p)) {
if (answer.num_digits < max_digits) {
answer.digits[answer.num_digits] = uint8_t(*p - '0');
}
answer.num_digits++;
++p;
}
if ((p != pend) && (*p == decimal_point)) {
++p;
const char *first_after_period = p;
// if we have not yet encountered a zero, we have to skip it as well
if(answer.num_digits == 0) {
// skip zeros
while ((p != pend) && (*p == '0')) {
++p;
}
}
// We expect that this loop will often take the bulk of the running time
// because when a value has lots of digits, these digits often
while ((p + 8 <= pend) && (answer.num_digits + 8 < max_digits)) {
uint64_t val = read_u64(p);
if(! is_made_of_eight_digits_fast(val)) { break; }
// We have eight digits, process them in one go!
val -= 0x3030303030303030;
write_u64(answer.digits + answer.num_digits, val);
answer.num_digits += 8;
p += 8;
}
while ((p != pend) && is_integer(*p)) {
if (answer.num_digits < max_digits) {
answer.digits[answer.num_digits] = uint8_t(*p - '0');
}
answer.num_digits++;
++p;
}
answer.decimal_point = int32_t(first_after_period - p);
}
// We want num_digits to be the number of significant digits, excluding
// leading *and* trailing zeros! Otherwise the truncated flag later is
// going to be misleading.
if(answer.num_digits > 0) {
// We potentially need the answer.num_digits > 0 guard because we
// prune leading zeros. So with answer.num_digits > 0, we know that
// we have at least one non-zero digit.
const char *preverse = p - 1;
int32_t trailing_zeros = 0;
while ((*preverse == '0') || (*preverse == decimal_point)) {
if(*preverse == '0') { trailing_zeros++; };
--preverse;
}
answer.decimal_point += int32_t(answer.num_digits);
answer.num_digits -= uint32_t(trailing_zeros);
}
if(answer.num_digits > max_digits) {
answer.truncated = true;
answer.num_digits = max_digits;
}
if ((p != pend) && (('e' == *p) || ('E' == *p))) {
++p;
bool neg_exp = false;
if ((p != pend) && ('-' == *p)) {
neg_exp = true;
++p;
} else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
int32_t exp_number = 0; // exponential part
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
if (exp_number < 0x10000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
answer.decimal_point += (neg_exp ? -exp_number : exp_number);
}
// In very rare cases, we may have fewer than 19 digits, we want to be able to reliably
// assume that all digits up to max_digit_without_overflow have been initialized.
for(uint32_t i = answer.num_digits; i < max_digit_without_overflow; i++) { answer.digits[i] = 0; }
UC const *const first = p;
bool const negative = (*p == UC('-'));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
#pragma warning(disable : 4127)
#endif
if (!std::is_signed<T>::value && negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#endif
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
if ((*p == UC('-')) ||
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
++p;
}
UC const *const start_num = p;
while (p != pend && *p == UC('0')) {
++p;
}
bool const has_leading_zeros = p > start_num;
UC const *const start_digits = p;
uint64_t i = 0;
if (base == 10) {
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
}
while (p != pend) {
uint8_t digit = ch_to_digit(*p);
if (digit >= base) {
break;
}
i = uint64_t(base) * i + digit; // might overflow, check this later
p++;
}
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0) {
if (has_leading_zeros) {
value = 0;
answer.ec = std::errc();
answer.ptr = p;
} else {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
}
return answer;
}
answer.ptr = p;
// check u64 overflow
size_t max_digits = max_digits_u64(base);
if (digit_count > max_digits) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// this check can be eliminated for all other types, but they will all require
// a max_digits(base) equivalent
if (digit_count == max_digits && i < min_safe_u64(base)) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// check other types overflow
if (!std::is_same<T, uint64_t>::value) {
if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
}
if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
#pragma warning(disable : 4146)
#endif
// this weird workaround is required because:
// - converting unsigned to signed when its value is greater than signed max
// is UB pre-C++23.
// - reinterpret_casting (~i + 1) would work, but it is not constexpr
// this is always optimized into a neg instruction (note: T is an integer
// type)
value = T(-std::numeric_limits<T>::max() -
T(i - uint64_t(std::numeric_limits<T>::max())));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#endif
} else {
value = T(i);
}
answer.ec = std::errc();
return answer;
}
} // namespace fast_float
#endif

638
include/fast_float/bigint.h Normal file
View File

@ -0,0 +1,638 @@
#ifndef FASTFLOAT_BIGINT_H
#define FASTFLOAT_BIGINT_H
#include <algorithm>
#include <cstdint>
#include <climits>
#include <cstring>
#include "float_common.h"
namespace fast_float {
// the limb width: we want efficient multiplication of double the bits in
// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
// extract the high and low parts efficiently. this is every 64-bit
// architecture except for sparc, which emulates 128-bit multiplication.
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
// doing `8 * sizeof(limb)`.
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb;
constexpr size_t limb_bits = 64;
#else
#define FASTFLOAT_32BIT_LIMB
typedef uint32_t limb;
constexpr size_t limb_bits = 32;
#endif
typedef span<limb> limb_span;
// number of bits in a bigint. this needs to be at least the number
// of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000.
constexpr size_t bigint_bits = 4000;
constexpr size_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes.
template <uint16_t size> struct stackvec {
limb data[size];
// we never need more than 150 limbs
uint16_t length{0};
stackvec() = default;
stackvec(stackvec const &) = delete;
stackvec &operator=(stackvec const &) = delete;
stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span.
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
FASTFLOAT_ASSERT(try_extend(s));
}
FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
// index from the end of the container
FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1;
return data[rindex];
}
// set the length, without bounds checking.
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
length = uint16_t(len);
}
constexpr size_t len() const noexcept { return length; }
constexpr bool is_empty() const noexcept { return length == 0; }
constexpr size_t capacity() const noexcept { return size; }
// append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value;
length++;
}
// append item to vector, returning if item was added
FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
if (len() < capacity()) {
push_unchecked(value);
return true;
} else {
return false;
}
}
// add items to the vector, from a span, without bounds checking
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb *ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len());
}
// try to add items to the vector, returning if items were added
FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
if (len() + s.len() <= capacity()) {
extend_unchecked(s);
return true;
} else {
return false;
}
}
// resize the vector, without bounds checking
// if the new size is longer than the vector, assign value to each
// appended item.
FASTFLOAT_CONSTEXPR20
void resize_unchecked(size_t new_len, limb value) noexcept {
if (new_len > len()) {
size_t count = new_len - len();
limb *first = data + len();
limb *last = first + count;
::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
}
}
// try to resize the vector, returning if the vector was resized.
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
if (new_len > capacity()) {
return false;
} else {
resize_unchecked(new_len, value);
return true;
}
}
// check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index
// is relative to the most significant limbs.
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
while (index < len()) {
if (rindex(index) != 0) {
return true;
}
index++;
}
return false;
}
// normalize the big integer, so most-significant zero limbs are removed.
FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) {
length--;
}
}
};
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
empty_hi64(bool &truncated) noexcept {
truncated = false;
return 0;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_hi64(uint64_t r0, bool &truncated) noexcept {
truncated = false;
int shl = leading_zeroes(r0);
return r0 << shl;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept {
int shl = leading_zeroes(r0);
if (shl == 0) {
truncated = r1 != 0;
return r0;
} else {
int shr = 64 - shl;
truncated = (r1 << shl) != 0;
return (r0 << shl) | (r1 >> shr);
}
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint32_hi64(uint32_t r0, bool &truncated) noexcept {
return uint64_hi64(r0, truncated);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
return uint64_hi64((x0 << 32) | x1, truncated);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
uint64_t x2 = r2;
return uint64_hi64(x0, (x1 << 32) | x2, truncated);
}
// add two small integers, checking for overflow.
// we want an efficient operation. for msvc, where
// we don't have built-in intrinsics, this is still
// pretty fast.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
scalar_add(limb x, limb y, bool &overflow) noexcept {
limb z;
// gcc and clang
#if defined(__has_builtin)
#if __has_builtin(__builtin_add_overflow)
if (!cpp20_and_in_constexpr()) {
overflow = __builtin_add_overflow(x, y, &z);
return z;
}
#endif
#endif
// generic, this still optimizes correctly on MSVC.
z = x + y;
overflow = z < x;
return z;
}
// multiply two small integers, getting both the high and low bits.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
scalar_mul(limb x, limb y, limb &carry) noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
#if defined(__SIZEOF_INT128__)
// GCC and clang both define it as an extension.
__uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#else
// fallback, no native 128-bit integer multiplication with carry.
// on msvc, this optimizes identically, somehow.
value128 z = full_multiplication(x, y);
bool overflow;
z.low = scalar_add(z.low, carry, overflow);
z.high += uint64_t(overflow); // cannot overflow
carry = z.high;
return z.low;
#endif
#else
uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#endif
}
// add scalar value to bigint starting from offset.
// used in grade school multiplication
template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
size_t start) noexcept {
size_t index = start;
limb carry = y;
bool overflow;
while (carry != 0 && index < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow);
carry = limb(overflow);
index += 1;
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add scalar value to bigint.
template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
small_add(stackvec<size> &vec, limb y) noexcept {
return small_add_from(vec, y, 0);
}
// multiply bigint by scalar value.
template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
limb y) noexcept {
limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) {
vec[index] = scalar_mul(vec[index], y, carry);
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add bigint to bigint starting from index.
// used in grade school multiplication
template <uint16_t size>
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
size_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) {
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
}
bool carry = false;
for (size_t index = 0; index < y.len(); index++) {
limb xi = x[index + start];
limb yi = y[index];
bool c1 = false;
bool c2 = false;
xi = scalar_add(xi, yi, c1);
if (carry) {
xi = scalar_add(xi, 1, c2);
}
x[index + start] = xi;
carry = c1 | c2;
}
// handle overflow
if (carry) {
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
}
return true;
}
// add bigint to bigint.
template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
large_add_from(stackvec<size> &x, limb_span y) noexcept {
return large_add_from(x, y, 0);
}
// grade-school multiplication algorithm
template <uint16_t size>
FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs);
limb_span zs = limb_span(z.data, z.len());
if (y.len() != 0) {
limb y0 = y[0];
FASTFLOAT_TRY(small_mul(x, y0));
for (size_t index = 1; index < y.len(); index++) {
limb yi = y[index];
stackvec<size> zi;
if (yi != 0) {
// re-use the same buffer throughout
zi.set_len(0);
FASTFLOAT_TRY(zi.try_extend(zs));
FASTFLOAT_TRY(small_mul(zi, yi));
limb_span zis = limb_span(zi.data, zi.len());
FASTFLOAT_TRY(large_add_from(x, zis, index));
}
}
}
x.normalize();
return true;
}
// grade-school multiplication algorithm
template <uint16_t size>
FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0]));
} else {
FASTFLOAT_TRY(long_mul(x, y));
}
return true;
}
template <typename = void> struct pow5_tables {
static constexpr uint32_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = {
1UL,
5UL,
25UL,
125UL,
625UL,
3125UL,
15625UL,
78125UL,
390625UL,
1953125UL,
9765625UL,
48828125UL,
244140625UL,
1220703125UL,
6103515625UL,
30517578125UL,
152587890625UL,
762939453125UL,
3814697265625UL,
19073486328125UL,
95367431640625UL,
476837158203125UL,
2384185791015625UL,
11920928955078125UL,
59604644775390625UL,
298023223876953125UL,
1490116119384765625UL,
7450580596923828125UL,
};
#ifdef FASTFLOAT_64BIT_LIMB
constexpr static limb large_power_of_5[] = {
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
10482974169319127550UL, 198276706040285095UL};
#else
constexpr static limb large_power_of_5[] = {
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
#endif
};
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <typename T> constexpr uint32_t pow5_tables<T>::large_step;
template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
template <typename T> constexpr limb pow5_tables<T>::large_power_of_5[];
#endif
// big integer type. implements a small subset of big integer
// arithmetic, using simple algorithms since asymptotically
// faster algorithms are slower for a small number of limbs.
// all operations assume the big-integer is normalized.
struct bigint : pow5_tables<> {
// storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec;
FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
bigint(bigint const &) = delete;
bigint &operator=(bigint const &) = delete;
bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete;
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() {
#ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value);
#else
vec.push_unchecked(uint32_t(value));
vec.push_unchecked(uint32_t(value >> 32));
#endif
vec.normalize();
}
// get the high 64 bits from the vector, and if bits were truncated.
// this is to get the significant digits for the float.
FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint64_hi64(vec.rindex(0), truncated);
} else {
uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
truncated |= vec.nonzero(2);
return result;
}
#else
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint32_hi64(vec.rindex(0), truncated);
} else if (vec.len() == 2) {
return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
} else {
uint64_t result =
uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
truncated |= vec.nonzero(3);
return result;
}
#endif
}
// compare two big integers, returning the large value.
// assumes both are normalized. if the return value is
// negative, other is larger, if the return value is
// positive, this is larger, otherwise they are equal.
// the limbs are stored in little-endian order, so we
// must compare the limbs in ever order.
FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept {
if (vec.len() > other.vec.len()) {
return 1;
} else if (vec.len() < other.vec.len()) {
return -1;
} else {
for (size_t index = vec.len(); index > 0; index--) {
limb xi = vec[index - 1];
limb yi = other.vec[index - 1];
if (xi > yi) {
return 1;
} else if (xi < yi) {
return -1;
}
}
return 0;
}
}
// shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits.
FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to:
// b10100100 b01000010
// b10 b10010001 b00001000
FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
size_t shl = n;
size_t shr = limb_bits - shl;
limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) {
limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr);
prev = xi;
}
limb carry = prev >> shr;
if (carry != 0) {
return vec.try_push(carry);
}
return true;
}
// move the limbs left by `n` limbs.
FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
FASTFLOAT_DEBUG_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) {
return false;
} else if (!vec.is_empty()) {
// move limbs
limb *dst = vec.data + n;
limb const *src = vec.data;
std::copy_backward(src, src + vec.len(), dst + vec.len());
// fill in empty limbs
limb *first = vec.data;
limb *last = first + n;
::std::fill(first, last, 0);
vec.set_len(n + vec.len());
return true;
} else {
return true;
}
}
// move the limbs left by `n` bits.
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
size_t rem = n % limb_bits;
size_t div = n / limb_bits;
if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem));
}
if (div != 0) {
FASTFLOAT_TRY(shl_limbs(div));
}
return true;
}
// get the number of leading zeros in the bigint.
FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
if (vec.is_empty()) {
return 0;
} else {
#ifdef FASTFLOAT_64BIT_LIMB
return leading_zeroes(vec.rindex(0));
#else
// no use defining a specialized leading_zeroes for a 32-bit type.
uint64_t r0 = vec.rindex(0);
return leading_zeroes(r0 << 32);
#endif
}
}
// get the number of bits in the bigint.
FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
int lz = ctlz();
return int(limb_bits * vec.len()) - lz;
}
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
// multiply as if by 2 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); }
// multiply as if by 5 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
// multiply by a power of 5
size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step;
}
#ifdef FASTFLOAT_64BIT_LIMB
uint32_t small_step = 27;
limb max_native = 7450580596923828125UL;
#else
uint32_t small_step = 13;
limb max_native = 1220703125U;
#endif
while (exp >= small_step) {
FASTFLOAT_TRY(small_mul(vec, max_native));
exp -= small_step;
}
if (exp != 0) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
// This is similar to https://github.com/llvm/llvm-project/issues/47746,
// except the workaround described there don't work here
FASTFLOAT_TRY(small_mul(
vec, limb(((void)small_power_of_5[0], small_power_of_5[exp]))));
}
return true;
}
// multiply as if by 10 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
FASTFLOAT_TRY(pow5(exp));
return pow2(exp);
}
};
} // namespace fast_float
#endif

View File

@ -0,0 +1,53 @@
#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
// Testing for https://wg21.link/N3652, adopted in C++14
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
#define FASTFLOAT_CONSTEXPR14 constexpr
#else
#define FASTFLOAT_CONSTEXPR14
#endif
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
#define FASTFLOAT_HAS_BIT_CAST 1
#else
#define FASTFLOAT_HAS_BIT_CAST 0
#endif
#if defined(__cpp_lib_is_constant_evaluated) && \
__cpp_lib_is_constant_evaluated >= 201811L
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
#else
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
#endif
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
#define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x)
#else
#define FASTFLOAT_IF_CONSTEXPR17(x) if (x)
#endif
// Testing for relevant C++20 constexpr library features
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \
defined(__cpp_lib_constexpr_algorithms) && \
__cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
#define FASTFLOAT_CONSTEXPR20 constexpr
#define FASTFLOAT_IS_CONSTEXPR 1
#else
#define FASTFLOAT_CONSTEXPR20
#define FASTFLOAT_IS_CONSTEXPR 0
#endif
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0
#else
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1
#endif
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H

View File

@ -7,33 +7,39 @@
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
namespace fast_float {
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
// the result, with the "high" part corresponding to the most significant bits and the
// low part corresponding to the least significant bits.
// This will compute or rather approximate w * 5**q and return a pair of 64-bit
// words approximating the result, with the "high" part corresponding to the
// most significant bits and the low part corresponding to the least significant
// bits.
//
template <int bit_precision>
fastfloat_really_inline
value128 compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact because
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
// gives the exact answer.
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
constexpr uint64_t precision_mask = (bit_precision < 64) ?
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
compute_product_approximation(int64_t q, uint64_t w) {
int const index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact
// because The line value128 firstproduct = full_multiplication(w,
// power_of_five_128[index]); gives the exact answer.
value128 firstproduct =
full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64),
" precision should be in (0,64]");
constexpr uint64_t precision_mask =
(bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF);
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
if ((firstproduct.high & precision_mask) ==
precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our
// expectation is that the compiler will optimize this extra work away if
// needed.
value128 secondproduct =
full_multiplication(w, powers::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) {
if (secondproduct.high > firstproduct.low) {
firstproduct.high++;
}
}
@ -56,20 +62,45 @@ namespace detail {
* where
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
*/
fastfloat_really_inline int power(int q) noexcept {
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
return (((152170 + 65536) * q) >> 16) + 63;
}
}
} // namespace detail
// w * 10 ** q
// The returned value should be a valid ieee64 number that simply need to be packed.
// However, in some very rare cases, the computation will fail. In such cases, we
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
// in such cases.
// create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q.
template <typename binary>
fastfloat_really_inline
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer;
answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
invalid_am_bias);
return answer;
}
// w * 10 ** q, without rounding the representation up.
// the power2 in the exponent will be adjusted by invalid_am_bias.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w);
w <<= lz;
value128 product =
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
return compute_error_scaled<binary>(q, product.high, lz);
}
// Computers w * 10 ** q.
// The returned value should be a valid number that simply needs to be
// packed. However, in some very rare cases, the computation will fail. In such
// cases, we return an adjusted_mantissa with a negative power of 2: the caller
// should recompute in such cases.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) {
answer.power2 = 0;
@ -83,7 +114,8 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
answer.mantissa = 0;
return answer;
}
// At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
// At this point in time q is in [powers::smallest_power_of_five,
// powers::largest_power_of_five].
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w);
@ -92,32 +124,32 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
// The required precision is binary::mantissa_explicit_bits() + 3 because
// 1. We need the implicit bit
// 2. We need an extra bit for rounding purposes
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
// 3. We might lose a bit due to the "upperbit" routine (result too small,
// requiring a shift)
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further
// In some very rare cases, this could happen, in which case we might need a more accurate
// computation that what we can provide cheaply. This is very, very unlikely.
//
const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0,
// and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation.
if(!inside_safe_exponent) {
answer.power2 = -1; // This (a negative value) indicates an error condition.
return answer;
}
}
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
// value128 product = compute_product(q, w);
// but in practice, we can win big with the compute_product_approximation if its additional branch
// is easily predicted. Which is best is data specific.
value128 product =
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
// The computed 'product' is always sufficient.
// Mathematical proof:
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to
// appear) See script/mushtak_lemire.py
// The "compute_product_approximation" function can be slightly slower than a
// branchless approach: value128 product = compute_product(q, w); but in
// practice, we can win big with the compute_product_approximation if its
// additional branch is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63);
int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
answer.mantissa = product.high >> shift;
answer.power2 = int(detail::power(int(q)) + upperbit - lz - binary::minimum_exponent());
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz -
binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal?
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
if (-answer.power2 + 1 >=
64) { // if we have more than 64 bits below the minimum exponent, you
// have a zero for sure.
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
@ -126,7 +158,8 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
// next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
// "round-to-even" only occurs for powers close to 0 in the 32-bit and
// and 64-bit case (with no more than 19 digits).
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
@ -136,19 +169,25 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
answer.power2 =
(answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits()))
? 0
: 1;
return answer;
}
// usually, we round *up*, but if we fall right in between and and we have an
// even basis, we need to round down
// We are only concerned with the cases where 5**q fits in single 64-bit word.
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) &&
(q <= binary::max_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1)) { // we may fall between two floats!
// To be in-between two floats we need that in doing
// answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
// ... we dropped out only zeroes. But if this happened, then we can go back!!!
if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) {
// answer.mantissa = product.high >> (upperbit + 64 -
// binary::mantissa_explicit_bits() - 3);
// ... we dropped out only zeroes. But if this happened, then we can go
// back!!!
if ((answer.mantissa << shift) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
@ -168,7 +207,6 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
return answer;
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,455 @@
#ifndef FASTFLOAT_DIGIT_COMPARISON_H
#define FASTFLOAT_DIGIT_COMPARISON_H
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
#include "float_common.h"
#include "bigint.h"
#include "ascii_number.h"
namespace fast_float {
// 1e0 to 1e19
constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
10UL,
100UL,
1000UL,
10000UL,
100000UL,
1000000UL,
10000000UL,
100000000UL,
1000000000UL,
10000000000UL,
100000000000UL,
1000000000000UL,
10000000000000UL,
100000000000000UL,
1000000000000000UL,
10000000000000000UL,
100000000000000000UL,
1000000000000000000UL,
10000000000000000000UL};
// calculate the exponent, in scientific notation, of the number.
// this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast.
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
while (mantissa >= 10000) {
mantissa /= 10000;
exponent += 4;
}
while (mantissa >= 100) {
mantissa /= 100;
exponent += 2;
}
while (mantissa >= 10) {
mantissa /= 10;
exponent += 1;
}
return exponent;
}
// this converts a native floating-point number to an extended-precision float.
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
to_extended(T value) noexcept {
using equiv_uint = equiv_uint_t<T>;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent();
equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value);
#else
::memcpy(&bits, &value, sizeof(T));
#endif
if ((bits & exponent_mask) == 0) {
// denormal
am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >>
binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
}
return am;
}
// get the extended precision value of the halfway point between b and b+u.
// we are given a native float that represents b, so we need to adjust it
// halfway between b and b+u.
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
to_extended_halfway(T value) noexcept {
adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1;
am.mantissa += 1;
am.power2 -= 1;
return am;
}
// round an extended-precision float to the nearest machine float.
template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) {
// have a denormal float
int32_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa <
(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
? 0
: 1;
return;
}
// have a normal float, use the default shift.
cb(am, mantissa_shift);
// check for carry
if (am.mantissa >=
(uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++;
}
// check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0;
}
}
template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
callback cb) noexcept {
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
uint64_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway;
// shift digits into position
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
round_down(adjusted_mantissa &am, int32_t shift) noexcept {
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
skip_zeros(UC const *&first, UC const *last) noexcept {
uint64_t val;
while (!cpp20_and_in_constexpr() &&
std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
break;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
break;
}
first++;
}
}
// determine if any non-zero digits were truncated.
// all characters must be valid digits.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
is_truncated(UC const *first, UC const *last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val;
while (!cpp20_and_in_constexpr() &&
std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
return true;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
return true;
}
++first;
}
return false;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
is_truncated(span<UC const> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len());
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
parse_eight_digits(UC const *&p, limb &value, size_t &counter,
size_t &count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
counter += 8;
count += 8;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
parse_one_digit(UC const *&p, limb &value, size_t &counter,
size_t &count) noexcept {
value = value * 10 + limb(*p - UC('0'));
p++;
counter++;
count++;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
add_native(bigint &big, limb power, limb value) noexcept {
big.mul(power);
big.add(value);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
round_up_bigint(bigint &big, size_t &count) noexcept {
// need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1);
count++;
}
// parse the significant digits into a big integer
template <typename UC>
inline FASTFLOAT_CONSTEXPR20 void
parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
size_t max_digits, size_t &digits) noexcept {
// try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step.
size_t counter = 0;
digits = 0;
limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB
size_t step = 19;
#else
size_t step = 9;
#endif
// process all integer digits.
UC const *p = num.integer.ptr;
UC const *pend = p + num.integer.len();
skip_zeros(p, pend);
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
(max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (num.fraction.ptr != nullptr) {
truncated |= is_truncated(num.fraction);
}
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
// add our fraction digits, if they're available.
if (num.fraction.ptr != nullptr) {
p = num.fraction.ptr;
pend = p + num.fraction.len();
if (digits == 0) {
skip_zeros(p, pend);
}
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
(max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
}
if (counter != 0) {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
}
}
template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
adjusted_mantissa answer;
bool truncated;
answer.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent();
answer.power2 = bigmant.bit_length() - 64 + bias;
round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) {
round_nearest_tie_even(
a, shift,
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
return is_above || (is_halfway && truncated) ||
(is_odd && is_halfway);
});
});
return answer;
}
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
// and for the theoretical digits `n * 2^f`. Since `e` is always negative,
// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
// we then need to scale by `2^(f- e)`, and then the two significant digits
// are of the same magnitude.
template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
bigint &real_digits = bigmant;
int32_t real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h
adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with
// -Wnoexcept-type.
round<T>(am_b,
[](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
T b;
to_float(false, am_b, b);
adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2;
// scale real digits and theor digits to be same power.
int32_t pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp);
if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
}
if (pow2_exp > 0) {
FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
} else if (pow2_exp < 0) {
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
}
// compare digits, and use it to direct rounding
int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
round_nearest_tie_even(
a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
(void)_; // not needed, since we've done our comparison
(void)__; // not needed, since we've done our comparison
if (ord > 0) {
return true;
} else if (ord < 0) {
return false;
} else {
return is_odd;
}
});
});
return answer;
}
// parse the significant digits as a big integer to unambiguously round
// the significant digits. here, we are trying to determine how to round
// an extended float representation close to `b+h`, halfway between `b`
// (the float rounded-down) and `b+u`, the next positive float. this
// algorithm is always correct, and uses one of two approaches. when
// the exponent is positive relative to the significant digits (such as
// 1234), we create a big-integer representation, get the high 64-bits,
// determine if any lower bits are truncated, and use that to direct
// rounding. in case of a negative exponent relative to the significant
// digits (such as 1.2345), we create a theoretical representation of
// `b` as a big-integer type, scaled to the same binary exponent as
// the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding.
template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias
am.power2 -= invalid_am_bias;
int32_t sci_exp =
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
bigint bigmant;
parse_mantissa(bigmant, num, max_digits, digits);
// can't underflow, since digits is at most max_digits.
int32_t exponent = sci_exp + 1 - int32_t(digits);
if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, exponent);
} else {
return negative_digit_comp<T>(bigmant, am, exponent);
}
}
} // namespace fast_float
#endif

View File

@ -1,63 +1,91 @@
#ifndef FASTFLOAT_FAST_FLOAT_H
#define FASTFLOAT_FAST_FLOAT_H
#include <system_error>
#include "float_common.h"
namespace fast_float {
enum chars_format {
scientific = 1<<0,
fixed = 1<<2,
hex = 1<<3,
general = fixed | scientific
};
struct from_chars_result {
const char *ptr;
std::errc ec;
};
struct parse_options {
explicit parse_options(chars_format fmt = chars_format::general,
char dot = '.')
: format(fmt), decimal_point(dot) {}
/** Which number formats are accepted */
chars_format format;
/** The character used as decimal point */
char decimal_point;
};
/**
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
* The resulting floating-point value is the closest floating-point values (using either float or double),
* using the "round to even" convention for values that would otherwise fall right in-between two values.
* That is, we provide exact parsing according to the IEEE standard.
* This function parses the character sequence [first,last) for a number. It
* parses floating-point numbers expecting a locale-indepent format equivalent
* to what is used by std::strtod in the default ("C") locale. The resulting
* floating-point value is the closest floating-point values (using either float
* or double), using the "round to even" convention for values that would
* otherwise fall right in-between two values. That is, we provide exact parsing
* according to the IEEE standard.
*
* Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
* parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
* `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
* Given a successful parse, the pointer (`ptr`) in the returned value is set to
* point right after the parsed number, and the `value` referenced is set to the
* parsed value. In case of error, the returned `ec` contains a representative
* error, otherwise the default (`std::errc()`) value is stored.
*
* The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
* The implementation does not throw and does not allocate memory (e.g., with
* `new` or `malloc`).
*
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
* the type `fast_float::chars_format`. It is a bitset value: we check whether
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
* to determine whether we allowe the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
* Like the C++17 standard, the `fast_float::from_chars` functions take an
* optional last argument of the type `fast_float::chars_format`. It is a bitset
* value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt &
* fast_float::chars_format::scientific` are set to determine whether we allow
* the fixed point and scientific notation respectively. The default is
* `fast_float::chars_format::general` which allows both `fixed` and
* `scientific`.
*/
template<typename T>
from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt = chars_format::general) noexcept;
template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value,
chars_format fmt = chars_format::general) noexcept;
/**
* Like from_chars, but accepts an `options` argument to govern number parsing.
* Both for floating-point types and integer types.
*/
template<typename T>
from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept;
template <typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept;
/**
* This function multiplies an integer number by a power of 10 and returns
* the result as a double precision floating-point value that is correctly
* rounded. The resulting floating-point value is the closest floating-point
* value, using the "round to nearest, tie to even" convention for values that
* would otherwise fall right in-between two values. That is, we provide exact
* conversion according to the IEEE standard.
*
* On overflow infinity is returned, on underflow 0 is returned.
*
* The implementation does not throw and does not allocate memory (e.g., with
* `new` or `malloc`).
*/
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
/**
* This function is a template overload of `integer_times_pow10()`
* that returns a floating-point value of type `T` that is one of
* supported floating-point types (e.g. `double`, `float`).
*/
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
/**
* from_chars for integer types.
*/
template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
} // namespace fast_float
}
#include "parse_number.h"
#endif // FASTFLOAT_FAST_FLOAT_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
#ifndef FASTFLOAT_PARSE_NUMBER_H
#define FASTFLOAT_PARSE_NUMBER_H
#include "ascii_number.h"
#include "decimal_to_binary.h"
#include "simple_decimal_conversion.h"
#include "digit_comparison.h"
#include "float_common.h"
#include <cassert>
#include <cmath>
#include <cstring>
#include <limits>
@ -12,47 +13,56 @@
namespace fast_float {
namespace detail {
/**
* Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the
* strings a null-free and fixed.
**/
template <typename T>
from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
from_chars_result answer;
template <typename T, typename UC>
from_chars_result_t<UC>
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
T &value, chars_format fmt) noexcept {
from_chars_result_t<UC> answer{};
answer.ptr = first;
answer.ec = std::errc(); // be optimistic
bool minusSign = false;
if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
minusSign = true;
// assume first < last, so dereference without checks;
bool const minusSign = (*first == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*first == UC('-')) ||
(uint64_t(fmt & chars_format::allow_leading_plus) &&
(*first == UC('+')))) {
++first;
}
if (last - first >= 3) {
if (fastfloat_strncasecmp(first, "nan", 3)) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if(first != last && *first == '(') {
for(const char* ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == ')') {
value = minusSign ? -std::numeric_limits<T>::quiet_NaN()
: std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7,
// C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if (first != last && *first == UC('(')) {
for (UC const *ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == UC(')')) {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break;
}
else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_'))
} else if (!((UC('a') <= *ptr && *ptr <= UC('z')) ||
(UC('A') <= *ptr && *ptr <= UC('Z')) ||
(UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
break; // forbidden char, not nan(n-char-seq-opt)
}
}
return answer;
}
if (fastfloat_strncasecmp(first, "inf", 3)) {
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) {
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
if ((last - first >= 8) &&
fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
answer.ptr = first + 8;
} else {
answer.ptr = first + 3;
}
value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
value = minusSign ? -std::numeric_limits<T>::infinity()
: std::numeric_limits<T>::infinity();
return answer;
}
}
@ -60,72 +70,416 @@ from_chars_result parse_infnan(const char *first, const char *last, T &value) n
return answer;
}
template<typename T>
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
uint64_t word = am.mantissa;
word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();
word = negative
? word | (uint64_t(1) << binary_format<T>::sign_index()) : word;
#if FASTFLOAT_IS_BIG_ENDIAN == 1
if (std::is_same<T, float>::value) {
::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian
} else {
::memcpy(&value, &word, sizeof(T));
}
#else
// For little-endian systems:
::memcpy(&value, &word, sizeof(T));
/**
* Returns true if the floating-pointing rounding mode is to 'nearest'.
* It is the default on most system. This function is meant to be inexpensive.
* Credit : @mwalcott3
*/
fastfloat_really_inline bool rounds_to_nearest() noexcept {
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return false;
#endif
// See
// A fast function to check your floating-point rounding mode
// https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
//
// This function is meant to be equivalent to :
// prior: #include <cfenv>
// return fegetround() == FE_TONEAREST;
// However, it is expected to be much faster than the fegetround()
// function call.
//
// The volatile keyword prevents the compiler from computing the function
// at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g.,
// asm). The value does not need to be std::numeric_limits<float>::min(), any
// small value so that 1 + x should round to 1 would do (after accounting for
// excess precision, as in 387 instructions).
static float volatile fmin = std::numeric_limits<float>::min();
float fmini = fmin; // we copy it so that it gets loaded at most once.
//
// Explanation:
// Only when fegetround() == FE_TONEAREST do we have that
// fmin + 1.0f == 1.0f - fmin.
//
// FE_UPWARD:
// fmin + 1.0f > 1
// 1.0f - fmin == 1
//
// FE_DOWNWARD or FE_TOWARDZERO:
// fmin + 1.0f == 1
// 1.0f - fmin < 1
//
// Note: This may fail to be accurate if fast-math has been
// enabled, as rounding conventions may not apply.
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
// todo: is there a VS warning?
// see
// https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
return (fmini + 1.0f == 1.0f - fmini);
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
}
} // namespace detail
template <typename T> struct from_chars_caller {
template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
return from_chars_advanced(first, last, value, options);
}
};
#ifdef __STDCPP_FLOAT32_T__
template <> struct from_chars_caller<std::float32_t> {
template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, std::float32_t &value,
parse_options_t<UC> options) noexcept {
// if std::float32_t is defined, and we are in C++23 mode; macro set for
// float32; set value to float due to equivalence between float and
// float32_t
float val;
auto ret = from_chars_advanced(first, last, val, options);
value = val;
return ret;
}
};
#endif
template<typename T>
from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_advanced(first, last, value, parse_options{fmt});
#ifdef __STDCPP_FLOAT64_T__
template <> struct from_chars_caller<std::float64_t> {
template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, std::float64_t &value,
parse_options_t<UC> options) noexcept {
// if std::float64_t is defined, and we are in C++23 mode; macro set for
// float64; set value as double due to equivalence between double and
// float64_t
double val;
auto ret = from_chars_advanced(first, last, val, options);
value = val;
return ret;
}
};
#endif
template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value,
chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_caller<T>::call(first, last, value,
parse_options_t<UC>(fmt));
}
template<typename T>
from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept {
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
T &value) noexcept {
// The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest()
// returns true.
if (binary_format<T>::min_exponent_fast_path() <= exponent &&
exponent <= binary_format<T>::max_exponent_fast_path()) {
// Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float.
//
// We expect the next branch to almost always be selected.
// We could check it first (before the previous branch), but
// there might be performance advantages at having the check
// be last.
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path.
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
value = T(mantissa);
if (exponent < 0) {
value = value / binary_format<T>::exact_power_of_ten(-exponent);
} else {
value = value * binary_format<T>::exact_power_of_ten(exponent);
}
if (is_negative) {
value = -value;
}
return true;
}
} else {
// We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
// proposal
if (exponent >= 0 &&
mantissa <= binary_format<T>::max_mantissa_fast_path(exponent)) {
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if (mantissa == 0) {
value = is_negative ? T(-0.) : T(0.);
return true;
}
#endif
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
if (is_negative) {
value = -value;
}
return true;
}
}
}
return false;
}
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
/**
* This function overload takes parsed_number_string_t structure that is created
* and populated either by from_chars_advanced function taking chars range and
* parsing options or other parsing custom function implemented by user.
*/
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
from_chars_result answer;
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
if (!pns.too_many_digits &&
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value))
return answer;
adjusted_mantissa am =
compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if (pns.too_many_digits && am.power2 >= 0) {
if (am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
}
}
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
// and we have an invalid power (am.power2 < 0), then we need to go the long
// way around again. This is very uncommon.
if (am.power2 < 0) {
am = digit_comp<T>(pns, am);
}
to_float(pns.negative, am, value);
// Test for over/underflow.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
am.power2 == binary_format<T>::infinite_power()) {
answer.ec = std::errc::result_out_of_range;
}
return answer;
}
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_float_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
from_chars_result_t<UC> answer;
if (uint64_t(fmt & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) {
first++;
}
}
if (first == last) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
parsed_number_string pns = parse_number_string(first, last, options);
parsed_number_string_t<UC> pns =
uint64_t(fmt & detail::basic_json_fmt)
? parse_number_string<true, UC>(first, last, options)
: parse_number_string<false, UC>(first, last, options);
if (!pns.valid) {
return detail::parse_infnan(first, last, value);
if (uint64_t(fmt & chars_format::no_infnan)) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
} else {
return detail::parse_infnan(first, last, value, fmt);
}
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
// Next is Clinger's fast path.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) {
value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
if (pns.negative) { value = -value; }
}
// call overload that takes parsed_number_string_t directly.
return from_chars_advanced(pns, value);
}
template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
static_assert(is_supported_integer_type<T>::value,
"only integer types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
parse_options_t<UC> options;
options.base = base;
return from_chars_advanced(first, last, value, options);
}
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
T value;
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
return value;
adjusted_mantissa am =
compute_float<binary_format<T>>(decimal_exponent, mantissa);
to_float(false, am, value);
return value;
}
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
const bool is_negative = mantissa < 0;
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
T value;
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
return value;
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m);
to_float(is_negative, am, value);
return value;
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
// the following overloads are here to avoid surprising ambiguity for int,
// unsigned, etc.
template <typename T, typename Int>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value &&
std::is_integral<Int>::value &&
!std::is_signed<Int>::value,
T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
decimal_exponent);
}
template <typename T, typename Int>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value &&
std::is_integral<Int>::value &&
std::is_signed<Int>::value,
T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
decimal_exponent);
}
template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
}
template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
}
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_int_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
static_assert(is_supported_integer_type<T>::value,
"only integer types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
from_chars_result_t<UC> answer;
if (uint64_t(fmt & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) {
first++;
}
}
if (first == last || base < 2 || base > 36) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(pns.too_many_digits) {
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
am.power2 = -1; // value is invalid.
return parse_int_string(first, last, value, options);
}
template <size_t TypeIx> struct from_chars_advanced_caller {
static_assert(TypeIx > 0, "unsupported type");
};
template <> struct from_chars_advanced_caller<1> {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
return from_chars_float_advanced(first, last, value, options);
}
};
template <> struct from_chars_advanced_caller<2> {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
return from_chars_int_advanced(first, last, value, options);
}
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
// then we need to go the long way around again. This is very uncommon.
if(am.power2 < 0) { am = parse_long_mantissa<binary_format<T>>(first, last, options); }
detail::to_float(pns.negative, am, value);
return answer;
};
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
return from_chars_advanced_caller<
size_t(is_supported_float_type<T>::value) +
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
options);
}
} // namespace fast_float

View File

@ -1,360 +0,0 @@
#ifndef FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H
#define FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H
/**
* This code is meant to handle the case where we have more than 19 digits.
*
* It is based on work by Nigel Tao (at https://github.com/google/wuffs/)
* who credits Ken Thompson for the design (via a reference to the Go source
* code).
*
* Rob Pike suggested that this algorithm be called "Simple Decimal Conversion".
*
* It is probably not very fast but it is a fallback that should almost never
* be used in real life. Though it is not fast, it is "easily" understood and debugged.
**/
#include "ascii_number.h"
#include "decimal_to_binary.h"
#include <cstdint>
namespace fast_float {
namespace detail {
// remove all final zeroes
inline void trim(decimal &h) {
while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) {
h.num_digits--;
}
}
inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) {
shift &= 63;
const static uint16_t number_of_digits_decimal_left_shift_table[65] = {
0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,
0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067,
0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF,
0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0,
0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA,
0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC,
0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C,
0x051C, 0x051C,
};
uint32_t x_a = number_of_digits_decimal_left_shift_table[shift];
uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1];
uint32_t num_new_digits = x_a >> 11;
uint32_t pow5_a = 0x7FF & x_a;
uint32_t pow5_b = 0x7FF & x_b;
const static uint8_t
number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = {
5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3,
9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8,
1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1,
0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8,
7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6,
9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5,
3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3,
1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0,
9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3,
9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1,
4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8,
0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4,
6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5,
7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5,
6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2,
5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5,
1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5,
5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5,
2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2,
5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0,
6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2,
0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1,
6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6,
4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, 2,
9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7,
3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5,
6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9,
3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8,
0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0,
9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2,
5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2,
4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0,
0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7,
1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8,
9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4,
6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1,
9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5,
6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9,
4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4,
9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4,
0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4,
2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1,
5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5,
4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1,
3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1,
3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9,
5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0,
3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6, 2,
6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5,
1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2,
4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1,
7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5,
9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5,
};
const uint8_t *pow5 =
&number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a];
uint32_t i = 0;
uint32_t n = pow5_b - pow5_a;
for (; i < n; i++) {
if (i >= h.num_digits) {
return num_new_digits - 1;
} else if (h.digits[i] == pow5[i]) {
continue;
} else if (h.digits[i] < pow5[i]) {
return num_new_digits - 1;
} else {
return num_new_digits;
}
}
return num_new_digits;
}
inline uint64_t round(decimal &h) {
if ((h.num_digits == 0) || (h.decimal_point < 0)) {
return 0;
} else if (h.decimal_point > 18) {
return UINT64_MAX;
}
// at this point, we know that h.decimal_point >= 0
uint32_t dp = uint32_t(h.decimal_point);
uint64_t n = 0;
for (uint32_t i = 0; i < dp; i++) {
n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0);
}
bool round_up = false;
if (dp < h.num_digits) {
round_up = h.digits[dp] >= 5; // normally, we round up
// but we may need to round to even!
if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) {
round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1]));
}
}
if (round_up) {
n++;
}
return n;
}
// computes h * 2^-shift
inline void decimal_left_shift(decimal &h, uint32_t shift) {
if (h.num_digits == 0) {
return;
}
uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift);
int32_t read_index = int32_t(h.num_digits - 1);
uint32_t write_index = h.num_digits - 1 + num_new_digits;
uint64_t n = 0;
while (read_index >= 0) {
n += uint64_t(h.digits[read_index]) << shift;
uint64_t quotient = n / 10;
uint64_t remainder = n - (10 * quotient);
if (write_index < max_digits) {
h.digits[write_index] = uint8_t(remainder);
} else if (remainder > 0) {
h.truncated = true;
}
n = quotient;
write_index--;
read_index--;
}
while (n > 0) {
uint64_t quotient = n / 10;
uint64_t remainder = n - (10 * quotient);
if (write_index < max_digits) {
h.digits[write_index] = uint8_t(remainder);
} else if (remainder > 0) {
h.truncated = true;
}
n = quotient;
write_index--;
}
h.num_digits += num_new_digits;
if (h.num_digits > max_digits) {
h.num_digits = max_digits;
}
h.decimal_point += int32_t(num_new_digits);
trim(h);
}
// computes h * 2^shift
inline void decimal_right_shift(decimal &h, uint32_t shift) {
uint32_t read_index = 0;
uint32_t write_index = 0;
uint64_t n = 0;
while ((n >> shift) == 0) {
if (read_index < h.num_digits) {
n = (10 * n) + h.digits[read_index++];
} else if (n == 0) {
return;
} else {
while ((n >> shift) == 0) {
n = 10 * n;
read_index++;
}
break;
}
}
h.decimal_point -= int32_t(read_index - 1);
if (h.decimal_point < -decimal_point_range) { // it is zero
h.num_digits = 0;
h.decimal_point = 0;
h.negative = false;
h.truncated = false;
return;
}
uint64_t mask = (uint64_t(1) << shift) - 1;
while (read_index < h.num_digits) {
uint8_t new_digit = uint8_t(n >> shift);
n = (10 * (n & mask)) + h.digits[read_index++];
h.digits[write_index++] = new_digit;
}
while (n > 0) {
uint8_t new_digit = uint8_t(n >> shift);
n = 10 * (n & mask);
if (write_index < max_digits) {
h.digits[write_index++] = new_digit;
} else if (new_digit > 0) {
h.truncated = true;
}
}
h.num_digits = write_index;
trim(h);
}
} // namespace detail
template <typename binary>
adjusted_mantissa compute_float(decimal &d) {
adjusted_mantissa answer;
if (d.num_digits == 0) {
// should be zero
answer.power2 = 0;
answer.mantissa = 0;
return answer;
}
// At this point, going further, we can assume that d.num_digits > 0.
//
// We want to guard against excessive decimal point values because
// they can result in long running times. Indeed, we do
// shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22
// which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not
// fine (runs for a long time).
//
if(d.decimal_point < -324) {
// We have something smaller than 1e-324 which is always zero
// in binary64 and binary32.
// It should be zero.
answer.power2 = 0;
answer.mantissa = 0;
return answer;
} else if(d.decimal_point >= 310) {
// We have something at least as large as 0.1e310 which is
// always infinite.
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
static const uint32_t max_shift = 60;
static const uint32_t num_powers = 19;
static const uint8_t decimal_powers[19] = {
0, 3, 6, 9, 13, 16, 19, 23, 26, 29, //
33, 36, 39, 43, 46, 49, 53, 56, 59, //
};
int32_t exp2 = 0;
while (d.decimal_point > 0) {
uint32_t n = uint32_t(d.decimal_point);
uint32_t shift = (n < num_powers) ? decimal_powers[n] : max_shift;
detail::decimal_right_shift(d, shift);
if (d.decimal_point < -decimal_point_range) {
// should be zero
answer.power2 = 0;
answer.mantissa = 0;
return answer;
}
exp2 += int32_t(shift);
}
// We shift left toward [1/2 ... 1].
while (d.decimal_point <= 0) {
uint32_t shift;
if (d.decimal_point == 0) {
if (d.digits[0] >= 5) {
break;
}
shift = (d.digits[0] < 2) ? 2 : 1;
} else {
uint32_t n = uint32_t(-d.decimal_point);
shift = (n < num_powers) ? decimal_powers[n] : max_shift;
}
detail::decimal_left_shift(d, shift);
if (d.decimal_point > decimal_point_range) {
// we want to get infinity:
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
exp2 -= int32_t(shift);
}
// We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2].
exp2--;
constexpr int32_t minimum_exponent = binary::minimum_exponent();
while ((minimum_exponent + 1) > exp2) {
uint32_t n = uint32_t((minimum_exponent + 1) - exp2);
if (n > max_shift) {
n = max_shift;
}
detail::decimal_right_shift(d, n);
exp2 += int32_t(n);
}
if ((exp2 - minimum_exponent) >= binary::infinite_power()) {
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1;
detail::decimal_left_shift(d, mantissa_size_in_bits);
uint64_t mantissa = detail::round(d);
// It is possible that we have an overflow, in which case we need
// to shift back.
if(mantissa >= (uint64_t(1) << mantissa_size_in_bits)) {
detail::decimal_right_shift(d, 1);
exp2 += 1;
mantissa = detail::round(d);
if ((exp2 - minimum_exponent) >= binary::infinite_power()) {
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
}
answer.power2 = exp2 - binary::minimum_exponent();
if(mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { answer.power2--; }
answer.mantissa = mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1);
return answer;
}
template <typename binary>
adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) {
decimal d = parse_decimal(first, last, options);
return compute_float<binary>(d);
}
} // namespace fast_float
#endif

View File

@ -1,56 +1,122 @@
# text parts
processed_files = { }
processed_files = {}
# authors
for filename in ['AUTHORS', 'CONTRIBUTORS']:
with open(filename) as f:
text = ''
for filename in ["AUTHORS", "CONTRIBUTORS"]:
with open(filename, encoding="utf8") as f:
text = ""
for line in f:
if filename == 'AUTHORS':
text += '// fast_float by ' + line
if filename == 'CONTRIBUTORS':
text += '// with contributions from ' + line
processed_files[filename] = text
if filename == "AUTHORS":
text += "// fast_float by " + line
if filename == "CONTRIBUTORS":
text += "// with contributions from " + line
processed_files[filename] = text + "//\n//\n"
# licenses
for filename in ['LICENSE-MIT', 'LICENSE-APACHE']:
with open(filename) as f:
text = ''
for line in f:
text += '// ' + line
for filename in ["LICENSE-MIT", "LICENSE-APACHE", "LICENSE-BOOST"]:
lines = []
with open(filename, encoding="utf8") as f:
lines = f.readlines()
# Retrieve subset required for inclusion in source
if filename == "LICENSE-APACHE":
lines = [" Copyright 2021 The fast_float authors\n", *lines[179:-1]]
text = ""
for line in lines:
line = line.strip()
if len(line):
line = " " + line
text += "//" + line + "\n"
processed_files[filename] = text
# code
for filename in [ 'fast_float.h', 'float_common.h', 'ascii_number.h',
'fast_table.h', 'decimal_to_binary.h', 'ascii_number.h',
'simple_decimal_conversion.h', 'parse_number.h']:
with open('include/fast_float/' + filename) as f:
text = ''
for filename in [
"constexpr_feature_detect.h",
"float_common.h",
"fast_float.h",
"ascii_number.h",
"fast_table.h",
"decimal_to_binary.h",
"bigint.h",
"digit_comparison.h",
"parse_number.h",
]:
with open("include/fast_float/" + filename, encoding="utf8") as f:
text = ""
for line in f:
if line.startswith('#include "'): continue
if line.startswith('#include "'):
continue
text += line
processed_files[filename] = text
processed_files[filename] = "\n" + text
# command line
import argparse
parser = argparse.ArgumentParser(description='Amalgamate fast_float.')
parser.add_argument('--license', default='MIT', help='choose license')
parser.add_argument('--output', default='', help='output file (stdout if none')
parser = argparse.ArgumentParser(description="Amalgamate fast_float.")
parser.add_argument(
"--license",
default="TRIPLE",
choices=["DUAL", "TRIPLE", "MIT", "APACHE", "BOOST"],
help="choose license",
)
parser.add_argument("--output", default="", help="output file (stdout if none")
args = parser.parse_args()
text = '\n\n'.join([
processed_files['AUTHORS'], processed_files['CONTRIBUTORS'],
processed_files['LICENSE-' + args.license],
processed_files['fast_float.h'], processed_files['float_common.h'],
processed_files['ascii_number.h'], processed_files['fast_table.h'],
processed_files['decimal_to_binary.h'], processed_files['ascii_number.h'],
processed_files['simple_decimal_conversion.h'],
processed_files['parse_number.h']])
def license_content(license_arg):
result = []
if license_arg == "TRIPLE":
result += [
"// Licensed under the Apache License, Version 2.0, or the\n",
"// MIT License or the Boost License. This file may not be copied,\n",
"// modified, or distributed except according to those terms.\n",
"//\n",
]
if license_arg == "DUAL":
result += [
"// Licensed under the Apache License, Version 2.0, or the\n",
"// MIT License at your option. This file may not be copied,\n",
"// modified, or distributed except according to those terms.\n",
"//\n",
]
if license_arg in ("DUAL", "TRIPLE", "MIT"):
result.append("// MIT License Notice\n//\n")
result.append(processed_files["LICENSE-MIT"])
result.append("//\n")
if license_arg in ("DUAL", "TRIPLE", "APACHE"):
result.append("// Apache License (Version 2.0) Notice\n//\n")
result.append(processed_files["LICENSE-APACHE"])
result.append("//\n")
if license_arg in ("TRIPLE", "BOOST"):
result.append("// BOOST License Notice\n//\n")
result.append(processed_files["LICENSE-BOOST"])
result.append("//\n")
return result
text = "".join(
[
processed_files["AUTHORS"],
processed_files["CONTRIBUTORS"],
*license_content(args.license),
processed_files["constexpr_feature_detect.h"],
processed_files["float_common.h"],
processed_files["fast_float.h"],
processed_files["ascii_number.h"],
processed_files["fast_table.h"],
processed_files["decimal_to_binary.h"],
processed_files["bigint.h"],
processed_files["digit_comparison.h"],
processed_files["parse_number.h"],
]
)
if args.output:
with open(args.output, 'wt') as f:
with open(args.output, "wt", encoding="utf8") as f:
f.write(text)
else:
print(text)

View File

@ -1,36 +1,38 @@
import sys
from math import floor
def log2(x):
"""returns ceil(log2(x)))"""
y = 0
while((1<<y) < x):
while (1 << y) < x:
y = y + 1
return y
for q in range(1,17+1):
d = 5**q
for q in range(1, 17 + 1):
d = 5 ** q
b = 127 + log2(d)
t = 2** b
c = t//d + 1
assert c < 2**128
assert c >= 2**127
K = 2**127
if(not(c * K * d<=( K + 1) * t)):
t = 2 ** b
c = t // d + 1
assert c < 2 ** 128
assert c >= 2 ** 127
K = 2 ** 127
if not (c * K * d <= (K + 1) * t):
print(q)
top = floor(t/(c * d - t))
top = floor(t / (c * d - t))
sys.exit(-1)
for q in range(18, 344+1):
d = 5**q
b = 64 + 2*log2(d)
t = 2**b
c = t//d + 1
assert c > 2**(64 +log2(d))
K = 2**64
if(not(c * K * d<=( K + 1) * t)):
for q in range(18, 344 + 1):
d = 5 ** q
b = 64 + 2 * log2(d)
t = 2 ** b
c = t // d + 1
assert c > 2 ** (64 + log2(d))
K = 2 ** 64
if not (c * K * d <= (K + 1) * t):
print(q)
top = floor(t/(c * d - t))
top = floor(t / (c * d - t))
sys.exit(-1)
print("all good")

81
script/mushtak_lemire.py Normal file
View File

@ -0,0 +1,81 @@
#
# Reference :
# Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear)
#
all_tqs = []
# Generates all possible values of T[q]
# Appendix B of Number parsing at a gigabyte per second.
# Software: Practice and Experience 2021;51(8):17001727.
for q in range(-342, -27):
power5 = 5 ** -q
z = 0
while (1 << z) < power5:
z += 1
b = 2 * z + 2 * 64
c = 2 ** b // power5 + 1
while c >= (1 << 128):
c //= 2
all_tqs.append(c)
for q in range(-27, 0):
power5 = 5 ** -q
z = 0
while (1 << z) < power5:
z += 1
b = z + 127
c = 2 ** b // power5 + 1
all_tqs.append(c)
for q in range(0, 308 + 1):
power5 = 5 ** q
while power5 < (1 << 127):
power5 *= 2
while power5 >= (1 << 128):
power5 //= 2
all_tqs.append(power5)
# Returns the continued fraction of numer/denom as a list [a0; a1, a2, ..., an]
def continued_fraction(numer, denom):
# (look at page numbers in top-left, not PDF page numbers)
cf = []
while denom != 0:
quot, rem = divmod(numer, denom)
cf.append(quot)
numer, denom = denom, rem
return cf
# Given a continued fraction [a0; a1, a2, ..., an], returns
# all the convergents of that continued fraction
# as pairs of the form (numer, denom), where numer/denom is
# a convergent of the continued fraction in simple form.
def convergents(cf):
p_n_minus_2 = 0
q_n_minus_2 = 1
p_n_minus_1 = 1
q_n_minus_1 = 0
convergents = []
for a_n in cf:
p_n = a_n * p_n_minus_1 + p_n_minus_2
q_n = a_n * q_n_minus_1 + q_n_minus_2
convergents.append((p_n, q_n))
p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = (
p_n_minus_1,
q_n_minus_1,
p_n,
q_n,
)
return convergents
# Enumerate through all the convergents of T[q] / 2^137 with denominators < 2^64
found_solution = False
for j, tq in enumerate(all_tqs):
for _, w in convergents(continued_fraction(tq, 2 ** 137)):
if w >= 2 ** 64:
break
if (tq * w) % 2 ** 137 > 2 ** 137 - 2 ** 64:
print(f"SOLUTION: q={j-342} T[q]={tq} w={w}")
found_solution = True
if not found_solution:
print("No solutions!")

183
script/release.py Executable file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env python3
########################################################################
# Generates a new release.
########################################################################
import sys
import re
import subprocess
import io
import os
import fileinput
if sys.version_info < (3, 0):
sys.stdout.write("Sorry, requires Python 3.x or better\n")
sys.exit(1)
def colored(r, g, b, text):
return f"\033[38;2;{r};{g};{b}m{text} \033[38;2;255;255;255m"
def extractnumbers(s):
return tuple(map(int, re.findall(r"(\d+)\.(\d+)\.(\d+)", str(s))[0]))
def toversionstring(major, minor, rev):
return f"{major}.{minor}.{rev}"
print("Calling git rev-parse --abbrev-ref HEAD")
pipe = subprocess.Popen(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
branchresult = pipe.communicate()[0].decode().strip()
if branchresult != "main":
print(
colored(
255,
0,
0,
f"We recommend that you release on main, you are on '{branchresult}'",
)
)
ret = subprocess.call(["git", "remote", "update"])
if ret != 0:
sys.exit(ret)
print("Calling git log HEAD.. --oneline")
pipe = subprocess.Popen(
["git", "log", "HEAD..", "--oneline"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
uptodateresult = pipe.communicate()[0].decode().strip()
if len(uptodateresult) != 0:
print(uptodateresult)
sys.exit(-1)
pipe = subprocess.Popen(
["git", "rev-parse", "--show-toplevel"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
maindir = pipe.communicate()[0].decode().strip()
scriptlocation = os.path.dirname(os.path.abspath(__file__))
print(f"repository: {maindir}")
pipe = subprocess.Popen(
["git", "describe", "--abbrev=0", "--tags"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
versionresult = pipe.communicate()[0].decode().strip()
print(f"last version: {versionresult}")
try:
currentv = extractnumbers(versionresult)
except:
currentv = [0, 0, 0]
if len(sys.argv) != 2:
nextv = (currentv[0], currentv[1], currentv[2] + 1)
print(f"please specify version number, e.g. {toversionstring(*nextv)}")
sys.exit(-1)
try:
newversion = extractnumbers(sys.argv[1])
print(newversion)
except:
print(f"can't parse version number {sys.argv[1]}")
sys.exit(-1)
print("checking that new version is valid")
if newversion[0] != currentv[0]:
assert newversion[0] == currentv[0] + 1
assert newversion[1] == 0
assert newversion[2] == 0
elif newversion[1] != currentv[1]:
assert newversion[1] == currentv[1] + 1
assert newversion[2] == 0
else:
assert newversion[2] == currentv[2] + 1
atleastminor = (currentv[0] != newversion[0]) or (currentv[1] != newversion[1])
newmajorversionstring = str(newversion[0])
newminorversionstring = str(newversion[1])
newpatchversionstring = str(newversion[2])
newversionstring = f"{newversion[0]}.{newversion[1]}.{newversion[2]}"
cmakefile = f"{maindir}{os.sep}CMakeLists.txt"
for line in fileinput.input(cmakefile, inplace=1, backup=".bak"):
line = re.sub(
r"project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)",
f"project(fast_float VERSION {newversionstring} LANGUAGES CXX)",
line.rstrip(),
)
print(line)
print(f"modified {cmakefile}, a backup was made")
versionfilerel = f"{os.sep}include{os.sep}fast_float{os.sep}float_common.h"
versionfile = f"{maindir}{versionfilerel}"
for line in fileinput.input(versionfile, inplace=1, backup=".bak"):
line = re.sub(
r"#define FASTFLOAT_VERSION_MAJOR \d+",
f"#define FASTFLOAT_VERSION_MAJOR {newmajorversionstring}",
line.rstrip(),
)
line = re.sub(
r"#define FASTFLOAT_VERSION_MINOR \d+",
f"#define FASTFLOAT_VERSION_MINOR {newminorversionstring}",
line.rstrip(),
)
line = re.sub(
r"#define FASTFLOAT_VERSION_PATCH \d+",
f"#define FASTFLOAT_VERSION_PATCH {newpatchversionstring}",
line.rstrip(),
)
print(line)
print(f"{versionfile} modified")
readmefile = f"{maindir}{os.sep}README.md"
for line in fileinput.input(readmefile, inplace=1, backup=".bak"):
line = re.sub(
r"https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h",
f"https://github.com/fastfloat/fast_float/releases/download/v{newversionstring}/fast_float.h",
line.rstrip(),
)
line = re.sub(
r"GIT_TAG tags/v(\d+\.\d+\.\d+)",
f"GIT_TAG tags/v{newversionstring}",
line.rstrip(),
)
line = re.sub(
r"GIT_TAG v(\d+\.\d+\.\d+)\)", f"GIT_TAG v{newversionstring})", line.rstrip()
)
print(line)
print(f"modified {readmefile}, a backup was made")
print("running amalgamate.py")
with open(f"{maindir}{os.sep}fast_float.h", "w") as outfile:
cp = subprocess.run(
[f"python3", f"{maindir}{os.sep}script{os.sep}amalgamate.py"], stdout=outfile
)
if cp.returncode != 0:
print("Failed to run amalgamate")
else:
print("amalgamate.py ran successfully")
print(f"You should upload {maindir}{os.sep}fast_float.h")
print("Please run the tests before issuing a release.\n")
print(
f'to issue release, enter\n git commit -a && git push && git tag -a v{toversionstring(*newversion)} -m "version {toversionstring(*newversion)}" && git push --tags\n'
)

22
script/run-clangcldocker.sh Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -e
COMMAND=$*
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
MAINSOURCE=$SCRIPTPATH/..
ALL_FILES=$(cd $MAINSOURCE && git ls-tree --full-tree --name-only -r HEAD | grep -e ".*\.\(c\|h\|cc\|cpp\|hh\)\$" | grep -vFf clang-format-ignore.txt)
if clang-format-17 --version 2>/dev/null | grep -qF 'version 17.'; then
cd $MAINSOURCE; clang-format-17 --style=file --verbose -i "$@" $ALL_FILES
exit 0
elif clang-format --version 2>/dev/null | grep -qF 'version 17.'; then
cd $MAINSOURCE; clang-format --style=file --verbose -i "$@" $ALL_FILES
exit 0
fi
echo "Trying to use docker"
command -v docker >/dev/null 2>&1 || { echo >&2 "Please install docker. E.g., go to https://www.docker.com/products/docker-desktop Type 'docker' to diagnose the problem."; exit 1; }
docker info >/dev/null 2>&1 || { echo >&2 "Docker server is not running? type 'docker info'."; exit 1; }
if [ -t 0 ]; then DOCKER_ARGS=-it; fi
docker pull kszonek/clang-format-17
docker run --rm $DOCKER_ARGS -v "$MAINSOURCE":"$MAINSOURCE":Z -w "$MAINSOURCE" -u "$(id -u $USER):$(id -g $USER)" kszonek/clang-format-17 --style=file --verbose -i "$@" $ALL_FILES

View File

@ -1,14 +1,15 @@
def format(number):
upper = number // (1<<64)
lower = number % (1<<64)
print(""+hex(upper)+","+hex(lower)+",")
upper = number // (1 << 64)
lower = number % (1 << 64)
print("" + hex(upper) + "," + hex(lower) + ",")
for q in range(-342,0):
for q in range(-342, 0):
power5 = 5 ** -q
z = 0
while( (1<<z) < power5) :
while (1 << z) < power5:
z += 1
if(q >= -27):
if q >= -27:
b = z + 127
c = 2 ** b // power5 + 1
format(c)
@ -16,16 +17,16 @@ for q in range(-342,0):
b = 2 * z + 2 * 64
c = 2 ** b // power5 + 1
# truncate
while(c >= (1<<128)):
while c >= (1 << 128):
c //= 2
format(c)
for q in range(0,308+1):
for q in range(0, 308 + 1):
power5 = 5 ** q
# move the most significant bit in position
while(power5 < (1<<127)):
while power5 < (1 << 127):
power5 *= 2
# *truncate*
while(power5 >= (1<<128)):
while power5 >= (1 << 128):
power5 //= 2
format(power5)

116
tests/BUILD.bazel Normal file
View File

@ -0,0 +1,116 @@
cc_test(
name = "basictest",
srcs = ["basictest.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "example_test",
srcs = ["example_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "example_comma_test",
srcs = ["example_comma_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "fast_int",
srcs = ["fast_int.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "fixedwidthtest",
srcs = ["fixedwidthtest.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "fortran",
srcs = ["fortran.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "json_fmt",
srcs = ["json_fmt.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "long_test",
srcs = ["long_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "powersoffive_hardround",
srcs = ["powersoffive_hardround.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "rcppfastfloat_test",
srcs = ["rcppfastfloat_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "wide_char_test",
srcs = ["wide_char_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "supported_chars_test",
srcs = ["supported_chars_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "string_test",
srcs = ["string_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)

View File

@ -5,40 +5,42 @@ cmake_minimum_required(VERSION 3.11 FATAL_ERROR)
include(FetchContent)
option(SYSTEM_DOCTEST "Use system copy of doctest" OFF)
option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON)
if (NOT SYSTEM_DOCTEST)
FetchContent_Declare(doctest
GIT_REPOSITORY https://github.com/onqtam/doctest.git
GIT_TAG 2.4.6)
GIT_REPOSITORY https://github.com/lemire/doctest.git)
else ()
find_package(doctest REQUIRED)
endif()
FetchContent_Declare(supplemental_test_files
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
FetchContent_Declare(supplemental_test_files
GIT_REPOSITORY https://github.com/fastfloat/supplemental_test_files.git
GIT_TAG origin/main)
endif()
# FetchContent_MakeAvailable() was only introduced in 3.14
# https://cmake.org/cmake/help/v3.14/release/3.14.html#modules
# FetchContent_MakeAvailable(doctest)
if (NOT SYSTEM_DOCTEST)
FetchContent_GetProperties(doctest)
if(NOT doctest_POPULATED)
FetchContent_Populate(doctest)
add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR})
endif()
endif()
FetchContent_GetProperties(supplemental_test_files)
if(NOT supplemental_test_files_POPULATED)
message(STATUS "Tests enabled. Retrieving test files.")
FetchContent_Populate(supplemental_test_files)
message(STATUS "Test files retrieved.")
add_subdirectory(${supplemental_test_files_SOURCE_DIR} ${supplemental_test_files_BINARY_DIR})
FetchContent_MakeAvailable(doctest)
endif()
add_library(supplemental-data INTERFACE)
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
message(STATUS "Supplemental tests enabled. Retrieving test files.")
FetchContent_MakeAvailable(supplemental_test_files)
message(STATUS "Supplemental test files retrieved.")
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
endif()
function(fast_float_add_cpp_test TEST_NAME)
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
add_test(${TEST_NAME} ${TEST_NAME})
if(CMAKE_CROSSCOMPILING)
set(ccemulator ${CMAKE_CROSSCOMPILING_EMULATOR})
endif()
add_test(NAME ${TEST_NAME}
COMMAND ${ccemulator} $<TARGET_FILE:${TEST_NAME}>)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(${TEST_NAME} PUBLIC /EHsc)
endif()
@ -49,24 +51,52 @@ function(fast_float_add_cpp_test TEST_NAME)
target_link_libraries(${TEST_NAME} PUBLIC fast_float supplemental-data)
if (NOT SYSTEM_DOCTEST)
target_link_libraries(${TEST_NAME} PUBLIC doctest)
else ()
target_link_libraries(${TEST_NAME} PUBLIC doctest::doctest)
endif()
endfunction(fast_float_add_cpp_test)
fast_float_add_cpp_test(rcppfastfloat_test)
fast_float_add_cpp_test(wide_char_test)
fast_float_add_cpp_test(supported_chars_test)
fast_float_add_cpp_test(example_test)
fast_float_add_cpp_test(example_comma_test)
fast_float_add_cpp_test(example_integer_times_pow10)
fast_float_add_cpp_test(basictest)
option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF)
if (FASTFLOAT_CONSTEXPR_TESTS)
target_compile_features(basictest PRIVATE cxx_std_20)
target_compile_definitions(basictest PRIVATE FASTFLOAT_CONSTEXPR_TESTS)
else()
target_compile_features(basictest PRIVATE cxx_std_17)
endif()
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
target_compile_definitions(basictest PRIVATE FASTFLOAT_SUPPLEMENTAL_TESTS)
endif()
fast_float_add_cpp_test(p2497)
fast_float_add_cpp_test(long_test)
fast_float_add_cpp_test(powersoffive_hardround)
fast_float_add_cpp_test(string_test)
fast_float_add_cpp_test(fast_int)
target_compile_features(fast_int PRIVATE cxx_std_17)
fast_float_add_cpp_test(json_fmt)
fast_float_add_cpp_test(fortran)
if(CMAKE_CXX_STANDARD GREATER_EQUAL 23)
option(FASTFLOAT_FIXEDWIDTH_TESTS "Require fixed width test for C++23 (build will fail if the compiler won't support it)" ON)
else()
option(FASTFLOAT_FIXEDWIDTH_TESTS "Require fixed width test for C++23 (build will fail if the compiler won't support it)" OFF)
endif()
if (FASTFLOAT_FIXEDWIDTH_TESTS)
fast_float_add_cpp_test(fixedwidthtest)
target_compile_features(fixedwidthtest PUBLIC cxx_std_23)
endif()
option(FASTFLOAT_EXHAUSTIVE "Exhaustive tests" OFF)
if (FASTFLOAT_EXHAUSTIVE)
fast_float_add_cpp_test(powersoffive_hardround)
fast_float_add_cpp_test(short_random_string)
fast_float_add_cpp_test(exhaustive32_midpoint)
fast_float_add_cpp_test(random_string)
fast_float_add_cpp_test(string_test)
fast_float_add_cpp_test(exhaustive32)
fast_float_add_cpp_test(exhaustive32_64)
fast_float_add_cpp_test(long_exhaustive32)
@ -76,3 +106,4 @@ if (FASTFLOAT_EXHAUSTIVE)
endif(FASTFLOAT_EXHAUSTIVE)
add_subdirectory(build_tests)
add_subdirectory(bloat_analysis)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
add_executable(bloaty main.cpp a1.cpp a2.cpp a3.cpp a4.cpp a4.cpp a5.cpp a6.cpp a7.cpp a8.cpp a9.cpp a10.cpp)
target_link_libraries(bloaty PUBLIC fast_float)
add_executable(bloatyref main_ref.cpp)
target_link_libraries(bloatyref PUBLIC fast_float)

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get1(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 1;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get10(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 10;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get2(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 2;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get3(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 3;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get4(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 4;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get5(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 5;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get6(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 6;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get7(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 7;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get8(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 8;
}
return result_value;
}

View File

@ -0,0 +1,11 @@
#include "fast_float/fast_float.h"
double get9(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 9;
}
return result_value;
}

View File

@ -0,0 +1,20 @@
double get1(char const *input);
double get2(char const *input);
double get3(char const *input);
double get4(char const *input);
double get5(char const *input);
double get6(char const *input);
double get7(char const *input);
double get8(char const *input);
double get9(char const *input);
double get10(char const *input);
int main(int arg, char **argv) {
double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) +
get5(argv[0]) + get6(argv[0]) + get7(argv[0]) + get8(argv[0]) +
get9(argv[0]) + get10(argv[0]);
return int(x);
}

View File

@ -0,0 +1,16 @@
#include "fast_float/fast_float.h"
double get(char const *input) {
double result_value;
auto result =
fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 10;
}
return result_value;
}
int main(int arg, char **argv) {
double x = get(argv[0]);
return int(x);
}

View File

@ -1,2 +1,3 @@
#include "test.h"
void foo() { }
void foo() {}

View File

@ -1,2 +1,3 @@
#include "test.h"
int main() { return 0; }

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,19 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
#include <system_error>
int main() {
const std::string input = "3,1416 xyz ";
std::string const input = "3,1416 xyz ";
double result;
fast_float::parse_options options{fast_float::chars_format::general, ','};
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options);
if ((answer.ec != std::errc()) || ((result != 3.1416))) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,36 @@
#include "fast_float/fast_float.h"
#include <iostream>
void default_overload() {
const uint64_t W = 12345678901234567;
const int Q = 23;
const double result = fast_float::integer_times_pow10(W, Q);
std::cout.precision(17);
std::cout << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
}
void double_specialization() {
const uint64_t W = 12345678901234567;
const int Q = 23;
const double result = fast_float::integer_times_pow10<double>(W, Q);
std::cout.precision(17);
std::cout << "double: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
}
void float_specialization() {
const uint64_t W = 12345678;
const int Q = 23;
const float result = fast_float::integer_times_pow10<float>(W, Q);
std::cout.precision(9);
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
}
int main() {
default_overload();
double_specialization();
float_specialization();
}

View File

@ -1,12 +1,158 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
#include <system_error>
bool many() {
std::string const input = "234532.3426362,7869234.9823,324562.645";
double result;
auto answer =
fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) {
return false;
}
if (result != 234532.3426362) {
return false;
}
if (answer.ptr[0] != ',') {
return false;
}
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(),
result);
if (answer.ec != std::errc()) {
return false;
}
if (result != 7869234.9823) {
return false;
}
if (answer.ptr[0] != ',') {
return false;
}
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(),
result);
if (answer.ec != std::errc()) {
return false;
}
if (result != 324562.645) {
return false;
}
return true;
}
void many_loop() {
std::string const input = "234532.3426362,7869234.9823,324562.645";
double result;
char const *pointer = input.data();
char const *end_pointer = input.data() + input.size();
while (pointer < end_pointer) {
auto answer = fast_float::from_chars(pointer, end_pointer, result);
if (answer.ec != std::errc()) {
std::cerr << "error while parsing" << std::endl;
break;
}
std::cout << "parsed: " << result << std::endl;
pointer = answer.ptr;
if ((answer.ptr < end_pointer) && (*pointer == ',')) {
pointer++;
}
}
}
#if FASTFLOAT_IS_CONSTEXPR
// consteval forces compile-time evaluation of the function in C++20.
consteval double parse(std::string_view input) {
double result;
auto answer =
fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) {
return -1.0;
}
return result;
}
// This function should compile to a function which
// merely returns 3.1415.
constexpr double constexptest() { return parse("3.1415 input"); }
#endif
bool small() {
double result = -1;
std::string str = "3e-1000";
auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
if (r.ec != std::errc::result_out_of_range) {
return false;
}
if (r.ptr != str.data() + 7) {
return false;
}
if (result != 0) {
return false;
}
printf("small values go to zero\n");
return true;
}
bool large() {
double result = -1;
std::string str = "3e1000";
auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
if (r.ec != std::errc::result_out_of_range) {
return false;
}
if (r.ptr != str.data() + 6) {
return false;
}
if (result != std::numeric_limits<double>::infinity()) {
return false;
}
printf("large values go to infinity\n");
return true;
}
int main() {
const std::string input = "3.1416 xyz ";
std::string input = "3.1416 xyz ";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
auto answer =
fast_float::from_chars(input.data(), input.data() + input.size(), result);
if ((answer.ec != std::errc()) || ((result != 3.1416))) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number " << result << std::endl;
#ifdef __STDCPP_FLOAT16_T__
printf("16-bit float\n");
// Parse as 16-bit float
std::float16_t parsed_16{};
input = "10000e-1452";
auto fast_float_r16 = fast_float::from_chars(
input.data(), input.data() + input.size(), parsed_16);
if (fast_float_r16.ec != std::errc() &&
fast_float_r16.ec != std::errc::result_out_of_range) {
std::cerr << "16-bit fast_float parsing failure for: " + input + "\n";
return false;
}
std::cout << "parsed the 16-bit value " << float(parsed_16) << std::endl;
#endif
if (!small()) {
printf("Bug\n");
return EXIT_FAILURE;
}
if (!large()) {
printf("Bug\n");
return EXIT_FAILURE;
}
if (!many()) {
printf("Bug\n");
return EXIT_FAILURE;
}
many_loop();
#if FASTFLOAT_IS_CONSTEXPR
if constexpr (constexptest() != 3.1415) {
return EXIT_FAILURE;
}
#endif
return EXIT_SUCCESS;
}

View File

@ -1,9 +1,13 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <ios>
#include <iostream>
#include <limits>
#include <system_error>
template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 64, "%.*e",
@ -23,10 +27,15 @@ void allvalues() {
memcpy(&v, &word, sizeof(v));
{
const char *string_end = to_string(v, buffer);
char const *string_end = to_string(v, buffer);
float result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value);
if (result.ec != std::errc()) {
// Starting with version 4.0 for fast_float, we return result_out_of_range
// if the value is either too small (too close to zero) or too large
// (effectively infinity). So std::errc::result_out_of_range is normal for
// well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl;
abort();
}
@ -35,9 +44,9 @@ void allvalues() {
std::cerr << "not nan" << buffer << std::endl;
abort();
}
} else if(copysign(1,result_value) != copysign(1,v)) {
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
<< std::endl;
} else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << v << std::endl;
abort();
} else if (result_value != v) {
std::cerr << "no match ? " << buffer << std::endl;

View File

@ -1,9 +1,14 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <ios>
#include <iostream>
#include <limits>
#include <string>
#include <system_error>
template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 64, "%.*e",
@ -11,12 +16,11 @@ template <typename T> char *to_string(T d, char *buffer) {
return buffer + written;
}
bool basic_test_64bit(std::string vals, double val) {
double result_value;
auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(),
result_value);
if (result.ec != std::errc()) {
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
std::cerr << " I could not parse " << vals << std::endl;
return false;
}
@ -26,14 +30,14 @@ bool basic_test_64bit(std::string vals, double val) {
std::cerr << "not nan" << result_value << std::endl;
return false;
}
} else if(copysign(1,result_value) != copysign(1,val)) {
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val
<< std::endl;
} else if (copysign(1, result_value) != copysign(1, val)) {
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << val << std::endl;
return false;
} else if (result_value != val) {
std::cerr << vals << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val
<< std::endl;
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << val << std::endl;
std::cerr << std::dec;
std::cerr << "string: " << vals << std::endl;
return false;
@ -41,7 +45,6 @@ bool basic_test_64bit(std::string vals, double val) {
return true;
}
void all_32bit_values() {
char buffer[64];
for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) {
@ -55,9 +58,9 @@ void all_32bit_values() {
double v = v32;
{
const char *string_end = to_string(v, buffer);
std::string s(buffer, size_t(string_end-buffer));
if(!basic_test_64bit(s,v)) {
char const *string_end = to_string(v, buffer);
std::string s(buffer, size_t(string_end - buffer));
if (!basic_test_64bit(s, v)) {
return;
}
}

View File

@ -1,8 +1,12 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <ios>
#include <iostream>
#include <limits>
#include <stdexcept>
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__)
// Anything at all that is related to cygwin, msys and so forth will
@ -10,29 +14,39 @@
// gcc.
#include <locale>
#include <sstream>
// workaround for CYGWIN
double cygwin_strtod_l(const char* start, char** end) {
double cygwin_strtod_l(char const *start, char **end) {
double d;
std::stringstream ss;
ss.imbue(std::locale::classic());
ss << start;
ss >> d;
if(ss.fail()) { *end = nullptr; }
if(ss.eof()) { ss.clear(); }
if (ss.fail()) {
*end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread;
*end = const_cast<char *>(start) + nread;
return d;
}
float cygwin_strtof_l(const char* start, char** end) {
float cygwin_strtof_l(char const *start, char **end) {
float d;
std::stringstream ss;
ss.imbue(std::locale::classic());
ss << start;
ss >> d;
if(ss.fail()) { *end = nullptr; }
if(ss.eof()) { ss.clear(); }
if (ss.fail()) {
*end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread;
*end = const_cast<char *>(start) + nread;
return d;
}
#endif
@ -43,9 +57,10 @@ template <typename T> char *to_string(T d, char *buffer) {
return buffer + written;
}
void strtof_from_string(const char * st, float& d) {
void strtof_from_string(char const *st, float &d) {
char *pr = (char *)st;
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun)
d = cygwin_strtof_l(st, &pr);
#elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -69,10 +84,14 @@ bool allvalues() {
}
uint32_t word = uint32_t(w);
memcpy(&v, &word, sizeof(v));
if(std::isfinite(v)) {
if (std::isfinite(v)) {
float nextf = std::nextafterf(v, INFINITY);
if(copysign(1,v) != copysign(1,nextf)) { continue; }
if(!std::isfinite(nextf)) { continue; }
if (copysign(1, v) != copysign(1, nextf)) {
continue;
}
if (!std::isfinite(nextf)) {
continue;
}
double v1{v};
assert(float(v1) == v);
double v2{nextf};
@ -80,13 +99,18 @@ bool allvalues() {
double midv{v1 + (v2 - v1) / 2};
float expected_midv = float(midv);
const char *string_end = to_string(midv, buffer);
char const *string_end = to_string(midv, buffer);
float str_answer;
strtof_from_string(buffer, str_answer);
float result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value);
if (result.ec != std::errc()) {
// Starting with version 4.0 for fast_float, we return result_out_of_range
// if the value is either too small (too close to zero) or too large
// (effectively infinity). So std::errc::result_out_of_range is normal for
// well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl;
return false;
}
@ -96,26 +120,30 @@ bool allvalues() {
std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv
<< std::endl;
return false;
}
} else if(copysign(1,result_value) != copysign(1,v)) {
} else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << buffer << std::endl;
std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
std::cerr << "expected_midv " << std::hexfloat << expected_midv
<< std::endl;
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << v << std::endl;
return false;
} else if (result_value != str_answer) {
std::cerr << "no match ? " << buffer << std::endl;
std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv
<< std::endl;
std::cout << "started with " << std::hexfloat << midv << std::endl;
std::cout << "round down to " << std::hexfloat << str_answer << std::endl;
std::cout << "round down to " << std::hexfloat << str_answer
<< std::endl;
std::cout << "got back " << std::hexfloat << result_value << std::endl;
std::cout << std::dec;
return false;
@ -127,15 +155,25 @@ bool allvalues() {
}
inline void Assert(bool Assertion) {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; }
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun)
if (!Assertion) {
std::cerr << "Omitting hard failure on msys/cygwin/sun systems.";
}
#else
if (!Assertion) { throw std::runtime_error("bug"); }
if (!Assertion) {
throw std::runtime_error("bug");
}
#endif
}
int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
std::cout << "Warning: msys/cygwin or solaris detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library as a gold standard." << std::endl;
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun)
std::cout << "Warning: msys/cygwin or solaris detected. This particular test "
"is likely to generate false failures due to our reliance on "
"the underlying runtime library as a gold standard."
<< std::endl;
#endif
Assert(allvalues());
std::cout << std::endl;

1114
tests/fast_int.cpp Normal file

File diff suppressed because it is too large Load Diff

70
tests/fixedwidthtest.cpp Normal file
View File

@ -0,0 +1,70 @@
#include <cstdlib>
#include <iostream>
#include <vector>
#include <cstring>
#include "fast_float/fast_float.h"
#include <cstdint>
#if __cplusplus >= 202300L
#include <stdfloat>
#endif
int main() {
// Write some testcases for the parsing of floating point numbers in the
// float32_t type. We use the from_chars function defined in this library.
#ifdef __STDCPP_FLOAT32_T__
std::vector<std::float32_t> const float32_test_expected{
123.456f, -78.9f, 0.0001f, 3.40282e+038f};
std::vector<std::string_view> const float32_test{"123.456", "-78.9", "0.0001",
"3.40282e+038"};
std::cout << "runing float32 test" << std::endl;
for (std::size_t i = 0; i < float32_test.size(); ++i) {
auto const &f = float32_test[i];
std::float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) {
std::cerr << "Failed to parse: \"" << f << "\"" << std::endl;
return EXIT_FAILURE;
}
if (result != float32_test_expected[i]) {
std::cerr << "Test failed for input: \"" << f << "\" expected "
<< float32_test_expected[i] << " got " << result << std::endl;
return EXIT_FAILURE;
}
}
#else
std::cout << "No std::float32_t type available." << std::endl;
#endif
#ifdef __STDCPP_FLOAT64_T__
// Test cases for std::float64_t
std::vector<std::float64_t> const float64_test_expected{
1.23e4, -5.67e-8, 1.7976931348623157e+308, -1.7976931348623157e+308};
std::vector<std::string_view> const float64_test{"1.23e4", "-5.67e-8",
"1.7976931348623157e+308",
"-1.7976931348623157e+308"};
std::cout << "runing float64 test" << std::endl;
for (std::size_t i = 0; i < float64_test.size(); ++i) {
auto const &f = float64_test[i];
std::float64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) {
std::cerr << "Failed to parse: \"" << f << "\"" << std::endl;
return EXIT_FAILURE;
}
if (result != float64_test_expected[i]) {
std::cerr << "Test failed for input: \"" << f << "\" expected "
<< float64_test_expected[i] << " got " << result << std::endl;
return EXIT_FAILURE;
}
}
#else
std::cout << "No std::float64_t type available." << std::endl;
#endif
std::cout << "All tests passed successfully." << std::endl;
return EXIT_SUCCESS;
return 0;
}

75
tests/fortran.cpp Normal file
View File

@ -0,0 +1,75 @@
/*
* Exercise the Fortran conversion option.
*/
#include <cstdlib>
#include <iostream>
#include <vector>
#include "fast_float/fast_float.h"
int main_readme() {
std::string const input = "1d+4";
double result;
fast_float::parse_options options{
fast_float::chars_format::fortran |
fast_float::chars_format::allow_leading_plus};
auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options);
if ((answer.ec != std::errc()) || ((result != 10000))) {
std::cerr << "parsing failure\n" << result << "\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
int main() {
std::vector<double> const expected{10000, 1000, 100, 10, 1,
.1, .01, .001, .0001};
std::vector<std::string> const fmt1{"1+4", "1+3", "1+2", "1+1", "1+0",
"1-1", "1-2", "1-3", "1-4"};
std::vector<std::string> const fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0",
"1d-1", "1d-2", "1d-3", "1d-4"};
std::vector<std::string> const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
"+1-1", "+1-2", "+1-3", "+1-4"};
fast_float::parse_options const options{
fast_float::chars_format::fortran |
fast_float::chars_format::allow_leading_plus};
for (auto const &f : fmt1) {
auto d{std::distance(&fmt1[0], &f)};
double result;
auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
result, options)};
if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
std::cerr << "parsing failure on " << f << std::endl;
return EXIT_FAILURE;
}
}
for (auto const &f : fmt2) {
auto d{std::distance(&fmt2[0], &f)};
double result;
auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
result, options)};
if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
std::cerr << "parsing failure on " << f << std::endl;
return EXIT_FAILURE;
}
}
for (auto const &f : fmt3) {
auto d{std::distance(&fmt3[0], &f)};
double result;
auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
result, options)};
if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
std::cerr << "parsing failure on " << f << std::endl;
return EXIT_FAILURE;
}
}
if (main_readme() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 3.15)
project(test_simdjson_install VERSION 0.1.0 LANGUAGES CXX)
project(test_install VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(FASTFLOAT_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for fastfloat")
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC_VERSION GREATER 1910)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -permissive-")
@ -16,10 +17,10 @@ file(WRITE main.cpp "
#include <iostream>
int main() {
const std::string input = \"3.1416 xyz \";
std::string input = \"3.1416 xyz \";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { std::cerr << \"parsing failure\\n\"; return EXIT_FAILURE; }
if (answer.ec != std::errc()) { std::cerr << \"parsing failure\\n\"; return EXIT_FAILURE; }
std::cout << \"parsed the number \" << result << std::endl;
return EXIT_SUCCESS;
}")

170
tests/json_fmt.cpp Normal file
View File

@ -0,0 +1,170 @@
#include <cstdlib>
#include <iostream>
#include <vector>
#include "fast_float/fast_float.h"
int main_readme() {
std::string const input = "+.1"; // not valid
double result;
fast_float::parse_options options{
fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) {
std::cerr << "should have failed\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int main_readme2() {
std::string const input = "inf"; // not valid in JSON
double result;
fast_float::parse_options options{
fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) {
std::cerr << "should have failed\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int main_readme3() {
std::string const input =
"inf"; // not valid in JSON but we allow it with json_or_infnan
double result;
fast_float::parse_options options{
fast_float::chars_format::json_or_infnan |
fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options);
if (answer.ec != std::errc() || (!std::isinf(result))) {
std::cerr << "should have parsed infinity\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
struct ExpectedResult {
double value;
std::string junk_chars;
};
struct AcceptedValue {
std::string input;
ExpectedResult expected;
};
struct RejectReason {
fast_float::parse_error error;
intptr_t location_offset;
};
struct RejectedValue {
std::string input;
RejectReason reason;
};
int main() {
std::vector<AcceptedValue> const accept{
{"-0.2", {-0.2, ""}},
{"0.02", {0.02, ""}},
{"0.002", {0.002, ""}},
{"1e+0000", {1., ""}},
{"0e-2", {0., ""}},
{"1e", {1., "e"}},
{"1e+", {1., "e+"}},
{"inf", {std::numeric_limits<double>::infinity(), ""}}};
std::vector<RejectedValue> const reject{
{"-.2", {fast_float::parse_error::missing_integer_after_sign, 1}},
{"00.02", {fast_float::parse_error::leading_zeros_in_integer_part, 0}},
{"0.e+1", {fast_float::parse_error::no_digits_in_fractional_part, 2}},
{"00.e+1", {fast_float::parse_error::leading_zeros_in_integer_part, 0}},
{".25", {fast_float::parse_error::no_digits_in_integer_part, 0}},
// The following cases already start as invalid JSON, so they are
// handled as trailing junk and the error is for not having digits in the
// empty string before the invalid token.
{"+0.25", {fast_float::parse_error::no_digits_in_integer_part, 0}},
{"inf", {fast_float::parse_error::no_digits_in_integer_part, 0}},
{"nan(snan)", {fast_float::parse_error::no_digits_in_integer_part, 0}}};
for (std::size_t i = 0; i < accept.size(); ++i) {
auto const &s = accept[i].input;
auto const &expected = accept[i].expected;
double result;
auto answer =
fast_float::from_chars(s.data(), s.data() + s.size(), result,
fast_float::chars_format::json_or_infnan);
if (answer.ec != std::errc()) {
std::cerr << "json fmt rejected valid json " << s << std::endl;
return EXIT_FAILURE;
}
if (result != expected.value) {
std::cerr << "json fmt gave wrong result " << s << " (expected "
<< expected.value << " got " << result << ")" << std::endl;
return EXIT_FAILURE;
}
if (std::string(answer.ptr) != expected.junk_chars) {
std::cerr << "json fmt has wrong trailing characters " << s
<< " (expected " << expected.junk_chars << " got " << answer.ptr
<< ")" << std::endl;
return EXIT_FAILURE;
}
}
for (std::size_t i = 0; i < reject.size(); ++i) {
auto const &s = reject[i].input;
double result;
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result,
fast_float::chars_format::json);
if (answer.ec == std::errc()) {
std::cerr << "json fmt accepted invalid json " << s << std::endl;
return EXIT_FAILURE;
}
}
for (std::size_t i = 0; i < reject.size(); ++i) {
auto const &f = reject[i].input;
auto const &expected_reason = reject[i].reason;
auto answer = fast_float::parse_number_string<true>(
f.data(), f.data() + f.size(),
fast_float::parse_options(
fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus)); // should be ignored
if (answer.valid) {
std::cerr << "json parse accepted invalid json " << f << std::endl;
return EXIT_FAILURE;
}
if (answer.error != expected_reason.error) {
std::cerr << "json parse failure had invalid error reason " << f
<< std::endl;
return EXIT_FAILURE;
}
intptr_t error_location = answer.lastmatch - f.data();
if (error_location != expected_reason.location_offset) {
std::cerr << "json parse failure had invalid error location " << f
<< " (expected " << expected_reason.location_offset << " got "
<< error_location << ")" << std::endl;
return EXIT_FAILURE;
}
}
if (main_readme() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
if (main_readme2() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
#ifndef __FAST_MATH__
if (main_readme3() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
#endif
return EXIT_SUCCESS;
}

View File

@ -1,13 +1,15 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <ios>
#include <iostream>
#include <system_error>
template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 128, "%.*e",
64, d);
auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
return buffer + written;
}
@ -23,10 +25,15 @@ void allvalues() {
memcpy(&v, &word, sizeof(v));
{
const char *string_end = to_string(v, buffer);
char const *string_end = to_string(v, buffer);
float result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value);
if (result.ec != std::errc()) {
// Starting with version 4.0 for fast_float, we return result_out_of_range
// if the value is either too small (too close to zero) or too large
// (effectively infinity). So std::errc::result_out_of_range is normal for
// well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl;
abort();
}
@ -35,13 +42,14 @@ void allvalues() {
std::cerr << "not nan" << buffer << std::endl;
abort();
}
} else if(copysign(1,result_value) != copysign(1,v)) {
} else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << buffer << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
<< std::endl;
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << v << std::endl;
abort();
} else if (result_value != v) {
std::cerr << "no match ? " << buffer << " got " << result_value << " expected " << v << std::endl;
std::cerr << "no match ? " << buffer << " got " << result_value
<< " expected " << v << std::endl;
std::cout << "started with " << std::hexfloat << v << std::endl;
std::cout << "got back " << std::hexfloat << result_value << std::endl;
std::cout << std::dec;

View File

@ -1,12 +1,13 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <ios>
#include <iostream>
template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 128, "%.*e",
64, d);
auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
return buffer + written;
}
@ -23,16 +24,26 @@ void all_32bit_values() {
double v = v32;
{
const char *string_end = to_string(v, buffer);
char const *string_end = to_string(v, buffer);
double result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value);
if (result.ec != std::errc()) {
// Starting with version 4.0 for fast_float, we return result_out_of_range
// if the value is either too small (too close to zero) or too large
// (effectively infinity). So std::errc::result_out_of_range is normal for
// well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl;
abort();
}
if(copysign(1,result_value) != copysign(1,v)) {
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
<< std::endl;
if (std::isnan(v)) {
if (!std::isnan(result_value)) {
std::cerr << "not nan" << buffer << std::endl;
abort();
}
} else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << v << std::endl;
abort();
} else if (std::isnan(v)) {
if (!std::isnan(result_value)) {

View File

@ -1,12 +1,14 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <ios>
#include <iostream>
#include <system_error>
template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 128, "%.*e",
64, d);
auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
return buffer + written;
}
@ -30,7 +32,8 @@ static inline void lehmer64_seed(uint64_t seed) {
}
static inline uint64_t lehmer64() {
fast_float::value128 v = fast_float::full_multiplication(g_lehmer64_state.low,UINT64_C(0xda942042e4dd58b5));
fast_float::value128 v = fast_float::full_multiplication(
g_lehmer64_state.low, UINT64_C(0xda942042e4dd58b5));
v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5);
g_lehmer64_state = v;
return v.high;
@ -50,15 +53,21 @@ void random_values(size_t N) {
double v;
memcpy(&v, &word, sizeof(v));
{
const char *string_end = to_string(v, buffer);
char const *string_end = to_string(v, buffer);
double result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value);
if (result.ec != std::errc()) {
// Starting with version 4.0 for fast_float, we return result_out_of_range
// if the value is either too small (too close to zero) or too large
// (effectively infinity). So std::errc::result_out_of_range is normal for
// well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl;
errors++;
if (errors > 10) {
abort();
}
continue;
}
if (std::isnan(v)) {
if (!std::isnan(result_value)) {
@ -68,10 +77,10 @@ void random_values(size_t N) {
abort();
}
}
} else if(copysign(1,result_value) != copysign(1,v)) {
} else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << buffer << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
<< std::endl;
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << v << std::endl;
abort();
} else if (result_value != v) {
std::cerr << "no match ? '" << buffer << "'" << std::endl;
@ -90,7 +99,8 @@ void random_values(size_t N) {
int main() {
errors = 0;
size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit
size_t N =
size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit
random_values(N);
if (errors == 0) {
std::cout << std::endl;

View File

@ -1,38 +1,49 @@
#include "fast_float/fast_float.h"
#include <cctype>
#include <iostream>
#include <stdexcept>
#include <string>
#include <system_error>
#include <vector>
inline void Assert(bool Assertion) {
if (!Assertion) { throw std::runtime_error("bug"); }
if (!Assertion) {
throw std::runtime_error("bug");
}
}
template <typename T>
bool test() {
std::string input = "0.156250000000000000000000000000000000000000 3.14159265358979323846264338327950288419716939937510 2.71828182845904523536028747135266249775724709369995";
std::vector<T> answers = {T(0.15625), T(3.141592653589793), T(2.718281828459045)};
const char * begin = input.data();
const char * end = input.data() + input.size();
for(size_t i = 0; i < answers.size(); i++) {
template <typename T> bool test() {
std::string input = "0.156250000000000000000000000000000000000000 "
"3.14159265358979323846264338327950288419716939937510 "
"2.71828182845904523536028747135266249775724709369995";
std::vector<T> answers = {T(0.15625), T(3.141592653589793),
T(2.718281828459045)};
char const *begin = input.data();
char const *end = input.data() + input.size();
for (size_t i = 0; i < answers.size(); i++) {
T result_value;
auto result = fast_float::from_chars(begin, end,
result_value);
if (result.ec != std::errc()) {
while ((begin < end) && (std::isspace(*begin))) {
begin++;
}
auto result = fast_float::from_chars(begin, end, result_value);
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
printf("parsing %.*s\n", int(end - begin), begin);
std::cerr << " I could not parse " << std::endl;
return false;
}
if(result_value != answers[i]) {
if (result_value != answers[i]) {
printf("parsing %.*s\n", int(end - begin), begin);
std::cerr << " Mismatch " << std::endl;
std::cerr << " Expected " << answers[i] << std::endl;
std::cerr << " Got " << result_value << std::endl;
return false;
}
begin = result.ptr;
}
if(begin != end) {
if (begin != end) {
std::cerr << " bad ending " << std::endl;
return false;
}

Some files were not shown because too many files have changed in this diff Show More