├── .github └── workflows │ ├── ci.yml │ └── maven-publish.yml ├── .gitignore ├── .idea ├── FastDoubleParser.iml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── copyright │ ├── MIT_License.xml │ └── profiles_settings.xml ├── encodings.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations │ ├── Main_Dev__0_1_.xml │ ├── Main_Dev_ar.xml │ ├── Main_Dev_canada.xml │ ├── Main_Dev_canada_hex.xml │ ├── Main_Dev_mesh.xml │ ├── Main_Java11__0_1_.xml │ ├── Main_Java17__0_1_.xml │ ├── Main_Java17_canada_hex.xml │ ├── Main_Java23__0_1_.xml │ └── Main_Java8__0_1_.xml ├── uiDesigner.xml └── vcs.xml ├── KEYS ├── LICENSE ├── NOTICE ├── README.md ├── deployment ├── deployment.md └── pom.xml ├── fastdoubleparser-dev ├── pom.xml └── src │ ├── main │ ├── java │ │ └── ch.randelshofer.fastdoubleparser │ │ │ ├── ch │ │ │ └── randelshofer │ │ │ │ └── fastdoubleparser │ │ │ │ ├── AbstractBigDecimalParser.java │ │ │ │ ├── AbstractBigIntegerParser.java │ │ │ │ ├── AbstractConfigurableFloatingPointBitsFromByteArrayAscii.java │ │ │ │ ├── AbstractConfigurableFloatingPointBitsFromByteArrayUtf8.java │ │ │ │ ├── AbstractConfigurableFloatingPointBitsFromCharArray.java │ │ │ │ ├── AbstractConfigurableFloatingPointBitsFromCharSequence.java │ │ │ │ ├── AbstractFloatValueParser.java │ │ │ │ ├── AbstractJavaFloatingPointBitsFromByteArray.java │ │ │ │ ├── AbstractJavaFloatingPointBitsFromCharArray.java │ │ │ │ ├── AbstractJavaFloatingPointBitsFromCharSequence.java │ │ │ │ ├── AbstractJsonFloatingPointBitsFromByteArray.java │ │ │ │ ├── AbstractJsonFloatingPointBitsFromCharArray.java │ │ │ │ ├── AbstractJsonFloatingPointBitsFromCharSequence.java │ │ │ │ ├── AbstractNumberParser.java │ │ │ │ ├── BigSignificand.java │ │ │ │ ├── ConfigurableDoubleBitsFromByteArrayAscii.java │ │ │ │ ├── ConfigurableDoubleBitsFromByteArrayUtf8.java │ │ │ │ ├── ConfigurableDoubleBitsFromCharArray.java │ │ │ │ ├── ConfigurableDoubleBitsFromCharSequence.java │ │ │ │ ├── ConfigurableDoubleParser.java │ │ │ │ ├── FastDoubleMath.java │ │ │ │ ├── FastDoubleSwar.java │ │ │ │ ├── FastDoubleVector.java │ │ │ │ ├── FastFloatMath.java │ │ │ │ ├── FastIntegerMath.java │ │ │ │ ├── FftMultiplier.java │ │ │ │ ├── JavaBigDecimalFromByteArray.java │ │ │ │ ├── JavaBigDecimalFromCharArray.java │ │ │ │ ├── JavaBigDecimalFromCharSequence.java │ │ │ │ ├── JavaBigDecimalParser.java │ │ │ │ ├── JavaBigIntegerFromByteArray.java │ │ │ │ ├── JavaBigIntegerFromCharArray.java │ │ │ │ ├── JavaBigIntegerFromCharSequence.java │ │ │ │ ├── JavaBigIntegerParser.java │ │ │ │ ├── JavaDoubleBitsFromByteArray.java │ │ │ │ ├── JavaDoubleBitsFromCharArray.java │ │ │ │ ├── JavaDoubleBitsFromCharSequence.java │ │ │ │ ├── JavaDoubleParser.java │ │ │ │ ├── JavaFloatBitsFromByteArray.java │ │ │ │ ├── JavaFloatBitsFromCharArray.java │ │ │ │ ├── JavaFloatBitsFromCharSequence.java │ │ │ │ ├── JavaFloatParser.java │ │ │ │ ├── JsonDoubleBitsFromByteArray.java │ │ │ │ ├── JsonDoubleBitsFromCharArray.java │ │ │ │ ├── JsonDoubleBitsFromCharSequence.java │ │ │ │ ├── JsonDoubleParser.java │ │ │ │ ├── NumberFormatSymbols.java │ │ │ │ ├── NumberFormatSymbolsInfo.java │ │ │ │ ├── ParseDigitsTaskByteArray.java │ │ │ │ ├── ParseDigitsTaskCharArray.java │ │ │ │ ├── ParseDigitsTaskCharSequence.java │ │ │ │ ├── SlowDoubleConversionPath.java │ │ │ │ ├── Utf8Decoder.java │ │ │ │ ├── bte │ │ │ │ ├── ByteDigitSet.java │ │ │ │ ├── ByteSet.java │ │ │ │ ├── ByteSetOfFew.java │ │ │ │ ├── ByteSetOfNone.java │ │ │ │ ├── ByteSetOfOne.java │ │ │ │ ├── ByteToIntMap.java │ │ │ │ ├── ByteTrie.java │ │ │ │ ├── ByteTrieNode.java │ │ │ │ ├── ByteTrieOfFew.java │ │ │ │ ├── ByteTrieOfFewIgnoreCase.java │ │ │ │ ├── ByteTrieOfNone.java │ │ │ │ ├── ByteTrieOfOne.java │ │ │ │ ├── ByteTrieOfOneSingleByte.java │ │ │ │ ├── ConsecutiveByteDigitSet.java │ │ │ │ └── package-info.java │ │ │ │ ├── chr │ │ │ │ ├── CharDigitSet.java │ │ │ │ ├── CharSet.java │ │ │ │ ├── CharSetOfFew.java │ │ │ │ ├── CharSetOfNone.java │ │ │ │ ├── CharSetOfOne.java │ │ │ │ ├── CharToIntMap.java │ │ │ │ ├── CharTrie.java │ │ │ │ ├── CharTrieNode.java │ │ │ │ ├── CharTrieOfFew.java │ │ │ │ ├── CharTrieOfFewIgnoreCase.java │ │ │ │ ├── CharTrieOfNone.java │ │ │ │ ├── CharTrieOfOne.java │ │ │ │ ├── CharTrieOfOneSingleChar.java │ │ │ │ ├── ConsecutiveCharDigitSet.java │ │ │ │ ├── FormatCharSet.java │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ └── resources │ │ └── ch.randelshofer.fastdoubleparser │ │ └── META-INF │ │ └── thirdparty-LICENSE │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparser │ └── ch │ └── randelshofer │ └── fastdoubleparser │ ├── BigDecimalTestDataFactory.java │ ├── BigIntegerTestDataFactory.java │ ├── BigSignificandTest.java │ ├── ConfigurableDoubleParserTest.java │ ├── ConfigurableDoubleParserTestDataFactory.java │ ├── EarlyAccessEightDigitsVectorTest.java │ ├── EightDigitsSwarTest.java │ ├── EightDigitsTestDataFactory.java │ ├── FastDoubleMathTest.java │ ├── FastFloatMathTest.java │ ├── FastIntegerMathTest.java │ ├── FftMultiplierTest.java │ ├── FloatValueTestDataFactory.java │ ├── JavaBigDecimalFromByteArrayTest.java │ ├── JavaBigDecimalFromCharArrayTest.java │ ├── JavaBigDecimalFromCharSequenceTest.java │ ├── JavaBigIntegerFromByteArrayTest.java │ ├── JavaBigIntegerFromCharArrayTest.java │ ├── JavaBigIntegerFromCharSequenceTest.java │ ├── JavaDoubleParserTest.java │ ├── JavaDoubleTestDataFactory.java │ ├── JavaFloatParserTest.java │ ├── JavaFloatTestDataFactory.java │ ├── JmhBigDecimalEmpiricial.java │ ├── JmhBigDecimalScalability.java │ ├── JmhBigIntegerScalability.java │ ├── JmhComplex.java │ ├── JmhConfigurableDoubleFromCharSequenceEmpirical.java │ ├── JmhDoubleEmpirical.java │ ├── JmhDoubleScalability.java │ ├── JmhEightDigits.java │ ├── JmhFastDoubleMath.java │ ├── JmhFastFloatMath.java │ ├── JmhFftMultiplier.java │ ├── JmhFloat.java │ ├── JmhJavaBigDecimalFromByteArrayEmpirical.java │ ├── JmhJavaBigDecimalFromByteArrayScalability.java │ ├── JmhJavaBigDecimalFromCharArrayScalability.java │ ├── JmhJavaBigDecimalFromCharSequenceEmpirical.java │ ├── JmhJavaBigDecimalFromCharSequenceScalability.java │ ├── JmhJavaBigIntegerFromByteArrayScalability.java │ ├── JmhJavaBigIntegerFromCharArrayScalability.java │ ├── JmhJavaBigIntegerFromCharSequenceScalability.java │ ├── JmhJavaDoubleFromByteArrayEmpirical.java │ ├── JmhJavaDoubleFromByteArrayScalability.java │ ├── JmhJavaDoubleFromCharArrayEmpirical.java │ ├── JmhJavaDoubleFromCharSequenceEmpirical.java │ ├── JmhJavaDoubleFromCharSequenceScalability.java │ ├── JmhJavaFloatFromByteArray.java │ ├── JmhJavaFloatFromCharSequence.java │ ├── JmhJsonDoubleFromByteArray.java │ ├── JmhJsonDoubleFromCharArray.java │ ├── JmhScalb.java │ ├── JmhSplitFloor16.java │ ├── JmhUseBigDecimalForSlowPath.java │ ├── JsonDoubleParserTest.java │ ├── JsonDoubleTestDataFactory.java │ ├── MiniTest.java │ ├── NumberTestData.java │ ├── NumberTestDataSupplier.java │ ├── ParseSignificandWithSwarTest.java │ ├── SlowDoubleConversionPathTest.java │ ├── Strings.java │ ├── Utf8DecoderTest.java │ ├── VirtualCharSequence.java │ ├── bte │ ├── ByteToIntMapTest.java │ ├── ByteTrieOfFewIgnoreCaseTest.java │ └── ByteTrieTest.java │ └── chr │ ├── CharToIntMapTest.java │ ├── CharTrieOfFewIgnoreCaseTest.java │ └── CharTrieTest.java ├── fastdoubleparser-java11 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparser │ │ ├── ch │ │ └── randelshofer │ │ │ └── fastdoubleparser │ │ │ ├── FastDoubleSwar.java │ │ │ ├── FastIntegerMath.java │ │ │ └── NumberFormatSymbols.java │ │ └── module-info.java │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparser │ └── ch │ └── randelshofer │ └── fastdoubleparser │ ├── NumberTestData.java │ └── NumberTestDataSupplier.java ├── fastdoubleparser-java17 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparser │ │ ├── ch │ │ └── randelshofer │ │ │ └── fastdoubleparser │ │ │ ├── FastDoubleSwar.java │ │ │ └── FastIntegerMath.java │ │ └── module-info.java │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparser │ └── ch │ └── randelshofer │ └── fastdoubleparser │ └── empty.txt ├── fastdoubleparser-java21 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparser │ │ ├── ch │ │ └── randelshofer │ │ │ └── fastdoubleparser │ │ │ └── FastDoubleSwar.java │ │ └── module-info.java │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparser │ └── ch │ └── randelshofer │ └── fastdoubleparser │ └── empty.txt ├── fastdoubleparser-java23 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparser │ │ └── module-info.java │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparser │ └── ch │ └── randelshofer │ └── fastdoubleparser │ └── empty.txt ├── fastdoubleparser-java8 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparser │ │ └── ch │ │ └── randelshofer │ │ └── fastdoubleparser │ │ ├── BigSignificand.java │ │ ├── FastDoubleSwar.java │ │ ├── FastIntegerMath.java │ │ └── NumberFormatSymbols.java │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparser │ └── ch │ └── randelshofer │ └── fastdoubleparser │ ├── NumberTestData.java │ └── NumberTestDataSupplier.java ├── fastdoubleparser ├── pom.xml └── src │ └── assembly │ └── mrjar.xml ├── fastdoubleparserdemo-dev ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparserdemo │ │ ├── ch │ │ └── randelshofer │ │ │ └── fastdoubleparserdemo │ │ │ ├── DoubleSum.java │ │ │ ├── GenerateNumberFormatNumbers.java │ │ │ ├── Main.java │ │ │ ├── Stats.java │ │ │ ├── SystemInfo.java │ │ │ └── VarianceStatistics.java │ │ └── module-info.java │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparserdemo │ └── ch │ └── randelshofer │ └── fastdoubleparser │ └── empty.txt ├── fastdoubleparserdemo-java11 ├── pom.xml └── src │ └── main │ └── java │ └── ch.randelshofer.fastdoubleparserdemo │ └── ch │ └── randelshofer │ └── fastdoubleparserdemo │ ├── DecimalFormatMain.java │ └── empty.txt ├── fastdoubleparserdemo-java17 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparserdemo │ │ └── ch │ │ └── randelshofer │ │ └── fastdoubleparser │ │ └── empty.txt │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparserdemo │ └── ch │ └── randelshofer │ └── fastdoubleparser │ └── empty.txt ├── fastdoubleparserdemo-java21 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparserdemo │ │ └── ch │ │ └── randelshofer │ │ └── fastdoubleparser │ │ └── empty.txt │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparserdemo │ └── ch │ └── randelshofer │ └── fastdoubleparser │ └── empty.txt ├── fastdoubleparserdemo-java23 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparserdemo │ │ └── ch │ │ └── randelshofer │ │ └── fastdoubleparser │ │ └── empty.txt │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparserdemo │ └── ch │ └── randelshofer │ └── fastdoubleparser │ └── empty.txt ├── fastdoubleparserdemo-java8 ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch.randelshofer.fastdoubleparserdemo │ │ └── empty.txt │ └── test │ └── java │ └── ch.randelshofer.fastdoubleparserdemo │ └── ch │ └── randelshofer │ └── fastdoubleparserdemo │ └── empty.txt ├── fastdoubleparserdemo ├── data │ ├── canada.txt │ ├── canada_hex.txt │ ├── formatted_ar-Java8.txt │ ├── formatted_ar-java11.txt │ ├── formatted_ar.txt │ ├── formatted_en-GB.txt │ ├── formatted_fr-FR.txt │ ├── formatted_zh-CN.txt │ └── mesh.txt ├── pom.xml └── src │ └── assembly │ └── mrjar.xml └── pom.xml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Java CI build and test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ ubuntu-latest ] 11 | java: [ 23 ] 12 | arch: [ x64 ] 13 | dist: [ zulu ] 14 | fail-fast: false 15 | max-parallel: 4 16 | name: Test JDK ${{ matrix.java }}, ${{ matrix.os }}, ${{ matrix.arch }} 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up JDK ${{ matrix.java }} 20 | uses: actions/setup-java@v4 21 | with: 22 | java-version: ${{ matrix.java }} 23 | architecture: ${{ matrix.arch }} 24 | distribution: ${{ matrix.dist }} 25 | 26 | - name: Show where Java Home is 27 | run: echo JAVA_HOME=$JAVA_HOME 28 | - name: Build with Maven 29 | run: mvn --batch-mode --update-snapshots --errors package 30 | - name: Performance Test canada.txt 31 | run: java -XX:CompileCommand=inline,java/lang/String.charAt -p ~/.m2/repository/com/ibm/icu/icu4j/75.1/icu4j-75.1.jar:fastdoubleparserdemo/target:fastdoubleparser/target -m ch.randelshofer.fastdoubleparserdemo/ch.randelshofer.fastdoubleparserdemo.Main fastdoubleparserdemo/data/canada.txt 32 | ... 33 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: Maven Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up JDK 21 17 | uses: actions/setup-java@v4 18 | with: 19 | distribution: 'zulu' 20 | java-version: '23' 21 | 22 | - name: Build with Maven 23 | run: mvn -B package --file pom.xml 24 | 25 | - name: Publish to GitHub Packages Apache Maven 26 | run: mvn deploy 27 | env: 28 | GITHUB_TOKEN: ${{ github.token }} # GITHUB_TOKEN is the default env for the password 29 | 30 | - name: Set up Apache Maven Central 31 | uses: actions/setup-java@v4 32 | with: # running setup-java again overwrites the settings.xml 33 | distribution: 'zulu' 34 | java-version: '23' 35 | server-id: maven # Value of the distributionManagement/repository/id field of the pom.xml 36 | server-username: MAVEN_USERNAME # env variable for username in deploy 37 | server-password: MAVEN_CENTRAL_TOKEN # env variable for token in deploy 38 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import 39 | gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase 40 | 41 | - name: Publish to Apache Maven Central 42 | run: mvn deploy 43 | env: 44 | MAVEN_USERNAME: maven_username123 45 | MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} 46 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out/ 2 | **/target/ 3 | /lib/ 4 | /.idea/shelf/ 5 | /.idea/workspace.xml 6 | **/.DS_Store 7 | **/.flattened-pom.xml 8 | -------------------------------------------------------------------------------- /.idea/FastDoubleParser.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/copyright/MIT_License.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Dev__0_1_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Dev_ar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Dev_canada.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Dev_canada_hex.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Dev_mesh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Java11__0_1_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Java17__0_1_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Java17_canada_hex.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Java23__0_1_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Main_Java8__0_1_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Werner Randelshofer, Switzerland. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | # FastDoubleParser 2 | 3 | This is a Java port of Daniel Lemire's fast_float project. 4 | This project provides parsers for double, float, BigDecimal and BigInteger values. 5 | 6 | ## Copyright 7 | 8 | Copyright © 2024 Werner Randelshofer, Switzerland. 9 | 10 | ## Licensing 11 | 12 | This code is licensed under MIT License. 13 | https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE 14 | (The file 'LICENSE' is included in the sources and classes Jar files that are released by this project 15 | - as is required by that license.) 16 | 17 | Some portions of the code have been derived from other projects. 18 | All these projects require that we include a copyright notice, and some require that we also include some text of their 19 | license file. 20 | 21 | fast_double_parser, Copyright (c) 2022 Daniel Lemire. BSL License. 22 | https://github.com/lemire/fast_double_parser 23 | https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL 24 | (The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project 25 | - as is required by that license.) 26 | 27 | fast_float, Copyright (c) 2021 The fast_float authors. MIT License. 28 | https://github.com/fastfloat/fast_float 29 | https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT 30 | (The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project 31 | - as is required by that license.) 32 | 33 | bigint, Copyright 2020 Tim Buktu. 2-clause BSD License. 34 | https://github.com/tbuktu/bigint/tree/floatfft 35 | https://github.com/tbuktu/bigint/blob/617c8cd8a7c5e4fb4d919c6a4d11e2586107f029/LICENSE 36 | https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE 37 | (We only use those portions of the bigint project that can be licensed under 2-clause BSD License.) 38 | (The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project 39 | - as is required by that license.) 40 | -------------------------------------------------------------------------------- /deployment/deployment.md: -------------------------------------------------------------------------------- 1 | # How to deploy to the nexus repository manager 2 | 3 | The nexus repository manager only accepts lower case character in the module name and in the version 4 | number. 5 | 6 | We deploy the following files: 7 | 8 | - fastdoubleparser/target/fastdoubleparser-x.y.z.jar 9 | - fastdoubleparser/target/fastdoubleparser-x.y.z-sources.jar 10 | - fastdoubleparser-java9/target/fastdoubleparser-java21-x.y.z-sources.jar 11 | 12 | 13 | All files must be signed with GPG. We create a bundle.jar file, which we then 14 | can upload to the nexus repository manager. 15 | 16 | ```shell 17 | cp ../fastdoubleparser/target/*.jar . 18 | cp ../fastdoubleparser-java23/target/*javadoc.jar fastdoubleparser-2.0.1-javadoc.jar 19 | rm -rf META-INF 20 | mkdir META-INF 21 | cp ../LICENSE META-INF 22 | cp ../fastdoubleparser-dev/src/main/resources/ch.randelshofer.fastdoubleparser/META-INF/thirdparty-LICENSE META-INF 23 | cp ../NOTICE META-INF 24 | jar -uf fastdoubleparser-2.0.1-javadoc.jar META-INF/* 25 | jar -uf fastdoubleparser-2.0.1-sources.jar META-INF/* 26 | rm *.asc 27 | for f in *.jar; do gpg -ab "$f"; done 28 | for f in *.xml; do gpg -ab "$f"; done 29 | rm *bundle.jar 30 | jar -cf fastdoubleparser-2.0.1-bundle.jar $(ls -1 pom*|xargs) $(ls -1 fastdoubleparser*|xargs) 31 | ``` 32 | -------------------------------------------------------------------------------- /deployment/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | ch.randelshofer 5 | fastdoubleparser 6 | 2.0.1 7 | jar 8 | 9 | ch.randelshofer:fastdoubleparser 10 | A Java port of Daniel Lemire's fast_float project. 11 | https://github.com/wrandelshofer/FastDoubleParser 12 | 13 | 14 | 15 | MIT License 16 | https://github.com/wrandelshofer/FastDoubleParser/blob/9a3ccae38254c9bf84b5e6a218a47675bf80ed9f/LICENSE 17 | repo 18 | 19 | 20 | 21 | 22 | 23 | Werner Randelshofer 24 | werner.randelshofer@bluewin.ch 25 | ch.randelshofer 26 | http://www.randelshofer.ch 27 | 28 | 29 | 30 | 31 | scm:git:git://github.com/wrandelshofer/FastDoubleParser.git 32 | scm:git:ssh://github.com/wrandelshofer/FastDoubleParser.git 33 | https://github.com/wrandelshofer/FastDoubleParser/tree/master 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ch.randelshofer 7 | fastdoubleparser-parentproject 8 | ${revision} 9 | 10 | fastdoubleparser-dev 11 | jar 12 | fastdoubleparser-dev 13 | 14 | 23 15 | ${javaVersion} 16 | ${javaVersion} 17 | true 18 | true 19 | true 20 | 21 | 22 | 23 | ${basedir}/src/main/java/ch.randelshofer.fastdoubleparser 24 | ${basedir}/src/test/java/ch.randelshofer.fastdoubleparser 25 | 26 | 27 | ${basedir}/src/main/resources/ch.randelshofer.fastdoubleparser 28 | 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-compiler-plugin 34 | 35 | 36 | -Xlint:all 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-surefire-plugin 44 | 45 | 46 | -Xmx20g 47 | classesAndMethods 48 | 4 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/AbstractBigDecimalParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)AbstractBigDecimalParser.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | abstract class AbstractBigDecimalParser extends ch.randelshofer.fastdoubleparser.AbstractNumberParser { 8 | 9 | /** 10 | * Threshold on the number of input characters for selecting the 11 | * algorithm optimised for few digits in the significand vs. the algorithm for many 12 | * digits in the significand. 13 | *

14 | * Set this to {@link Integer#MAX_VALUE} if you only want to use 15 | * the algorithm optimised for few digits in the significand. 16 | *

17 | * Set this to {@code 0} if you only want to use the algorithm for 18 | * long inputs. 19 | *

20 | * Rationale for choosing a specific threshold value: 21 | * We speculate that we only need to use the algorithm for large inputs 22 | * if there is zero chance, that we can parse the input with the algorithm 23 | * for small inputs. 24 | *

25 |      * optional significant sign = 1
26 |      * 18 significant digits = 18
27 |      * optional decimal point in significant = 1
28 |      * optional exponent = 1
29 |      * optional exponent sign = 1
30 |      * 10 exponent digits = 10
31 |      * 
32 | */ 33 | public static final int MANY_DIGITS_THRESHOLD = 1 + 18 + 1 + 1 + 1 + 10; 34 | /** 35 | * Threshold on the number of digits for selecting the 36 | * recursive algorithm instead of the iterative algorithm. 37 | *

38 | * Set this to {@link Integer#MAX_VALUE} if you only want to use the 39 | * iterative algorithm. 40 | *

41 | * Set this to {@code 0} if you only want to use the recursive algorithm. 42 | *

43 | * Rationale for choosing a specific threshold value: 44 | * The iterative algorithm has a smaller constant overhead than the 45 | * recursive algorithm. We speculate that we break even somewhere at twice 46 | * the threshold value. 47 | */ 48 | static final int RECURSION_THRESHOLD = 400; 49 | 50 | 51 | protected final static long MAX_EXPONENT_NUMBER = Integer.MAX_VALUE; 52 | /** 53 | * See {@link JavaBigDecimalParser}. 54 | */ 55 | protected final static int MAX_DIGITS_WITHOUT_LEADING_ZEROS = 646_456_993; 56 | 57 | protected static boolean hasManyDigits(int length) { 58 | return length >= MANY_DIGITS_THRESHOLD; 59 | } 60 | 61 | protected static void checkParsedBigDecimalBounds(boolean illegal, int index, int endIndex, int digitCount, long exponent) { 62 | if (illegal || index < endIndex) { 63 | throw new NumberFormatException(SYNTAX_ERROR); 64 | } 65 | if (exponent <= Integer.MIN_VALUE || exponent > Integer.MAX_VALUE || digitCount > MAX_DIGITS_WITHOUT_LEADING_ZEROS) { 66 | throw new NumberFormatException(VALUE_EXCEEDS_LIMITS); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/AbstractBigIntegerParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)AbstractBigIntegerParser.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | abstract class AbstractBigIntegerParser extends AbstractNumberParser { 8 | 9 | /** 10 | * The resulting value must fit into {@code 2^31 - 1} bits. 11 | * The decimal representation of {@code 2^31 - 1} bits has 646,456,993 digits. 12 | */ 13 | private static final int MAX_DECIMAL_DIGITS = 646_456_993; 14 | 15 | /** 16 | * The resulting value must fit into {@code 2^31 - 1} bits. 17 | * The hexadecimal representation of {@code 2^31 - 1} bits has 536,870,912 digits. 18 | */ 19 | private static final int MAX_HEX_DIGITS = 536_870_912; 20 | /** 21 | * Threshold on the number of digits for selecting the 22 | * recursive algorithm instead of the iterative algorithm. 23 | *

24 | * Set this to {@link Integer#MAX_VALUE} if you only want to use the 25 | * iterative algorithm. 26 | *

27 | * Set this to {@code 0} if you only want to use the recursive algorithm. 28 | *

29 | * Rationale for choosing a specific threshold value: 30 | * The iterative algorithm has a smaller constant overhead than the 31 | * recursive algorithm. We speculate that we break even somewhere at twice 32 | * the threshold value. 33 | */ 34 | static final int RECURSION_THRESHOLD = 400; 35 | 36 | protected static boolean hasManyDigits(int length) { 37 | return length > 18; 38 | } 39 | 40 | protected static void checkHexBigIntegerBounds(int numDigits) { 41 | if (numDigits > MAX_HEX_DIGITS) { 42 | throw new NumberFormatException(AbstractNumberParser.VALUE_EXCEEDS_LIMITS); 43 | } 44 | } 45 | 46 | protected static void checkDecBigIntegerBounds(int numDigits) { 47 | if (numDigits > MAX_DECIMAL_DIGITS) { 48 | throw new NumberFormatException(AbstractNumberParser.VALUE_EXCEEDS_LIMITS); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/AbstractFloatValueParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)AbstractFloatValueParser.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Abstract base class for parsers that parse a {@code FloatingPointLiteral} from a 9 | * character sequence ({@code str}). 10 | *

11 | * This is a C++ to Java port of Daniel Lemire's fast_double_parser. 12 | *

13 | * References: 14 | *

15 | *
Daniel Lemire, fast_float number parsing library: 4x faster than strtod. 16 | * MIT License.
17 | *
github.com
18 | * 19 | *
Daniel Lemire, Number Parsing at a Gigabyte per Second, 20 | * Software: Practice and Experience 51 (8), 2021. 21 | * arXiv.2101.11408v3 [cs.DS] 24 Feb 2021
22 | *
arxiv.org
23 | *
24 | */ 25 | abstract class AbstractFloatValueParser extends AbstractNumberParser { 26 | /** 27 | * This is the maximal input length that a Java array can have. 28 | */ 29 | public final static int MAX_INPUT_LENGTH = Integer.MAX_VALUE - 4; 30 | 31 | /** 32 | * This is the smallest non-negative number that has 19 decimal digits. 33 | */ 34 | final static long MINIMAL_NINETEEN_DIGIT_INTEGER = 1000_00000_00000_00000L; 35 | 36 | /** 37 | * The decimal exponent of a double has a range of -324 to +308. 38 | * The hexadecimal exponent of a double has a range of -1022 to +1023. 39 | */ 40 | final static int MAX_EXPONENT_NUMBER = 1024; 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/BigSignificand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)BigSignificand.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.lang.invoke.MethodHandles; 8 | import java.lang.invoke.VarHandle; 9 | import java.math.BigInteger; 10 | import java.nio.ByteOrder; 11 | 12 | /** 13 | * A mutable non-negative significand with a fixed number of bits. 14 | */ 15 | final class BigSignificand { 16 | private static final long LONG_MASK = 0xffffffffL; 17 | private final static VarHandle readIntBE = 18 | MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); 19 | private final int numInts; 20 | private final byte[] x; 21 | private int firstNonZeroInt; 22 | 23 | /** 24 | * Creates a new instance with the specified number in bits. 25 | * 26 | * @param numBits the number of bits in range {@literal [0, Integer.MAX_VALUE)}. 27 | */ 28 | public BigSignificand(long numBits) { 29 | if (numBits <= 0 || numBits >= Integer.MAX_VALUE) { 30 | throw new IllegalArgumentException("numBits=" + numBits); 31 | } 32 | int numLongs = (int) ((numBits + 63) >>> 6) + 1; 33 | numInts = numLongs << 1; 34 | int numBytes = numLongs << 3; 35 | x = new byte[numBytes]; 36 | firstNonZeroInt = numInts; 37 | } 38 | 39 | /** 40 | * Adds the specified value to the significand in place. 41 | * 42 | * @param value the addend, must be a non-negative value 43 | * @throws ArrayIndexOutOfBoundsException on overflow 44 | */ 45 | public void add(int value) { 46 | if (value == 0) { 47 | return; 48 | } 49 | long carry = value & LONG_MASK; 50 | int i = numInts - 1; 51 | for (; carry != 0; i--) { 52 | long sum = (x(i) & LONG_MASK) + carry; 53 | x(i, (int) sum); 54 | carry = sum >>> 32; 55 | } 56 | firstNonZeroInt = Math.min(firstNonZeroInt, i + 1); 57 | } 58 | 59 | /** 60 | * Multiplies the significand with the specified factor in place, 61 | * and then adds the specified addend to it (also in place). 62 | * 63 | * @param factor the multiplication factor, must be a non-negative value 64 | * @param addend the addend, must be a non-negative value 65 | * @throws ArrayIndexOutOfBoundsException on overflow 66 | */ 67 | public void fma(int factor, int addend) { 68 | long factorL = factor & LONG_MASK; 69 | long carry = addend; 70 | int i = numInts - 1; 71 | for (; i >= firstNonZeroInt; i--) { 72 | long product = factorL * (x(i) & LONG_MASK) + carry; 73 | x(i, (int) product); 74 | carry = product >>> 32; 75 | } 76 | if (carry != 0) { 77 | x(i, (int) carry); 78 | firstNonZeroInt = i; 79 | } 80 | } 81 | 82 | /** 83 | * Converts the BigSignificand to a BigInteger. 84 | * @return a new BigInteger instance 85 | */ 86 | public BigInteger toBigInteger() { 87 | return new BigInteger(x); 88 | } 89 | 90 | private void x(int i, int value) { 91 | readIntBE.set(x, i << 2, value); 92 | } 93 | 94 | private int x(int i) { 95 | return (int) readIntBE.get(x, i << 2); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/ConfigurableDoubleBitsFromByteArrayAscii.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ConfigurableDoubleBitsFromCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@code char[]} with configurable {@link NumberFormatSymbols}. 9 | */ 10 | final class ConfigurableDoubleBitsFromByteArrayAscii extends AbstractConfigurableFloatingPointBitsFromByteArrayAscii { 11 | /** 12 | * Creates a new instance. 13 | */ 14 | public ConfigurableDoubleBitsFromByteArrayAscii(NumberFormatSymbols symbols, boolean ignoreCase) { 15 | super(symbols, ignoreCase); 16 | } 17 | 18 | @Override 19 | long nan() { 20 | return Double.doubleToRawLongBits(Double.NaN); 21 | } 22 | 23 | @Override 24 | long negativeInfinity() { 25 | return Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY); 26 | } 27 | 28 | @Override 29 | long positiveInfinity() { 30 | return Double.doubleToRawLongBits(Double.POSITIVE_INFINITY); 31 | } 32 | @Override 33 | long valueOfFloatLiteral(byte[] str, int integerStartIndex, int integerEndIndex, int fractionStartIndex, int fractionEndIndex, boolean isSignificandNegative, 34 | long significand, int exponent, boolean isSignificandTruncated, 35 | int exponentOfTruncatedSignificand, int exponentValue, int startIndex, int endIndex) { 36 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isSignificandNegative, significand, exponent, isSignificandTruncated, 37 | exponentOfTruncatedSignificand); 38 | return Double.doubleToRawLongBits(Double.isNaN(d) ? 39 | slowPathToDouble(str, integerStartIndex, integerEndIndex, fractionStartIndex, fractionEndIndex, isSignificandNegative, exponentValue) : 40 | d); 41 | } 42 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/ConfigurableDoubleBitsFromByteArrayUtf8.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ConfigurableDoubleBitsFromCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@code char[]} with configurable {@link NumberFormatSymbols}. 9 | */ 10 | final class ConfigurableDoubleBitsFromByteArrayUtf8 extends AbstractConfigurableFloatingPointBitsFromByteArrayUtf8 { 11 | /** 12 | * Creates a new instance. 13 | */ 14 | public ConfigurableDoubleBitsFromByteArrayUtf8(NumberFormatSymbols symbols, boolean ignoreCase) { 15 | super(symbols, ignoreCase); 16 | } 17 | 18 | @Override 19 | long nan() { 20 | return Double.doubleToRawLongBits(Double.NaN); 21 | } 22 | 23 | @Override 24 | long negativeInfinity() { 25 | return Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY); 26 | } 27 | 28 | @Override 29 | long positiveInfinity() { 30 | return Double.doubleToRawLongBits(Double.POSITIVE_INFINITY); 31 | } 32 | 33 | @Override 34 | long valueOfFloatLiteral(byte[] str, int integerStartIndex, int integerEndIndex, int fractionStartIndex, int fractionEndIndex, boolean isSignificandNegative, 35 | long significand, int exponent, boolean isSignificandTruncated, 36 | int exponentOfTruncatedSignificand, int exponentValue, int startIndex, int endIndex) { 37 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isSignificandNegative, significand, exponent, isSignificandTruncated, 38 | exponentOfTruncatedSignificand); 39 | return Double.doubleToRawLongBits(Double.isNaN(d) ? 40 | slowPathToDouble(str, integerStartIndex, integerEndIndex, fractionStartIndex, fractionEndIndex, isSignificandNegative, exponentValue) : 41 | d); 42 | } 43 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/ConfigurableDoubleBitsFromCharArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ConfigurableDoubleBitsFromCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@code char[]} with configurable {@link NumberFormatSymbols}. 9 | */ 10 | final class ConfigurableDoubleBitsFromCharArray extends AbstractConfigurableFloatingPointBitsFromCharArray { 11 | /** 12 | * Creates a new instance. 13 | */ 14 | public ConfigurableDoubleBitsFromCharArray(NumberFormatSymbols symbols, boolean ignoreCase) { 15 | super(symbols, ignoreCase); 16 | } 17 | 18 | @Override 19 | long nan() { 20 | return Double.doubleToRawLongBits(Double.NaN); 21 | } 22 | 23 | @Override 24 | long negativeInfinity() { 25 | return Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY); 26 | } 27 | 28 | @Override 29 | long positiveInfinity() { 30 | return Double.doubleToRawLongBits(Double.POSITIVE_INFINITY); 31 | } 32 | @Override 33 | long valueOfFloatLiteral(char[] str, int integerStartIndex, int integerEndIndex, int fractionStartIndex, int fractionEndIndex, boolean isSignificandNegative, 34 | long significand, int exponent, boolean isSignificandTruncated, 35 | int exponentOfTruncatedSignificand, int exponentValue, int startIndex, int endIndex) { 36 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isSignificandNegative, significand, exponent, isSignificandTruncated, 37 | exponentOfTruncatedSignificand); 38 | return Double.doubleToRawLongBits(Double.isNaN(d) ? 39 | slowPathToDouble(str, integerStartIndex, integerEndIndex, fractionStartIndex, fractionEndIndex, isSignificandNegative, exponentValue) : 40 | d); 41 | } 42 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/ConfigurableDoubleBitsFromCharSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ConfigurableDoubleBitsFromCharSequence.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@link CharSequence} with configurable {@link NumberFormatSymbols}. 9 | */ 10 | final class ConfigurableDoubleBitsFromCharSequence extends AbstractConfigurableFloatingPointBitsFromCharSequence { 11 | /** 12 | * Creates a new instance. 13 | */ 14 | public ConfigurableDoubleBitsFromCharSequence(NumberFormatSymbols symbols, boolean ignoreCase) { 15 | super(symbols, ignoreCase); 16 | } 17 | 18 | @Override 19 | long nan() { 20 | return Double.doubleToRawLongBits(Double.NaN); 21 | } 22 | 23 | @Override 24 | long negativeInfinity() { 25 | return Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY); 26 | } 27 | 28 | @Override 29 | long positiveInfinity() { 30 | return Double.doubleToRawLongBits(Double.POSITIVE_INFINITY); 31 | } 32 | 33 | @Override 34 | long valueOfFloatLiteral(CharSequence str, int integerStartIndex, int integerEndIndex, int fractionStartIndex, int fractionEndIndex, boolean isSignificandNegative, 35 | long significand, int exponent, boolean isSignificandTruncated, 36 | int exponentOfTruncatedSignificand, int exponentValue, int startIndex, int endIndex) { 37 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isSignificandNegative, significand, exponent, isSignificandTruncated, 38 | exponentOfTruncatedSignificand); 39 | return Double.doubleToRawLongBits(Double.isNaN(d) ? 40 | slowPathToDouble(str, integerStartIndex, integerEndIndex, fractionStartIndex, fractionEndIndex, isSignificandNegative, exponentValue) : 41 | d); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JavaDoubleBitsFromByteArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JavaDoubleBitsFromByteArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | 9 | /** 10 | * Parses a {@code double} from a {@code byte} array. 11 | */ 12 | final class JavaDoubleBitsFromByteArray extends AbstractJavaFloatingPointBitsFromByteArray { 13 | 14 | /** 15 | * Creates a new instance. 16 | */ 17 | public JavaDoubleBitsFromByteArray() { 18 | 19 | } 20 | 21 | @Override 22 | long nan() { 23 | return Double.doubleToRawLongBits(Double.NaN); 24 | } 25 | 26 | @Override 27 | long negativeInfinity() { 28 | return Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY); 29 | } 30 | 31 | @Override 32 | long positiveInfinity() { 33 | return Double.doubleToRawLongBits(Double.POSITIVE_INFINITY); 34 | } 35 | 36 | @Override 37 | long valueOfFloatLiteral(byte[] str, int startIndex, int endIndex, boolean isNegative, 38 | long significand, int exponent, boolean isSignificandTruncated, 39 | int exponentOfTruncatedSignificand) { 40 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 41 | exponentOfTruncatedSignificand); 42 | return Double.doubleToRawLongBits(Double.isNaN(d) 43 | // via Double.parseDouble 44 | ? Double.parseDouble(new String(str, startIndex, endIndex - startIndex, StandardCharsets.ISO_8859_1)) 45 | 46 | // via BigDecimal 47 | // This only makes sense from JDK 21 onwards. 48 | // See fix for https://bugs.openjdk.org/browse/JDK-8205592 49 | // FIXME Only pass up to 764 significand digits to the BigDecimalParser 50 | // new JavaBigDecimalFromByteArray().valueOfBigDecimalString(str,integerPartIndex,decimalPointIndex,nonZeroFractionalPartIndex,exponentIndicatorIndex,isNegative,exponent).doubleValue() 51 | //? new JavaBigDecimalFromByteArray().parseBigDecimalString(str, startIndex, endIndex - startIndex).doubleValue() 52 | 53 | : d); 54 | } 55 | 56 | @Override 57 | long valueOfHexLiteral( 58 | byte[] str, int startIndex, int endIndex, boolean isNegative, long significand, int exponent, 59 | boolean isSignificandTruncated, int exponentOfTruncatedSignificand) { 60 | double d = FastDoubleMath.tryHexFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 61 | exponentOfTruncatedSignificand); 62 | return Double.doubleToRawLongBits(Double.isNaN(d) ? Double.parseDouble(new String(str, startIndex, endIndex - startIndex, StandardCharsets.ISO_8859_1)) : d); 63 | } 64 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JavaDoubleBitsFromCharArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JavaDoubleBitsFromCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@code char} array. 9 | */ 10 | final class JavaDoubleBitsFromCharArray extends AbstractJavaFloatingPointBitsFromCharArray { 11 | 12 | /** 13 | * Creates a new instance. 14 | */ 15 | public JavaDoubleBitsFromCharArray() { 16 | 17 | } 18 | 19 | @Override 20 | long nan() { 21 | return Double.doubleToRawLongBits(Double.NaN); 22 | } 23 | 24 | @Override 25 | long negativeInfinity() { 26 | return Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY); 27 | } 28 | 29 | @Override 30 | long positiveInfinity() { 31 | return Double.doubleToRawLongBits(Double.POSITIVE_INFINITY); 32 | } 33 | 34 | @Override 35 | long valueOfFloatLiteral(char[] str, int startIndex, int endIndex, boolean isNegative, 36 | long significand, int exponent, boolean isSignificandTruncated, 37 | int exponentOfTruncatedSignificand) { 38 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 39 | exponentOfTruncatedSignificand); 40 | return Double.doubleToRawLongBits(Double.isNaN(d) ? Double.parseDouble(new String(str, startIndex, endIndex - startIndex)) : d); 41 | } 42 | 43 | @Override 44 | long valueOfHexLiteral( 45 | char[] str, int startIndex, int endIndex, boolean isNegative, long significand, int exponent, 46 | boolean isSignificandTruncated, int exponentOfTruncatedSignificand) { 47 | double d = FastDoubleMath.tryHexFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 48 | exponentOfTruncatedSignificand); 49 | return Double.doubleToRawLongBits(Double.isNaN(d) ? Double.parseDouble(new String(str, startIndex, endIndex - startIndex)) : d); 50 | } 51 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JavaDoubleBitsFromCharSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JavaDoubleBitsFromCharSequence.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@link CharSequence}. 9 | */ 10 | final class JavaDoubleBitsFromCharSequence extends AbstractJavaFloatingPointBitsFromCharSequence { 11 | 12 | /** 13 | * Creates a new instance. 14 | */ 15 | public JavaDoubleBitsFromCharSequence() { 16 | 17 | } 18 | 19 | @Override 20 | long nan() { 21 | return Double.doubleToRawLongBits(Double.NaN); 22 | } 23 | 24 | @Override 25 | long negativeInfinity() { 26 | return Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY); 27 | } 28 | 29 | @Override 30 | long positiveInfinity() { 31 | return Double.doubleToRawLongBits(Double.POSITIVE_INFINITY); 32 | } 33 | 34 | @Override 35 | long valueOfFloatLiteral(CharSequence str, int startIndex, int endIndex, boolean isNegative, 36 | long significand, int exponent, boolean isSignificandTruncated, 37 | int exponentOfTruncatedSignificand) { 38 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 39 | exponentOfTruncatedSignificand); 40 | return Double.doubleToRawLongBits(Double.isNaN(d) 41 | ? Double.parseDouble(str.subSequence(startIndex, endIndex).toString()) 42 | : d); 43 | } 44 | 45 | @Override 46 | long valueOfHexLiteral( 47 | CharSequence str, int startIndex, int endIndex, boolean isNegative, long significand, int exponent, 48 | boolean isSignificandTruncated, int exponentOfTruncatedSignificand) { 49 | double d = FastDoubleMath.tryHexFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 50 | exponentOfTruncatedSignificand); 51 | return Double.doubleToRawLongBits(Double.isNaN(d) 52 | ? Double.parseDouble(str.subSequence(startIndex, endIndex).toString()) 53 | : d); 54 | } 55 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JavaFloatBitsFromByteArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JavaFloatBitsFromByteArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | 9 | /** 10 | * Parses a {@code float} from a {@code byte} array. 11 | */ 12 | final class JavaFloatBitsFromByteArray extends AbstractJavaFloatingPointBitsFromByteArray { 13 | 14 | 15 | /** 16 | * Creates a new instance. 17 | */ 18 | public JavaFloatBitsFromByteArray() { 19 | 20 | } 21 | 22 | @Override 23 | long nan() { 24 | return Float.floatToRawIntBits(Float.NaN); 25 | } 26 | 27 | @Override 28 | long negativeInfinity() { 29 | return Float.floatToRawIntBits(Float.NEGATIVE_INFINITY); 30 | } 31 | 32 | @Override 33 | long positiveInfinity() { 34 | return Float.floatToRawIntBits(Float.POSITIVE_INFINITY); 35 | } 36 | 37 | @Override 38 | long valueOfFloatLiteral(byte[] str, int startIndex, int endIndex, boolean isNegative, 39 | long significand, int exponent, boolean isSignificandTruncated, 40 | int exponentOfTruncatedSignificand) { 41 | float result = FastFloatMath.tryDecFloatToFloatTruncated(isNegative, significand, exponent, isSignificandTruncated, exponentOfTruncatedSignificand); 42 | return Float.floatToRawIntBits(Float.isNaN(result) ? Float.parseFloat( 43 | new String(str, startIndex, endIndex - startIndex, StandardCharsets.ISO_8859_1)) : result); 44 | } 45 | 46 | @Override 47 | long valueOfHexLiteral( 48 | byte[] str, int startIndex, int endIndex, boolean isNegative, long significand, int exponent, 49 | boolean isSignificandTruncated, int exponentOfTruncatedSignificand) { 50 | float d = FastFloatMath.tryHexFloatToFloatTruncated(isNegative, significand, exponent, isSignificandTruncated, exponentOfTruncatedSignificand); 51 | return Float.floatToRawIntBits(Float.isNaN(d) ? Float.parseFloat(new String(str, startIndex, endIndex - startIndex, StandardCharsets.ISO_8859_1)) : d); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JavaFloatBitsFromCharArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JavaFloatBitsFromCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code float} from a {@code char} array. 9 | */ 10 | final class JavaFloatBitsFromCharArray extends AbstractJavaFloatingPointBitsFromCharArray { 11 | 12 | 13 | /** 14 | * Creates a new instance. 15 | */ 16 | public JavaFloatBitsFromCharArray() { 17 | 18 | } 19 | 20 | @Override 21 | long nan() { 22 | return Float.floatToRawIntBits(Float.NaN); 23 | } 24 | 25 | @Override 26 | long negativeInfinity() { 27 | return Float.floatToRawIntBits(Float.NEGATIVE_INFINITY); 28 | } 29 | 30 | @Override 31 | long positiveInfinity() { 32 | return Float.floatToRawIntBits(Float.POSITIVE_INFINITY); 33 | } 34 | 35 | @Override 36 | long valueOfFloatLiteral(char[] str, int startIndex, int endIndex, boolean isNegative, 37 | long significand, int exponent, boolean isSignificandTruncated, 38 | int exponentOfTruncatedSignificand) { 39 | float result = FastFloatMath.tryDecFloatToFloatTruncated(isNegative, significand, exponent, isSignificandTruncated, exponentOfTruncatedSignificand); 40 | return Float.isNaN(result) ? (long) Float.floatToRawIntBits(Float.parseFloat(new String(str, startIndex, endIndex - startIndex))) : Float.floatToRawIntBits(result); 41 | } 42 | 43 | @Override 44 | long valueOfHexLiteral( 45 | char[] str, int startIndex, int endIndex, boolean isNegative, long significand, int exponent, 46 | boolean isSignificandTruncated, int exponentOfTruncatedSignificand) { 47 | float d = FastFloatMath.tryHexFloatToFloatTruncated(isNegative, significand, exponent, isSignificandTruncated, exponentOfTruncatedSignificand); 48 | return Float.floatToRawIntBits(Float.isNaN(d) ? Float.parseFloat(new String(str, startIndex, endIndex - startIndex)) : d); 49 | } 50 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JavaFloatBitsFromCharSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JavaFloatBitsFromCharSequence.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code float} from a {@link CharSequence}. 9 | */ 10 | final class JavaFloatBitsFromCharSequence extends AbstractJavaFloatingPointBitsFromCharSequence { 11 | 12 | 13 | /** 14 | * Creates a new instance. 15 | */ 16 | public JavaFloatBitsFromCharSequence() { 17 | 18 | } 19 | 20 | @Override 21 | long nan() { 22 | return Float.floatToRawIntBits(Float.NaN); 23 | } 24 | 25 | @Override 26 | long negativeInfinity() { 27 | return Float.floatToRawIntBits(Float.NEGATIVE_INFINITY); 28 | } 29 | 30 | @Override 31 | long positiveInfinity() { 32 | return Float.floatToRawIntBits(Float.POSITIVE_INFINITY); 33 | } 34 | 35 | @Override 36 | long valueOfFloatLiteral(CharSequence str, int startIndex, int endIndex, boolean isNegative, 37 | long significand, int exponent, boolean isSignificandTruncated, 38 | int exponentOfTruncatedSignificand) { 39 | float d = FastFloatMath.tryDecFloatToFloatTruncated(isNegative, significand, exponent, isSignificandTruncated, exponentOfTruncatedSignificand); 40 | return Float.floatToRawIntBits(Float.isNaN(d) ? Float.parseFloat(str.subSequence(startIndex, endIndex).toString()) : d); 41 | } 42 | 43 | @Override 44 | long valueOfHexLiteral( 45 | CharSequence str, int startIndex, int endIndex, boolean isNegative, long significand, int exponent, 46 | boolean isSignificandTruncated, int exponentOfTruncatedSignificand) { 47 | float d = FastFloatMath.tryHexFloatToFloatTruncated(isNegative, significand, exponent, isSignificandTruncated, exponentOfTruncatedSignificand); 48 | return Float.floatToRawIntBits(Float.isNaN(d) ? Float.parseFloat(str.subSequence(startIndex, endIndex).toString()) : d); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JsonDoubleBitsFromByteArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JsonDoubleBitsFromByteArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | 9 | /** 10 | * Parses a {@code double} from a {@code byte} array. 11 | */ 12 | final class JsonDoubleBitsFromByteArray extends AbstractJsonFloatingPointBitsFromByteArray { 13 | 14 | /** 15 | * Creates a new instance. 16 | */ 17 | public JsonDoubleBitsFromByteArray() { 18 | 19 | } 20 | 21 | @Override 22 | long valueOfFloatLiteral(byte[] str, int startIndex, int endIndex, boolean isNegative, 23 | long significand, int exponent, boolean isSignificandTruncated, 24 | int exponentOfTruncatedSignificand) { 25 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 26 | exponentOfTruncatedSignificand); 27 | return Double.doubleToRawLongBits(Double.isNaN(d) ? Double.parseDouble(new String(str, startIndex, endIndex - startIndex, StandardCharsets.ISO_8859_1)) : d); 28 | } 29 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JsonDoubleBitsFromCharArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JsonDoubleBitsFromCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@code char} array. 9 | */ 10 | final class JsonDoubleBitsFromCharArray extends AbstractJsonFloatingPointBitsFromCharArray { 11 | 12 | /** 13 | * Creates a new instance. 14 | */ 15 | public JsonDoubleBitsFromCharArray() { 16 | 17 | } 18 | 19 | @Override 20 | long valueOfFloatLiteral(char[] str, int startIndex, int endIndex, boolean isNegative, 21 | long significand, int exponent, boolean isSignificandTruncated, 22 | int exponentOfTruncatedSignificand) { 23 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 24 | exponentOfTruncatedSignificand); 25 | return Double.doubleToRawLongBits(Double.isNaN(d) ? Double.parseDouble(new String(str, startIndex, endIndex - startIndex)) : d); 26 | } 27 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JsonDoubleBitsFromCharSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JsonDoubleBitsFromCharSequence.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | /** 8 | * Parses a {@code double} from a {@link CharSequence}. 9 | */ 10 | final class JsonDoubleBitsFromCharSequence extends AbstractJsonFloatingPointBitsFromCharSequence { 11 | 12 | /** 13 | * Creates a new instance. 14 | */ 15 | public JsonDoubleBitsFromCharSequence() { 16 | 17 | } 18 | 19 | @Override 20 | long valueOfFloatLiteral(CharSequence str, int startIndex, int endIndex, boolean isNegative, 21 | long significand, int exponent, boolean isSignificandTruncated, 22 | int exponentOfTruncatedSignificand) { 23 | double d = FastDoubleMath.tryDecFloatToDoubleTruncated(isNegative, significand, exponent, isSignificandTruncated, 24 | exponentOfTruncatedSignificand); 25 | return Double.doubleToRawLongBits(Double.isNaN(d) ? Double.parseDouble(str.subSequence(startIndex, endIndex).toString()) : d); 26 | } 27 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/ParseDigitsTaskByteArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ParseDigitsTaskByteArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.math.BigInteger; 8 | import java.util.Map; 9 | 10 | import static ch.randelshofer.fastdoubleparser.AbstractNumberParser.SYNTAX_ERROR; 11 | import static ch.randelshofer.fastdoubleparser.FastIntegerMath.splitFloor16; 12 | 13 | /** 14 | * Parses digits. 15 | */ 16 | final class ParseDigitsTaskByteArray { 17 | /** 18 | * Don't let anyone instantiate this class. 19 | */ 20 | private ParseDigitsTaskByteArray() { 21 | } 22 | 23 | 24 | /** 25 | * Parses digits in quadratic time O(N2). 26 | */ 27 | static BigInteger parseDigitsIterative(byte[] str, int from, int to) { 28 | assert str != null : "str==null"; 29 | 30 | int numDigits = to - from; 31 | 32 | BigSignificand bigSignificand = new BigSignificand(FastIntegerMath.estimateNumBits(numDigits)); 33 | int preroll = from + (numDigits & 7); 34 | int value = FastDoubleSwar.tryToParseUpTo7Digits(str, from, preroll); 35 | boolean success = value >= 0; 36 | bigSignificand.add(value); 37 | for (from = preroll; from < to; from += 8) { 38 | int addend = FastDoubleSwar.tryToParseEightDigits(str, from); 39 | success &= addend >= 0; 40 | bigSignificand.fma(100_000_000, addend); 41 | } 42 | if (!success) { 43 | throw new NumberFormatException(SYNTAX_ERROR); 44 | } 45 | return bigSignificand.toBigInteger(); 46 | } 47 | 48 | /** 49 | * Parses digits in O(N log N (log log N)) time. 50 | *

51 | * A conventional recursive algorithm would require O(N1.5). 52 | * We achieve better performance by performing multiplications of long bit sequences 53 | * in the frequency domain using {@link FftMultiplier}. 54 | */ 55 | static BigInteger parseDigitsRecursive(byte[] str, int from, int to, Map powersOfTen, int recursionThreshold) { 56 | assert str != null : "str==null"; 57 | assert powersOfTen != null : "powersOfTen==null"; 58 | 59 | int numDigits = to - from; 60 | 61 | // Base case: Short sequences can be parsed iteratively. 62 | if (numDigits <= recursionThreshold) { 63 | return parseDigitsIterative(str, from, to); 64 | } 65 | 66 | // Recursion case: Split large sequences up into two parts. The lower part is a multiple of 16 digits. 67 | int mid = splitFloor16(from, to); 68 | BigInteger high = parseDigitsRecursive(str, from, mid, powersOfTen, recursionThreshold); 69 | BigInteger low = parseDigitsRecursive(str, mid, to, powersOfTen, recursionThreshold); 70 | 71 | //high = high.multiply(powersOfTen.get(to - mid)); 72 | high = FftMultiplier.multiply(high, powersOfTen.get(to - mid)); 73 | return low.add(high); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/ParseDigitsTaskCharArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ParseDigitsTaskCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.math.BigInteger; 8 | import java.util.Map; 9 | 10 | import static ch.randelshofer.fastdoubleparser.AbstractNumberParser.SYNTAX_ERROR; 11 | import static ch.randelshofer.fastdoubleparser.FastIntegerMath.splitFloor16; 12 | 13 | /** 14 | * Parses digits. 15 | */ 16 | final class ParseDigitsTaskCharArray { 17 | /** 18 | * Don't let anyone instantiate this class. 19 | */ 20 | private ParseDigitsTaskCharArray() { 21 | } 22 | 23 | 24 | /** 25 | * Parses digits in quadratic time O(N2). 26 | */ 27 | static BigInteger parseDigitsIterative(char[] str, int from, int to) { 28 | assert str != null : "str==null"; 29 | 30 | int numDigits = to - from; 31 | 32 | BigSignificand bigSignificand = new BigSignificand(FastIntegerMath.estimateNumBits(numDigits)); 33 | int preroll = from + (numDigits & 7); 34 | int value = FastDoubleSwar.tryToParseUpTo7Digits(str, from, preroll); 35 | boolean success = value >= 0; 36 | bigSignificand.add(value); 37 | for (from = preroll; from < to; from += 8) { 38 | int addend = FastDoubleSwar.tryToParseEightDigits(str, from); 39 | success &= addend >= 0; 40 | bigSignificand.fma(100_000_000, addend); 41 | } 42 | if (!success) { 43 | throw new NumberFormatException(SYNTAX_ERROR); 44 | } 45 | return bigSignificand.toBigInteger(); 46 | } 47 | 48 | /** 49 | * Parses digits in O(N log N (log log N)) time. 50 | *

51 | * A conventional recursive algorithm would require O(N1.5). 52 | * We achieve better performance by performing multiplications of long bit sequences 53 | * in the frequency domain. 54 | */ 55 | static BigInteger parseDigitsRecursive(char[] str, int from, int to, Map powersOfTen, int recursionThreshold) { 56 | assert str != null : "str==null"; 57 | assert powersOfTen != null : "powersOfTen==null"; 58 | 59 | int numDigits = to - from; 60 | 61 | // Base case: Short sequences can be parsed iteratively. 62 | if (numDigits <= recursionThreshold) { 63 | return parseDigitsIterative(str, from, to); 64 | } 65 | 66 | // Recursion case: Split large sequences up into two parts. The lower part is a multiple of 16 digits. 67 | int mid = splitFloor16(from, to); 68 | BigInteger high = parseDigitsRecursive(str, from, mid, powersOfTen, recursionThreshold); 69 | BigInteger low = parseDigitsRecursive(str, mid, to, powersOfTen, recursionThreshold); 70 | 71 | high = FftMultiplier.multiply(high, powersOfTen.get(to - mid)); 72 | return low.add(high); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/ParseDigitsTaskCharSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ParseDigitsTaskCharSequence.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.math.BigInteger; 8 | import java.util.Map; 9 | 10 | import static ch.randelshofer.fastdoubleparser.AbstractNumberParser.SYNTAX_ERROR; 11 | import static ch.randelshofer.fastdoubleparser.FastIntegerMath.splitFloor16; 12 | 13 | /** 14 | * Parses digits. 15 | */ 16 | final class ParseDigitsTaskCharSequence { 17 | /** 18 | * Don't let anyone instantiate this class. 19 | */ 20 | private ParseDigitsTaskCharSequence() { 21 | } 22 | 23 | 24 | 25 | /** 26 | * Parses digits in quadratic time O(N2). 27 | */ 28 | static BigInteger parseDigitsIterative(CharSequence str, int from, int to) { 29 | assert str != null : "str==null"; 30 | 31 | int numDigits = to - from; 32 | 33 | BigSignificand bigSignificand = new BigSignificand(FastIntegerMath.estimateNumBits(numDigits)); 34 | int preroll = from + (numDigits & 7); 35 | int value = FastDoubleSwar.tryToParseUpTo7Digits(str, from, preroll); 36 | boolean success = value >= 0; 37 | bigSignificand.add(value); 38 | for (from = preroll; from < to; from += 8) { 39 | int addend = FastDoubleSwar.tryToParseEightDigits(str, from); 40 | success &= addend >= 0; 41 | bigSignificand.fma(100_000_000, addend); 42 | } 43 | if (!success) { 44 | throw new NumberFormatException(SYNTAX_ERROR); 45 | } 46 | return bigSignificand.toBigInteger(); 47 | } 48 | 49 | /** 50 | * Parses digits in O(N log N (log log N)) time. 51 | *

52 | * A conventional recursive algorithm would require O(N1.5). 53 | * We achieve better performance by performing multiplications of long bit sequences 54 | * in the frequency domain. 55 | */ 56 | static BigInteger parseDigitsRecursive(CharSequence str, int from, int to, Map powersOfTen, int recursionThreshold) { 57 | assert str != null : "str==null"; 58 | assert powersOfTen != null : "powersOfTen==null"; 59 | 60 | // Base case: All sequences of 18 or fewer digits fit into a long. 61 | int numDigits = to - from; 62 | 63 | // Base case: Short sequences can be parsed iteratively. 64 | if (numDigits <= recursionThreshold) { 65 | return parseDigitsIterative(str, from, to); 66 | } 67 | 68 | // Recursion case: Split large sequences up into two parts. The lower part is a multiple of 16 digits. 69 | int mid = splitFloor16(from, to); 70 | BigInteger high = parseDigitsRecursive(str, from, mid, powersOfTen, recursionThreshold); 71 | BigInteger low = parseDigitsRecursive(str, mid, to, powersOfTen, recursionThreshold); 72 | 73 | //high = high.multiply(powersOfTen.get(to - mid)); 74 | high = FftMultiplier.multiply(high, powersOfTen.get(to - mid)); 75 | return low.add(high); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteDigitSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteDigitSet.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Interface for sets of digit bytes. 11 | */ 12 | public interface ByteDigitSet { 13 | /** 14 | * Returns a value in the range 0 to 9 if the specified character is a digit. 15 | * Otherwise, Returns a value greater than 9. 16 | * 17 | * @param ch a character 18 | * @return a value in the range 0 to Integer.MAX_VALUE. 19 | */ 20 | int toDigit(byte ch); 21 | 22 | /** 23 | * Creates a new {@link ByteDigitSet} instead from the 24 | * specified list. 25 | *

26 | * The list must contain characters for the digits 0 to 9. 27 | * 28 | * @param digits a list of digit characters 29 | * @return a new {@link ByteDigitSet} instance 30 | */ 31 | @SuppressWarnings("SequencedCollectionMethodCanBeUsed") 32 | static ByteDigitSet copyOf(List digits) { 33 | boolean consecutive = true; 34 | char zeroDigit = digits.get(0); 35 | for (int i = 1; i < 10; i++) { 36 | char current = digits.get(i); 37 | consecutive &= current == zeroDigit + i; 38 | } 39 | return consecutive ? 40 | new ConsecutiveByteDigitSet(digits.get(0)) : 41 | new ByteToIntMap(digits); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteSet.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.util.LinkedHashSet; 8 | import java.util.Set; 9 | 10 | /** 11 | * Interface for sets of bytes. 12 | */ 13 | public interface ByteSet { 14 | /** 15 | * Returns true if the set contains the specified byte. 16 | * 17 | * @param b a byte 18 | * @return true if the byte is in the set 19 | */ 20 | boolean containsKey(byte b); 21 | 22 | /** 23 | * Creates a new {@link ByteSet} from the provided set. 24 | * 25 | * @param set a set of characters 26 | * @param ignoreCase whether the {@link ByteSet} shall ignore the 27 | * case of the characters 28 | * @return a new {@link ByteSet} instance 29 | */ 30 | static ByteSet copyOf(Set set, boolean ignoreCase) { 31 | set = applyIgnoreCase(set, ignoreCase); 32 | switch (set.size()) { 33 | case 0: 34 | return new ByteSetOfNone(); 35 | case 1: 36 | return new ByteSetOfOne(set); 37 | default: 38 | return set.size() < 5 ? new ByteSetOfFew(set) : new ByteToIntMap(set); 39 | } 40 | } 41 | 42 | /** 43 | * Creates a copy of the provided set, or returns the same set. 44 | *

45 | * If {@code ignoreCase} is set to true, the copy will contain 46 | * an upper and lower case character for each character in the provided 47 | * set. 48 | * 49 | * @param set a set of characters 50 | * @param ignoreCase whether the copy of the set shall contain 51 | * upper and lower case characters from the 52 | * provided set 53 | * @return a new set if {@code ignoreCase} is false, otherwise a copy of the set 54 | */ 55 | static Set applyIgnoreCase(Set set, boolean ignoreCase) { 56 | if (ignoreCase) { 57 | LinkedHashSet convertedSet = new LinkedHashSet(); 58 | for (Character ch : set) { 59 | // Add the original input char. 60 | convertedSet.add(ch); 61 | 62 | // Convert to lower case. This does not cover all cases. 63 | char lc = Character.toLowerCase(ch); 64 | 65 | // We have to convert to upper case and then to lower case 66 | // because of sophisticated writing systems, like Georgian script. 67 | char uc = Character.toUpperCase(ch); 68 | char uclc = Character.toLowerCase(uc); 69 | 70 | convertedSet.add(lc); 71 | convertedSet.add(uc); 72 | convertedSet.add(uclc); 73 | } 74 | set = convertedSet; 75 | } 76 | return set; 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteSetOfFew.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteSetOfFew.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Arrays; 9 | import java.util.Set; 10 | 11 | /** 12 | * A set of {@code char} with linear search. 13 | */ 14 | final class ByteSetOfFew implements ByteSet { 15 | private final byte[] bytes; 16 | 17 | public ByteSetOfFew(Set set) { 18 | byte[] tmp = new byte[set.size() * 4]; 19 | int i = 0; 20 | for (char ch : set) { 21 | for (byte b : String.valueOf(ch).getBytes(StandardCharsets.UTF_8)) { 22 | tmp[i++] = b; 23 | } 24 | } 25 | this.bytes = Arrays.copyOf(tmp, i); 26 | } 27 | 28 | public boolean containsKey(byte b) { 29 | boolean found = false; 30 | for (byte aChar : bytes) { 31 | found |= aChar == b; 32 | } 33 | return found; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteSetOfNone.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteSetOfNone.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | final class ByteSetOfNone implements ByteSet { 8 | 9 | ByteSetOfNone() { 10 | } 11 | 12 | public boolean containsKey(byte b) { 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteSetOfOne.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteSetOfOne.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.util.Set; 8 | 9 | final class ByteSetOfOne implements ByteSet { 10 | private final byte ch; 11 | 12 | ByteSetOfOne(Set set) { 13 | if (set.size() != 1) throw new IllegalArgumentException("set size must be 1, size=" + set.size()); 14 | char ch = set.iterator().next(); 15 | if (ch > 127) 16 | throw new IllegalArgumentException("can not map to a single byte. ch='" + ch + "' 0x" + Integer.toHexString(ch)); 17 | this.ch = (byte) ch; 18 | } 19 | 20 | @Override 21 | public boolean containsKey(byte b) { 22 | return this.ch == b; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteToIntMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteToIntMap.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.util.Collection; 8 | 9 | /** 10 | * A primitive map {@literal Map}. 11 | */ 12 | final class ByteToIntMap implements ByteDigitSet, ByteSet { 13 | 14 | 15 | public ByteToIntMap(Collection chars) { 16 | this(chars.size()); 17 | int i = 0; 18 | for (char ch : chars) { 19 | if (ch > 127) throw new IllegalArgumentException("can not map to a single byte. ch=" + ch); 20 | put((byte) ch, i++); 21 | } 22 | } 23 | 24 | @Override 25 | public boolean containsKey(byte b) { 26 | return getOrDefault(b, -1) >= 0; 27 | } 28 | 29 | @Override 30 | public int toDigit(byte ch) { 31 | return getOrDefault(ch, 10); 32 | } 33 | 34 | private static class Node { 35 | byte key; 36 | int value; 37 | Node next; 38 | 39 | public Node(byte key, int value) { 40 | this.key = key; 41 | this.value = value; 42 | } 43 | } 44 | 45 | private Node[] table; 46 | 47 | public ByteToIntMap(int maxSize) { 48 | // int n = BigInteger.valueOf(maxSize*2).nextProbablePrime().intValue(); 49 | int n = (-1 >>> Integer.numberOfLeadingZeros(maxSize * 2)) + 1; 50 | this.table = new Node[n]; 51 | } 52 | 53 | public void put(byte key, int value) { 54 | int index = getIndex(key); 55 | Node found = table[index]; 56 | if (found == null) { 57 | table[index] = new Node(key, value); 58 | } else { 59 | while (found.next != null && found.key != key) { 60 | found = found.next; 61 | } 62 | if (found.key == key) { 63 | found.value = value; 64 | } else { 65 | found.next = new Node(key, value); 66 | } 67 | } 68 | } 69 | 70 | private int getIndex(byte key) { 71 | return key & (table.length - 1); 72 | } 73 | 74 | public int getOrDefault(byte key, int defaultValue) { 75 | int index = getIndex(key); 76 | Node found = table[index]; 77 | while (found != null) { 78 | if (found.key == key) return found.value; 79 | found = found.next; 80 | } 81 | return defaultValue; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteTrieNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteTrieNode.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * Represents a node in a {@link ch.randelshofer.fastdoubleparser.bte.ByteTrieOfFew} 11 | */ 12 | final class ByteTrieNode { 13 | private byte[] chars = new byte[0]; 14 | private ByteTrieNode[] children = new ByteTrieNode[0]; 15 | private boolean isEnd; 16 | 17 | 18 | public ByteTrieNode() { 19 | } 20 | 21 | /** 22 | * Insert a character into this node if it does not already exist. 23 | * Returns the child node corresponding to the char. 24 | * 25 | * @param ch the character 26 | * @return the child node corresponding to the char 27 | */ 28 | public ByteTrieNode insert(byte ch) { 29 | int index = indexOf(ch); 30 | if (index < 0) { 31 | index = chars.length; 32 | chars = Arrays.copyOf(chars, chars.length + 1); 33 | children = Arrays.copyOf(children, children.length + 1); 34 | chars[index] = ch; 35 | children[index] = new ByteTrieNode(); 36 | } 37 | return children[index]; 38 | } 39 | 40 | /** 41 | * Insert a character into this node if it does not already exist. 42 | * Forces the node 'forceNode' to be inserted. 43 | * 44 | * @param ch the character 45 | * @param forcedNode the forced node 46 | * @return the forced node 47 | */ 48 | public ByteTrieNode insert(byte ch, ByteTrieNode forcedNode) { 49 | int index = indexOf(ch); 50 | if (index < 0) { 51 | index = chars.length; 52 | chars = Arrays.copyOf(chars, chars.length + 1); 53 | children = Arrays.copyOf(children, children.length + 1); 54 | chars[index] = ch; 55 | children[index] = forcedNode; 56 | } 57 | if (children[index] != forcedNode) { 58 | throw new AssertionError("trie is corrupt"); 59 | } 60 | return children[index]; 61 | } 62 | 63 | /** 64 | * Gets the child not for the given character, if it exists. 65 | * 66 | * @param ch the character 67 | * @return the child node corresponding to the char, or the sentinel node 68 | */ 69 | public ByteTrieNode get(byte ch) { 70 | int index = indexOf(ch); 71 | return index < 0 ? null : children[index]; 72 | } 73 | 74 | /** 75 | * Returns the index of the specified character in this node. 76 | * 77 | * @param ch the character 78 | * @return the index or -1 79 | */ 80 | private int indexOf(byte ch) { 81 | // intentionally 'branchless' loop 82 | int index = -1; 83 | for (int i = 0; i < chars.length; i++) { 84 | if (chars[i] == ch) index = i; 85 | } 86 | return index; 87 | } 88 | 89 | public void setEnd() { 90 | isEnd = true; 91 | } 92 | 93 | public boolean isEnd() { 94 | return isEnd; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteTrieOfFew.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteTrieOfFew.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Set; 9 | 10 | /** 11 | * A trie for testing if a String is contained in a set of Strings. 12 | */ 13 | final class ByteTrieOfFew implements ByteTrie { 14 | private ByteTrieNode root = new ByteTrieNode(); 15 | 16 | public ByteTrieOfFew(Set set) { 17 | for (String str : set) { 18 | if (!str.isEmpty()) { 19 | add(str); 20 | } 21 | } 22 | } 23 | 24 | private void add(String str) { 25 | ByteTrieNode node = root; 26 | byte[] strBytes = str.getBytes(StandardCharsets.UTF_8); 27 | for (int i = 0; i < strBytes.length; i++) { 28 | node = node.insert(strBytes[i]); 29 | } 30 | node.setEnd(); 31 | } 32 | 33 | 34 | @Override 35 | public int match(byte[] str, int startIndex, int endIndex) { 36 | ByteTrieNode node = root; 37 | int longestMatch = startIndex; 38 | for (int i = startIndex; i < endIndex; i++) { 39 | node = node.get(str[i]); 40 | if (node == null) break; 41 | longestMatch = node.isEnd() ? i + 1 : longestMatch; 42 | } 43 | return longestMatch - startIndex; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteTrieOfNone.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteTrieOfNone.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | final class ByteTrieOfNone implements ByteTrie { 8 | 9 | 10 | @Override 11 | public int match(byte[] str) { 12 | return 0; 13 | } 14 | 15 | @Override 16 | public int match(byte[] str, int startIndex, int endIndex) { 17 | return 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteTrieOfOne.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteTrieOfOne.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Set; 9 | 10 | final class ByteTrieOfOne implements ByteTrie { 11 | private final byte[] chars; 12 | 13 | public ByteTrieOfOne(Set set) { 14 | if (set.size() != 1) throw new IllegalArgumentException("set size must be 1, size=" + set.size()); 15 | chars = set.iterator().next().getBytes(StandardCharsets.UTF_8); 16 | } 17 | 18 | 19 | @Override 20 | public int match(byte[] str) { 21 | return match(str, 0, str.length); 22 | } 23 | 24 | @Override 25 | public int match(byte[] str, int startIndex, int endIndex) { 26 | int i = 0; 27 | int limit = Math.min(endIndex - startIndex, chars.length); 28 | while (i < limit && str[i + startIndex] == chars[i]) { 29 | i++; 30 | } 31 | return i == chars.length ? chars.length : 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteTrieOfOneSingleByte.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteTrieOfOne.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Set; 9 | 10 | final class ByteTrieOfOneSingleByte implements ByteTrie { 11 | private final byte ch; 12 | 13 | public ByteTrieOfOneSingleByte(Set set) { 14 | if (set.size() != 1) throw new IllegalArgumentException("set size must be 1, size=" + set.size()); 15 | byte[] chars = set.iterator().next().getBytes(StandardCharsets.UTF_8); 16 | if (chars.length != 1) throw new IllegalArgumentException("char size must be 1, size=" + set.size()); 17 | ch = chars[0]; 18 | } 19 | 20 | public ByteTrieOfOneSingleByte(byte ch) { 21 | this.ch = ch; 22 | } 23 | 24 | 25 | @Override 26 | public int match(byte[] str) { 27 | return match(str, 0, str.length); 28 | } 29 | 30 | @Override 31 | public int match(byte[] str, int startIndex, int endIndex) { 32 | return startIndex < endIndex && str[startIndex] == ch ? 1 : 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ConsecutiveByteDigitSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ConsecutiveByteDigitSet.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | final class ConsecutiveByteDigitSet implements ByteDigitSet { 8 | private final byte zeroDigit; 9 | 10 | public ConsecutiveByteDigitSet(char zeroDigit) { 11 | if (zeroDigit > 127) { 12 | throw new IllegalArgumentException("can not map to a single byte. zeroDigit=" + zeroDigit + "' 0x" + Integer.toHexString(zeroDigit)); 13 | } 14 | this.zeroDigit = (byte) zeroDigit; 15 | } 16 | 17 | @Override 18 | public int toDigit(byte ch) { 19 | return (char) (ch - zeroDigit); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)package-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * This is a module-private package. 8 | *

9 | * It provides primitive {@code byte} collections. 10 | */ 11 | package ch.randelshofer.fastdoubleparser.bte; -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharDigitSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharDigitSet.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import ch.randelshofer.fastdoubleparser.bte.ByteDigitSet; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Interface for sets of digit characters. 13 | */ 14 | public interface CharDigitSet { 15 | /** 16 | * Returns a value in the range 0 to 9 if the specified character is a digit. 17 | * Otherwise, Returns a value greater than 9. 18 | * 19 | * @param ch a character 20 | * @return a value in the range 0 to Integer.MAX_VALUE. 21 | */ 22 | int toDigit(char ch); 23 | 24 | 25 | /** 26 | * Creates a new {@link CharDigitSet} instead from the 27 | * specified list. 28 | *

29 | * The list must contain characters for the digits 0 to 9. 30 | * 31 | * @param digits a list of digit characters 32 | * @return a new {@link ByteDigitSet} instance 33 | */ 34 | @SuppressWarnings("SequencedCollectionMethodCanBeUsed") 35 | static CharDigitSet copyOf(List digits) { 36 | boolean consecutive = true; 37 | char zeroDigit = digits.get(0); 38 | for (int i = 1; i < 10; i++) { 39 | char current = digits.get(i); 40 | consecutive &= current == zeroDigit + i; 41 | } 42 | return consecutive ? 43 | new ConsecutiveCharDigitSet(digits.get(0)) : 44 | new CharToIntMap(digits); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteSet.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import ch.randelshofer.fastdoubleparser.bte.ByteSet; 8 | 9 | import java.util.LinkedHashSet; 10 | import java.util.Set; 11 | 12 | /** 13 | * Interface for sets of characters. 14 | */ 15 | public interface CharSet { 16 | /** 17 | * Returns true if the set contains the specified character. 18 | * 19 | * @param ch a character 20 | * @return true if the byte is in the set 21 | */ 22 | boolean containsKey(char ch); 23 | 24 | 25 | /** 26 | * Creates a new {@link CharSet} from the provided set. 27 | * 28 | * @param set a set of characters 29 | * @param ignoreCase whether the {@link CharSet} shall ignore the 30 | * case of the characters 31 | * @return a new {@link ByteSet} instance 32 | */ 33 | static CharSet copyOf(Set set, boolean ignoreCase) { 34 | set = applyIgnoreCase(set, ignoreCase); 35 | switch (set.size()) { 36 | case 0: 37 | return new CharSetOfNone(); 38 | case 1: 39 | return new CharSetOfOne(set); 40 | default: 41 | return set.size() < 5 ? new CharSetOfFew(set) : new CharToIntMap(set); 42 | } 43 | } 44 | 45 | /** 46 | * Creates a copy of the provided set, or returns the same set. 47 | *

48 | * If {@code ignoreCase} is set to true, the copy will contain 49 | * an upper and lower case character for each character in the provided 50 | * set. 51 | * 52 | * @param set a set of characters 53 | * @param ignoreCase whether the copy of the set shall contain 54 | * upper and lower case characters from the 55 | * provided set 56 | * @return a new set if {@code ignoreCase} is false, otherwise a copy of the set 57 | */ 58 | static Set applyIgnoreCase(Set set, boolean ignoreCase) { 59 | if (ignoreCase) { 60 | LinkedHashSet convertedSet = new LinkedHashSet(); 61 | for (Character ch : set) { 62 | // Add the original input char. 63 | convertedSet.add(ch); 64 | 65 | // Convert to lower case. This does not cover all cases. 66 | char lc = Character.toLowerCase(ch); 67 | 68 | // We have to convert to upper case and then to lower case 69 | // because of sophisticated writing systems, like Georgian script. 70 | char uc = Character.toUpperCase(ch); 71 | char uclc = Character.toLowerCase(uc); 72 | 73 | convertedSet.add(lc); 74 | convertedSet.add(uc); 75 | convertedSet.add(uclc); 76 | } 77 | set = convertedSet; 78 | } 79 | return set; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharSetOfFew.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharSetOfFew.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * A set of {@code char} with linear search. 11 | */ 12 | final class CharSetOfFew implements CharSet { 13 | private final char[] chars; 14 | 15 | public CharSetOfFew(Set set) { 16 | this.chars = new char[set.size()]; 17 | int i = 0; 18 | for (Character ch : set) { 19 | chars[i++] = ch; 20 | } 21 | } 22 | 23 | public boolean containsKey(char ch) { 24 | boolean found = false; 25 | for (char aChar : chars) { 26 | found |= aChar == ch; 27 | } 28 | return found; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharSetOfNone.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharSetOfNone.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | public final class CharSetOfNone implements CharSet { 8 | 9 | public CharSetOfNone() { 10 | } 11 | 12 | public boolean containsKey(char ch) { 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharSetOfOne.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharSetOfOne.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Set; 8 | 9 | final class CharSetOfOne implements CharSet { 10 | private final char ch; 11 | 12 | CharSetOfOne(Set set) { 13 | if (set.size() != 1) throw new IllegalArgumentException("set size must be 1, size=" + set.size()); 14 | this.ch = set.iterator().next(); 15 | 16 | } 17 | 18 | public boolean containsKey(char ch) { 19 | return this.ch == ch; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharToIntMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharToIntMap.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Collection; 8 | 9 | /** 10 | * A primitive map {@literal Map}. 11 | */ 12 | final class CharToIntMap implements CharDigitSet, CharSet { 13 | 14 | 15 | public CharToIntMap(Collection chars) { 16 | this(chars.size()); 17 | int i = 0; 18 | for (char ch : chars) { 19 | put(ch, i++); 20 | } 21 | } 22 | 23 | @Override 24 | public boolean containsKey(char key) { 25 | return getOrDefault(key, -1) >= 0; 26 | } 27 | 28 | @Override 29 | public int toDigit(char ch) { 30 | return getOrDefault(ch, 10); 31 | } 32 | 33 | private static class Node { 34 | char key; 35 | int value; 36 | Node next; 37 | 38 | public Node(char key, int value) { 39 | this.key = key; 40 | this.value = value; 41 | } 42 | } 43 | 44 | private Node[] table; 45 | 46 | public CharToIntMap(int maxSize) { 47 | // int n = BigInteger.valueOf(maxSize*2).nextProbablePrime().intValue(); 48 | int n = (-1 >>> Integer.numberOfLeadingZeros(maxSize * 2)) + 1; 49 | this.table = new Node[n]; 50 | } 51 | 52 | public void put(char key, int value) { 53 | int index = getIndex(key); 54 | Node found = table[index]; 55 | if (found == null) { 56 | table[index] = new Node(key, value); 57 | } else { 58 | while (found.next != null && found.key != key) { 59 | found = found.next; 60 | } 61 | if (found.key == key) { 62 | found.value = value; 63 | } else { 64 | found.next = new Node(key, value); 65 | } 66 | } 67 | } 68 | 69 | private int getIndex(char key) { 70 | return key & (table.length - 1); 71 | } 72 | 73 | public int getOrDefault(char key, int defaultValue) { 74 | int index = getIndex(key); 75 | Node found = table[index]; 76 | while (found != null) { 77 | if (found.key == key) return found.value; 78 | found = found.next; 79 | } 80 | return defaultValue; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrie.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteTrie.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * Interface for a data retrieval tree (trie) of characters. 11 | */ 12 | public interface CharTrie { 13 | /** 14 | * Searches for the longest matching string in the trie 15 | * that matches the provided string. 16 | * 17 | * @param str a string in the form of a {@link CharSequence} 18 | * @return the length of the longest matching string, or 0 if no string matches 19 | */ 20 | default int match(CharSequence str) { 21 | return match(str, 0, str.length()); 22 | } 23 | 24 | /** 25 | * Searches for the longest matching string in the trie 26 | * that matches the provided string. 27 | * 28 | * @param str a string in the form of a char array 29 | * @return the length of the longest matching string, or 0 if no string matches 30 | */ 31 | default int match(char[] str) { 32 | return match(str, 0, str.length); 33 | } 34 | 35 | 36 | /** 37 | * Searches for the longest matching string in the trie 38 | * that matches the provided string. 39 | * 40 | * @param str a string 41 | * @param startIndex start index (inclusive) 42 | * @param endIndex end index (exclusive) 43 | * @return the length of the longest matching string, or 0 if no string matches 44 | */ 45 | int match(CharSequence str, int startIndex, int endIndex); 46 | 47 | /** 48 | * Searches for the longest matching string in the trie 49 | * that matches the provided string. 50 | * 51 | * @param str a string 52 | * @param startIndex start index (inclusive) 53 | * @param endIndex end index (exclusive) 54 | * @return the length of the longest matching string, or 0 if no string matches 55 | */ 56 | int match(char[] str, int startIndex, int endIndex); 57 | 58 | 59 | /** 60 | * Creates a new {@link CharTrie} from the provided set. 61 | * 62 | * @param set a set of strings 63 | * @param ignoreCase whether the {@link CharTrie} shall ignore the 64 | * case of the characters 65 | * @return a new {@link CharTrie} instance 66 | */ 67 | static CharTrie copyOf(Set set, boolean ignoreCase) { 68 | switch (set.size()) { 69 | case 0: 70 | return new CharTrieOfNone(); 71 | case 1: 72 | if (set.iterator().next().length() == 1) { 73 | return ignoreCase ? new CharTrieOfFewIgnoreCase(set) : new CharTrieOfOneSingleChar(set); 74 | } 75 | return ignoreCase ? new CharTrieOfFewIgnoreCase(set) : new CharTrieOfOne(set); 76 | default: 77 | return ignoreCase ? new CharTrieOfFewIgnoreCase(set) : new CharTrieOfFew(set); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieNode.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | 8 | import java.util.Arrays; 9 | 10 | final class CharTrieNode { 11 | private char[] chars = new char[0]; 12 | private CharTrieNode[] children = new CharTrieNode[0]; 13 | private boolean isEnd; 14 | 15 | 16 | 17 | public CharTrieNode() { 18 | } 19 | 20 | /** 21 | * Insert a character into this node if it does not already exist. 22 | * Returns the child node corresponding to the char. 23 | * 24 | * @param ch the character 25 | * @return the child node corresponding to the char 26 | */ 27 | public CharTrieNode insert(char ch) { 28 | int index = indexOf(ch); 29 | if (index < 0) { 30 | index = chars.length; 31 | chars = Arrays.copyOf(chars, chars.length + 1); 32 | children = Arrays.copyOf(children, children.length + 1); 33 | chars[index] = ch; 34 | children[index] = new CharTrieNode(); 35 | } 36 | return children[index]; 37 | } 38 | 39 | /** 40 | * Gets the child not for the given character, if it exists. 41 | * 42 | * @param ch the character 43 | * @return the child node corresponding to the char, or the sentinel node 44 | */ 45 | public CharTrieNode get(char ch) { 46 | int index = indexOf(ch); 47 | return index < 0 ? null : children[index]; 48 | } 49 | 50 | /** 51 | * Returns the index of the specified character in this node. 52 | * 53 | * @param ch the character 54 | * @return the index or -1 55 | */ 56 | private int indexOf(char ch) { 57 | // intentionally 'branchless' loop 58 | int index = -1; 59 | for (int i = 0; i < chars.length; i++) { 60 | if (chars[i] == ch) index = i; 61 | } 62 | return index; 63 | } 64 | 65 | public void setEnd() { 66 | isEnd = true; 67 | } 68 | 69 | public boolean isEnd() { 70 | return isEnd; 71 | } 72 | 73 | /** 74 | * Insert a character into this node if it does not already exist. 75 | * Forces the node 'forceNode' to be inserted. 76 | * 77 | * @param ch the character 78 | * @param forcedNode the forced node 79 | * @return the forced node 80 | */ 81 | public CharTrieNode insert(char ch, CharTrieNode forcedNode) { 82 | int index = indexOf(ch); 83 | if (index < 0) { 84 | index = chars.length; 85 | chars = Arrays.copyOf(chars, chars.length + 1); 86 | children = Arrays.copyOf(children, children.length + 1); 87 | chars[index] = ch; 88 | children[index] = forcedNode; 89 | } 90 | if (children[index] != forcedNode) { 91 | throw new AssertionError("trie is corrupt"); 92 | } 93 | return children[index]; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieOfFew.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieOfFew.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * A trie for testing if a String is contained in a set of Strings. 11 | */ 12 | final class CharTrieOfFew implements CharTrie { 13 | private CharTrieNode root = new CharTrieNode(); 14 | 15 | public CharTrieOfFew(Set set) { 16 | for (String str : set) { 17 | if (!str.isEmpty()) { 18 | add(str); 19 | } 20 | } 21 | } 22 | 23 | private void add(String str) { 24 | CharTrieNode node = root; 25 | for (int i = 0; i < str.length(); i++) { 26 | node = node.insert(str.charAt(i)); 27 | } 28 | node.setEnd(); 29 | } 30 | 31 | 32 | @Override 33 | public int match(CharSequence str, int startIndex, int endIndex) { 34 | CharTrieNode node = root; 35 | int longestMatch = startIndex; 36 | for (int i = startIndex; i < endIndex; i++) { 37 | node = node.get(str.charAt(i)); 38 | if (node == null) break; 39 | longestMatch = node.isEnd() ? i + 1 : longestMatch; 40 | } 41 | return longestMatch - startIndex; 42 | } 43 | 44 | @Override 45 | public int match(char[] str, int startIndex, int endIndex) { 46 | CharTrieNode node = root; 47 | int longestMatch = startIndex; 48 | for (int i = startIndex; i < endIndex; i++) { 49 | node = node.get(str[i]); 50 | if (node == null) break; 51 | longestMatch = node.isEnd() ? i + 1 : longestMatch; 52 | } 53 | return longestMatch - startIndex; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieOfFewIgnoreCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieOfFewIgnoreCase.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * A trie for testing if a String is contained in a set of Strings. 11 | *

12 | * This trie is a directed acyclic graph. 13 | *

14 | *

15 |  *     Given: the strings: "NaN", "Inf"
16 |  *     The trie will have the following structure:
17 |  *
18 |  *     root ['N','n', 'I','i']
19 |  *            │   │    │   │
20 |  *            │   │    └─┬─┘
21 |  *            │   │      └─→ node ['N','n']
22 |  *            │   │                 │   │
23 |  *            │   │                 └─┬─┘
24 |  *            └─┬─┘                   └─→ node ['F','f']
25 |  *              └─→ node ['A','a']
26 |  *                         │   │
27 |  *                         └─┬─┘
28 |  *                           └─→ node ['N','N']
29 |  * 
30 | */ 31 | final class CharTrieOfFewIgnoreCase implements CharTrie { 32 | private CharTrieNode root = new CharTrieNode(); 33 | 34 | public CharTrieOfFewIgnoreCase(Set set) { 35 | for (String str : set) { 36 | if (!str.isEmpty()) { 37 | add(str); 38 | } 39 | } 40 | } 41 | 42 | private void add(String str) { 43 | // We have to convert to upper case and then to lower case 44 | // because of sophisticated writing systems, like Georgian script. 45 | CharTrieNode upperNode = root; 46 | CharTrieNode lowerNode = root; 47 | String upperStr = str.toUpperCase(); 48 | String lowerStr = upperStr.toLowerCase(); 49 | for (int i = 0; i < str.length(); i++) { 50 | char upper = upperStr.charAt(i); 51 | char lower = lowerStr.charAt(i); 52 | upperNode = upperNode.insert(upper); 53 | lowerNode = lowerNode.insert(lower, upperNode); 54 | 55 | } 56 | upperNode.setEnd(); 57 | } 58 | 59 | 60 | @Override 61 | public int match(char[] str, int startIndex, int endIndex) { 62 | CharTrieNode node = root; 63 | int longestMatch = startIndex; 64 | for (int i = startIndex; i < endIndex; i++) { 65 | node = node.get(str[i]); 66 | if (node == null) break; 67 | longestMatch = node.isEnd() ? i + 1 : longestMatch; 68 | } 69 | return longestMatch - startIndex; 70 | } 71 | 72 | @Override 73 | public int match(CharSequence str, int startIndex, int endIndex) { 74 | CharTrieNode node = root; 75 | int longestMatch = startIndex; 76 | for (int i = startIndex; i < endIndex; i++) { 77 | node = node.get(str.charAt(i)); 78 | if (node == null) break; 79 | longestMatch = node.isEnd() ? i + 1 : longestMatch; 80 | } 81 | return longestMatch - startIndex; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieOfNone.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieOfNone.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | final class CharTrieOfNone implements CharTrie { 8 | @Override 9 | public int match(CharSequence str) { 10 | return 0; 11 | } 12 | 13 | @Override 14 | public int match(CharSequence str, int startIndex, int endIndex) { 15 | return 0; 16 | } 17 | 18 | @Override 19 | public int match(char[] str) { 20 | return 0; 21 | } 22 | 23 | @Override 24 | public int match(char[] str, int startIndex, int endIndex) { 25 | return 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieOfOne.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieOfOne.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Set; 8 | 9 | final class CharTrieOfOne implements CharTrie { 10 | private final char[] chars; 11 | 12 | public CharTrieOfOne(Set set) { 13 | if (set.size() != 1) throw new IllegalArgumentException("set size must be 1, size=" + set.size()); 14 | chars = set.iterator().next().toCharArray(); 15 | } 16 | 17 | public CharTrieOfOne(char[] chars) { 18 | this.chars = chars; 19 | } 20 | 21 | @Override 22 | public int match(CharSequence str) { 23 | return match(str, 0, str.length()); 24 | } 25 | 26 | @Override 27 | public int match(CharSequence str, int startIndex, int endIndex) { 28 | int i = 0; 29 | int limit = Math.min(endIndex - startIndex, chars.length); 30 | while (i < limit && str.charAt(i + startIndex) == chars[i]) { 31 | i++; 32 | } 33 | return i == chars.length ? chars.length : 0; 34 | } 35 | 36 | @Override 37 | public int match(char[] str) { 38 | return match(str, 0, str.length); 39 | } 40 | 41 | @Override 42 | public int match(char[] str, int startIndex, int endIndex) { 43 | /* 44 | int mismatch = Arrays.mismatch(chars, 0, chars.length, str, startIndex,startIndex+ Math.min(endIndex - startIndex, chars.length)); 45 | return mismatch<0?chars.length:0; 46 | */ 47 | int i = 0; 48 | int limit = Math.min(endIndex - startIndex, chars.length); 49 | while (i < limit && str[i + startIndex] == chars[i]) { 50 | i++; 51 | } 52 | return i == chars.length ? chars.length : 0; 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieOfOneSingleChar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ByteTrieOfOne.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import java.util.Set; 8 | 9 | final class CharTrieOfOneSingleChar implements CharTrie { 10 | private final char ch; 11 | 12 | public CharTrieOfOneSingleChar(Set set) { 13 | if (set.size() != 1) throw new IllegalArgumentException("set size must be 1, size=" + set.size()); 14 | char[] chars = set.iterator().next().toCharArray(); 15 | if (chars.length != 1) throw new IllegalArgumentException("char size must be 1, size=" + set.size()); 16 | ch = chars[0]; 17 | } 18 | 19 | public CharTrieOfOneSingleChar(char ch) { 20 | this.ch = ch; 21 | } 22 | 23 | @Override 24 | public int match(CharSequence str, int startIndex, int endIndex) { 25 | return startIndex < endIndex && str.charAt(startIndex) == ch ? 1 : 0; 26 | } 27 | 28 | @Override 29 | public int match(char[] str) { 30 | return match(str, 0, str.length); 31 | } 32 | 33 | @Override 34 | public int match(char[] str, int startIndex, int endIndex) { 35 | return startIndex < endIndex && str[startIndex] == ch ? 1 : 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/ConsecutiveCharDigitSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)ConsecutiveByteDigitSet.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | final class ConsecutiveCharDigitSet implements CharDigitSet { 8 | private final char zeroDigit; 9 | 10 | public ConsecutiveCharDigitSet(char zeroDigit) { 11 | this.zeroDigit = zeroDigit; 12 | } 13 | 14 | @Override 15 | public int toDigit(char ch) { 16 | return (char) (ch - zeroDigit); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/FormatCharSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)FormatCharSet.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | /** 8 | * This format set contains all Unicode format chars. 9 | */ 10 | public class FormatCharSet implements CharSet { 11 | @Override 12 | public boolean containsKey(char ch) { 13 | return Character.getType(ch) == Character.FORMAT; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)package-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * This is a module-private package. 8 | *

9 | * It provides primitive {@code char} collections. 10 | */ 11 | package ch.randelshofer.fastdoubleparser.chr; -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)package-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * Provides fast parsers for Java {@code FloatingPointLiteral}s, 8 | * and JSON {@code number}s. 9 | *

10 | * References: 11 | *

12 | *
The Java® Language Specification, Java SE 18 Edition, 13 | * Chapter 3. Lexical Structure, 3.10.2. Floating-Point Literals
14 | *
docs.oracle.com
15 | * 16 | *
IETF RFC 8259. The JavaScript Object Notation (JSON) Data Interchange 17 | * Format, Chapter 6. Numbers
18 | *
www.ietf.org
19 | *
20 | */ 21 | package ch.randelshofer.fastdoubleparser; -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/java/ch.randelshofer.fastdoubleparser/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)module-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * Provides fast parsers for Java {@code FloatingPointLiteral}s, 8 | * and JSON {@code number}s. 9 | */ 10 | module ch.randelshofer.fastdoubleparser { 11 | requires jdk.incubator.vector; 12 | exports ch.randelshofer.fastdoubleparser; 13 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/main/resources/ch.randelshofer.fastdoubleparser/META-INF/thirdparty-LICENSE: -------------------------------------------------------------------------------- 1 | ------ 2 | Third-party license for 3 | fast_float, Copyright (c) 2021 The fast_float authors. MIT License. 4 | https://github.com/fastfloat/fast_float 5 | https://github.com/fastfloat/fast_float/blob/35d523195bf7d57aba0e735ad6eba1e6f71ba8d6/LICENSE-MIT 6 | 7 | MIT License 8 | 9 | Copyright (c) 2021 The fast_float authors 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | ------ 29 | Third-party license for 30 | bigint, Copyright 2020 Tim Buktu. 2-clause BSD License. 31 | https://github.com/tbuktu/bigint/tree/floatfft 32 | https://github.com/tbuktu/bigint/blob/617c8cd8a7c5e4fb4d919c6a4d11e2586107f029/LICENSE 33 | https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE 34 | (We only use those portions of the bigint project that can be licensed under 2-clause BSD License.) 35 | 36 | 2-clause BSD License 37 | 38 | Copyright 2022 Tim Buktu 39 | 40 | Redistribution and use in source and binary forms, with or without 41 | modification, are permitted provided that the following conditions 42 | are met: 43 | 44 | 1. Redistributions of source code must retain the above copyright notice, this 45 | list of conditions and the following disclaimer. 46 | 47 | 2. Redistributions in binary form must reproduce the above copyright notice, 48 | this list of conditions and the following disclaimer in the documentation 49 | and/or other materials provided with the distribution. 50 | 51 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND 52 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 53 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 54 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 55 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 57 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 58 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 59 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 60 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/BigSignificandTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)BigSignificandTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.math.BigInteger; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | /** 14 | * Unit tests for {@link BigSignificand}. 15 | */ 16 | public final class BigSignificandTest { 17 | @Test 18 | public void shouldAddValuesThatExceedIntValueRange() { 19 | int value = 2_000_000_000; 20 | int addend = 1_000_000_000; 21 | 22 | BigInteger expected = BigInteger.valueOf(value); 23 | expected = expected.add(BigInteger.valueOf(addend)); 24 | 25 | BigSignificand instance = new BigSignificand(64); 26 | instance.add(value); 27 | instance.add(addend); 28 | BigInteger actual = instance.toBigInteger(); 29 | 30 | assertEquals(expected, actual); 31 | } 32 | 33 | @Test 34 | public void shouldMultiplyValuesThatExceedIntValueRange() { 35 | int value = 2_000_000_000; 36 | int factor = 1_000_000_000; 37 | 38 | BigInteger expected = BigInteger.valueOf(value); 39 | expected = expected.multiply(BigInteger.valueOf(factor)); 40 | 41 | BigSignificand instance = new BigSignificand(64); 42 | instance.add(value); 43 | instance.fma(factor, 0); 44 | BigInteger actual = instance.toBigInteger(); 45 | 46 | assertEquals(expected, actual); 47 | } 48 | 49 | @Test 50 | public void shouldFmaValuesThatExceedIntValueRange() { 51 | int value = 2_000_000_000; 52 | int addend = 1_900_000_000; 53 | int factor = 1_000_000_000; 54 | 55 | BigInteger expected = BigInteger.valueOf(value); 56 | expected = expected.multiply(BigInteger.valueOf(factor)); 57 | expected = expected.add(BigInteger.valueOf(addend)); 58 | 59 | BigSignificand instance = new BigSignificand(64); 60 | instance.fma(1, value); 61 | instance.fma(factor, addend); 62 | BigInteger actual = instance.toBigInteger(); 63 | 64 | assertEquals(expected, actual); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastIntegerMathTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)FastIntegerMathTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.params.ParameterizedTest; 9 | import org.junit.jupiter.params.provider.Arguments; 10 | import org.junit.jupiter.params.provider.MethodSource; 11 | 12 | import java.util.stream.Stream; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | 16 | public final class FastIntegerMathTest { 17 | @Test 18 | public void testUnsignedMultiplyHigh() { 19 | long actualHigh = FastIntegerMath.unsignedMultiplyHigh(0x123456789ABCDEF0L, 0x10L); 20 | assertEquals(1L, actualHigh); 21 | 22 | actualHigh = FastIntegerMath.unsignedMultiplyHigh(0x123456789ABCDEF0L, -0x10L); 23 | assertEquals(0x123456789abcdeeeL, actualHigh); 24 | } 25 | 26 | private static Stream splitFloor16testData() { 27 | return Stream.of(// from, to, expectedMid 28 | Arguments.of(0, 0, 0), 29 | Arguments.of(0, 16, 0), 30 | Arguments.of(10, 30, 14), 31 | Arguments.of(0, 32, 16), 32 | Arguments.of(10, 40, 24), 33 | Arguments.of(0, 100, 36), 34 | Arguments.of(1, 101, 37) 35 | ); 36 | } 37 | 38 | @ParameterizedTest 39 | @MethodSource("splitFloor16testData") 40 | void splitFloor16specialValues(int from, int to, int expectedMid) { 41 | assert (to - expectedMid) % 16 == 0; 42 | assert expectedMid <= from + (to - from) / 2; 43 | assert expectedMid >= from + (to - from) / 2 - 15; 44 | 45 | assertEquals(expectedMid, FastIntegerMath.splitFloor16(from, to)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhBigDecimalScalability.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhBigDecimalScalability.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | import org.openjdk.jmh.annotations.Warmup; 19 | 20 | import java.math.BigDecimal; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static ch.randelshofer.fastdoubleparser.Strings.repeat; 24 | 25 | /** 26 | * Benchmarks for selected floating point strings. 27 | *
28 |  * # JMH version: 1.35
29 |  * # VM version: JDK 20-ea, OpenJDK 64-Bit Server VM, 20-ea+27-2213
30 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
31 |  *
32 |  * Benchmark    Mode     Cnt                    Score   Error   Units
33 |  * m         24  avgt       2                    116.786          ns/op
34 |  * m          1  avgt       2                     12.473          ns/op
35 |  * m         10  avgt       2                     20.776          ns/op
36 |  * m        100  avgt       2                    480.817          ns/op
37 |  * m       1000  avgt       2                  14832.646          ns/op
38 |  * m      10000  avgt       2                1200632.862          ns/op
39 |  * m     100000  avgt       2              117587913.424          ns/op
40 |  * m    1000000  avgt       2            12027995151.000          ns/op
41 |  * m   10000000  avgt       1          1281686999490.000          ns/op
42 |  * m  100000000  avgt       ?    128_168_699_949_000.000          ns/op
43 |  * m  646391315  avgt       ?  5_355_166_821_464_850.000          ns/op
44 |  * 
45 | */ 46 | 47 | @Fork(value = 1, jvmArgsAppend = { 48 | "-Xmx16g" 49 | }) 50 | @Measurement(iterations = 2) 51 | @Warmup(iterations = 2) 52 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 53 | @BenchmarkMode(Mode.AverageTime) 54 | @State(Scope.Benchmark) 55 | public class JmhBigDecimalScalability { 56 | 57 | 58 | @Param({ 59 | // "24" 60 | // , "1" 61 | // , "10" 62 | // , "100" 63 | // , "1000" 64 | // , "10000" 65 | // , "100000" 66 | // , "1000000" 67 | // , "10000000" 68 | "100000000" 69 | , "646391315"// The maximal number non-zero digits in the significand 70 | 71 | }) 72 | public int digits; 73 | private String str; 74 | 75 | @Setup(Level.Trial) 76 | public void setUp() { 77 | str = repeat('7', digits); 78 | } 79 | 80 | @Benchmark 81 | public BigDecimal m() { 82 | return new BigDecimal(str); 83 | } 84 | } 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhBigIntegerScalability.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhBigIntegerScalability.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | import org.openjdk.jmh.annotations.Warmup; 19 | 20 | import java.math.BigInteger; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static ch.randelshofer.fastdoubleparser.Strings.repeat; 24 | 25 | /** 26 | * Benchmarks for selected floating point strings. 27 | *
28 |  * # JMH version: 1.35
29 |  * # VM version: OpenJDK 64-Bit Server VM, Oracle Corporation, 20-ea+22-1594
30 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
31 |  *
32 |  *    (digits)  Mode  Cnt            Score   Error  Units
33 |  * m         1  avgt    2           23.866          ns/op
34 |  * m        10  avgt    2           64.853          ns/op
35 |  * m       100  avgt    2          505.986          ns/op
36 |  * m      1000  avgt    2        15322.876          ns/op
37 |  * m     10000  avgt    2      1211620.965          ns/op
38 |  * m    100000  avgt    2    117030135.430          ns/op
39 |  * m   1000000  avgt    2  11883795509.500          ns/op
40 |  * 
41 | */ 42 | 43 | @Fork(value = 1) 44 | @Measurement(iterations = 2) 45 | @Warmup(iterations = 2) 46 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 47 | @BenchmarkMode(Mode.AverageTime) 48 | @State(Scope.Benchmark) 49 | public class JmhBigIntegerScalability { 50 | 51 | 52 | @Param({ 53 | "1", 54 | "10", 55 | "100", 56 | "1000", 57 | "10000", 58 | "100000", 59 | "1000000", 60 | "10000000", 61 | "100000000", 62 | "646391315" 63 | }) 64 | public int digits; 65 | private String str; 66 | 67 | @Setup(Level.Trial) 68 | public void setUp() { 69 | str = repeat("8808065258", (digits + 9) / 10).substring(0, digits); 70 | } 71 | 72 | @Benchmark 73 | public BigInteger m() { 74 | return new BigInteger(str); 75 | } 76 | } 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhComplex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhComplex.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | 19 | import java.util.Random; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * Benchmarks for selected integer strings. 24 | *
 25 |  * # JMH version: 1.35
 26 |  * # VM version: JDK 20-ea, OpenJDK 64-Bit Server VM, 20-ea+29-2280
 27 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
 28 |  *
 29 |  * Benchmark           Mode  Cnt  Score   Error  Units
 30 |  * JmhComplex.mul      avgt    4  1.080 ± 0.117  ns/op
 31 |  * JmhComplex.mulFma1  avgt    4  0.995 ± 0.042  ns/op
 32 |  * JmhComplex.mulFma2  avgt    4  0.997 ± 0.028  ns/op
 33 |  * 
34 | */ 35 | @Fork(value = 1, jvmArgsAppend = { 36 | "-XX:+UnlockExperimentalVMOptions", "--add-modules", "jdk.incubator.vector" 37 | , "--enable-preview" 38 | , "-Xmx24g" 39 | // , "--add-opens", "java.base/java.math=ALL-UNNAMED" 40 | 41 | // Options for analysis with https://github.com/AdoptOpenJDK/jitwatch 42 | //, "-XX:+UnlockDiagnosticVMOptions" 43 | //, "-Xlog:class+load=info" 44 | //, "-XX:+LogCompilation" 45 | //, "-XX:+PrintAssembly" 46 | 47 | }) 48 | @Measurement(iterations = 4) 49 | @Warmup(iterations = 2) 50 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 51 | @BenchmarkMode(Mode.AverageTime) 52 | @State(Scope.Benchmark) 53 | public class JmhComplex { 54 | 55 | 56 | private double ar, ai; 57 | private double br, bi; 58 | 59 | private double rr, ri; 60 | 61 | @Setup(Level.Trial) 62 | public void setUp() { 63 | Random rng = new Random(); 64 | ar = rng.nextDouble(); 65 | ai = rng.nextDouble(); 66 | br = rng.nextDouble(); 67 | bi = rng.nextDouble(); 68 | } 69 | 70 | 71 | @Benchmark 72 | public void mul() { 73 | double real = ar; 74 | double imag = ai; 75 | rr = real * br - imag * bi; 76 | ri = real * bi + imag * br; 77 | } 78 | 79 | @Benchmark 80 | public void mulFma1() { 81 | double real = ar; 82 | double imag = ai; 83 | rr = real * br - imag * bi; 84 | ri = Math.fma(real, bi, imag * br); 85 | } 86 | 87 | @Benchmark 88 | public void mulFma2() { 89 | double real = ar; 90 | double imag = ai; 91 | rr = Math.fma(real, br, -imag * bi); 92 | ri = Math.fma(real, bi, imag * br); 93 | } 94 | 95 | } 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhDoubleScalability.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhDoubleScalability.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | import org.openjdk.jmh.annotations.Warmup; 19 | 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import static ch.randelshofer.fastdoubleparser.Strings.repeat; 23 | 24 | /** 25 | * Benchmarks for selected floating point strings. 26 | *
27 |  * # JMH version: 1.35
28 |  * # VM version: JDK 20-ea, OpenJDK 64-Bit Server VM, 20-ea+27-2213
29 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
30 |  *
31 |  *      (digits)  Mode  Cnt           Score   Error  Units
32 |  * m           1  avgt    2          13.594          ns/op
33 |  * m          10  avgt    2          22.188          ns/op
34 |  * m         100  avgt    2         276.957          ns/op
35 |  * m        1000  avgt    2         440.983          ns/op
36 |  * m       10000  avgt    2        4426.218          ns/op
37 |  * m      100000  avgt    2       47251.366          ns/op
38 |  * m     1000000  avgt    2      469427.913          ns/op
39 |  * m    10000000  avgt    2     4821577.118          ns/op
40 |  * m   100000000  avgt    2    48427968.251          ns/op
41 |  * m  1000000000  avgt    2   490691954.952          ns/op
42 |  * m  2147483643  avgt    2  1040876939.400          ns/op
43 |  * 
44 | */ 45 | 46 | @Fork(value = 1, jvmArgsAppend = { 47 | "-XX:+UnlockExperimentalVMOptions", "--add-modules", "jdk.incubator.vector" 48 | , "--enable-preview" 49 | , "-Xmx16g" 50 | 51 | // ,"-XX:+UnlockDiagnosticVMOptions", "-XX:PrintAssemblyOptions=intel", "-XX:CompileCommand=print,ch/randelshofer/fastdoubleparser/FastDoubleParser.*" 52 | 53 | }) 54 | @Measurement(iterations = 2) 55 | @Warmup(iterations = 2) 56 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 57 | @BenchmarkMode(Mode.AverageTime) 58 | @State(Scope.Benchmark) 59 | public class JmhDoubleScalability { 60 | 61 | 62 | @Param({ 63 | "24" // Double.toString() never produces more than 24 characters 64 | , "1" 65 | , "10" 66 | , "100" 67 | , "1000" 68 | , "10000" 69 | , "100000" 70 | , "1000000" 71 | , "10000000" 72 | , "100000000" 73 | , "1000000000" 74 | , "2147483643" // Integer.MAX_VALUE - 4 = the largest support array size 75 | }) 76 | public int digits; 77 | private String str; 78 | 79 | @Setup(Level.Trial) 80 | public void setUp() { 81 | str = repeat('7', digits); 82 | } 83 | 84 | @Benchmark 85 | public double m() { 86 | return Double.valueOf(str); 87 | } 88 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhFastDoubleMath.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhFastDoubleMath.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | 8 | import org.openjdk.jmh.annotations.Benchmark; 9 | import org.openjdk.jmh.annotations.BenchmarkMode; 10 | import org.openjdk.jmh.annotations.Fork; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | 19 | import java.util.Random; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | *
24 |  * # JMH version: 1.36
25 |  * # VM version: JDK 20.0.1, OpenJDK 64-Bit Server VM, 20.0.1+9-29
26 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz SIMD-256
27 |  *
28 |  * Benchmark                                       Mode  Cnt  Score   Error  Units
29 |  * JmhFastDoubleMath.tryHexFloatToDoubleTruncated  avgt    4  2.939 ± 0.427  ns/op
30 |  */
31 | @Fork(value = 1, jvmArgsAppend = {"-ea"})
32 | @Measurement(iterations = 4)
33 | @Warmup(iterations = 2)
34 | @OutputTimeUnit(TimeUnit.NANOSECONDS)
35 | @BenchmarkMode(Mode.AverageTime)
36 | @State(Scope.Benchmark)
37 | public class JmhFastDoubleMath {
38 |     private boolean negative;
39 |     private long significand;
40 |     private int exponent;
41 | 
42 |     @Setup
43 |     public void prepare() {
44 |         Random rng = new Random();
45 |         negative = rng.nextBoolean();
46 |         significand = rng.nextLong();
47 |         exponent = rng.nextInt(Double.MIN_EXPONENT, Double.MAX_EXPONENT);
48 |     }
49 | 
50 |     @Benchmark
51 |     public double tryHexFloatToDoubleTruncated() {
52 |         double v = FastDoubleMath.tryHexFloatToDoubleTruncated(negative, significand, exponent, false, 0);
53 |         assert !Double.isNaN(v);
54 |         return v;
55 |     }
56 | }
57 | 


--------------------------------------------------------------------------------
/fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhFastFloatMath.java:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * @(#)JmhFastFloatMath.java
 3 |  * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License.
 4 |  */
 5 | package ch.randelshofer.fastdoubleparser;
 6 | 
 7 | 
 8 | import org.openjdk.jmh.annotations.Benchmark;
 9 | import org.openjdk.jmh.annotations.BenchmarkMode;
10 | import org.openjdk.jmh.annotations.Fork;
11 | import org.openjdk.jmh.annotations.Measurement;
12 | import org.openjdk.jmh.annotations.Mode;
13 | import org.openjdk.jmh.annotations.OutputTimeUnit;
14 | import org.openjdk.jmh.annotations.Scope;
15 | import org.openjdk.jmh.annotations.Setup;
16 | import org.openjdk.jmh.annotations.State;
17 | import org.openjdk.jmh.annotations.Warmup;
18 | 
19 | import java.util.Random;
20 | import java.util.concurrent.TimeUnit;
21 | 
22 | /**
23 |  * 
24 |  * # JMH version: 1.36
25 |  * # VM version: JDK 20.0.1, OpenJDK 64-Bit Server VM, 20.0.1+9-29
26 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz SIMD-256
27 |  *
28 |  * Benchmark                                     Mode  Cnt  Score   Error  Units
29 |  * JmhFastFloatMath.tryHexFloatToFloatTruncated  avgt    4  2.531 ± 0.009  ns/op
30 |  */
31 | @Fork(value = 1, jvmArgsAppend = {"-ea"})
32 | @Measurement(iterations = 4)
33 | @Warmup(iterations = 2)
34 | @OutputTimeUnit(TimeUnit.NANOSECONDS)
35 | @BenchmarkMode(Mode.AverageTime)
36 | @State(Scope.Benchmark)
37 | public class JmhFastFloatMath {
38 |     private boolean negative;
39 |     private long significand;
40 |     private int exponent;
41 | 
42 |     @Setup
43 |     public void prepare() {
44 |         Random rng = new Random();
45 |         negative = rng.nextBoolean();
46 |         significand = rng.nextLong();
47 |         exponent = rng.nextInt(Float.MIN_EXPONENT, Float.MAX_EXPONENT);
48 |     }
49 | 
50 |     @Benchmark
51 |     public float tryHexFloatToFloatTruncated() {
52 |         float v = FastFloatMath.tryHexFloatToFloatTruncated(negative, significand, exponent, false, 0);
53 |         assert !Float.isNaN(v);
54 |         return v;
55 |     }
56 | }
57 | 


--------------------------------------------------------------------------------
/fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhFloat.java:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * @(#)JmhFloat.java
 3 |  * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License.
 4 |  */
 5 | package ch.randelshofer.fastdoubleparser;
 6 | 
 7 | import org.openjdk.jmh.annotations.Benchmark;
 8 | import org.openjdk.jmh.annotations.BenchmarkMode;
 9 | import org.openjdk.jmh.annotations.Fork;
10 | import org.openjdk.jmh.annotations.Measurement;
11 | import org.openjdk.jmh.annotations.Mode;
12 | import org.openjdk.jmh.annotations.OutputTimeUnit;
13 | import org.openjdk.jmh.annotations.Param;
14 | import org.openjdk.jmh.annotations.Scope;
15 | import org.openjdk.jmh.annotations.State;
16 | import org.openjdk.jmh.annotations.Warmup;
17 | 
18 | import java.util.concurrent.TimeUnit;
19 | 
20 | /**
21 |  * Benchmarks for selected floating point strings.
22 |  * 
23 |  * # JMH version: 1.28
24 |  * # VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
25 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
26 |  *
27 |  * Benchmark    (str)  Mode  Cnt    Score   Error  Units
28 |  * m                0  avgt    2    9.607          ns/op
29 |  * m              365  avgt    2   15.991          ns/op
30 |  * m             10.1  avgt    2   18.379          ns/op
31 |  * m        3.1415927  avgt    2   77.434          ns/op
32 |  * m    1.6162552E-35  avgt    2   89.729          ns/op
33 |  * m  0x1.57bd4ep-116  avgt    2  290.443          ns/op
34 |  * 
35 | */ 36 | 37 | @Fork(value = 1) 38 | @Measurement(iterations = 2) 39 | @Warmup(iterations = 2) 40 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 41 | @BenchmarkMode(Mode.AverageTime) 42 | @State(Scope.Benchmark) 43 | public class JmhFloat { 44 | 45 | 46 | @Param({ 47 | "0", 48 | "365", 49 | "10.1", 50 | "3.1415927", 51 | "1.6162552E-35", 52 | "0x1.57bd4ep-116" 53 | }) 54 | public String str; 55 | 56 | @Benchmark 57 | public float m() { 58 | return Float.parseFloat(str); 59 | } 60 | } 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhJavaBigDecimalFromByteArrayScalability.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhJavaBigDecimalFromByteArrayScalability.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | import org.openjdk.jmh.annotations.Warmup; 19 | 20 | import java.math.BigDecimal; 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | import static ch.randelshofer.fastdoubleparser.Strings.repeatStringBuilder; 25 | 26 | /** 27 | * Benchmarks for selected floating point strings. 28 | *
29 |  * # JMH version: 1.37
30 |  * # VM version: JDK 21-ea, OpenJDK 64-Bit Server VM, 21-ea+20-1677
31 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
32 |  *
33 |  *     (digits)  Mode  Cnt            Score   Error  Units
34 |  * m          1  avgt                10.688          ns/op
35 |  * m         10  avgt                19.903          ns/op
36 |  * m        100  avgt               551.403          ns/op
37 |  * m       1000  avgt              5999.290          ns/op
38 |  * m      10000  avgt            221929.429          ns/op
39 |  * m     100000  avgt           5881864.021          ns/op
40 |  * m    1000000  avgt         110878102.879          ns/op
41 |  * m   10000000  avgt        1649224533.143          ns/op
42 |  * m  100000000  avgt       25932859264.000          ns/op
43 |  * 
44 | */ 45 | 46 | @Fork(value = 1, jvmArgsAppend = { 47 | "-XX:+UnlockExperimentalVMOptions", "--add-modules", "jdk.incubator.vector" 48 | , "--enable-preview" 49 | , "-Xmx24g" 50 | 51 | // ,"-XX:+UnlockDiagnosticVMOptions", "-XX:PrintAssemblyOptions=intel", "-XX:CompileCommand=print,ch/randelshofer/fastdoubleparser/JavaBigDecimalParser.*" 52 | 53 | }) 54 | @Measurement(iterations = 1) 55 | @Warmup(iterations = 1) 56 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 57 | @BenchmarkMode(Mode.AverageTime) 58 | @State(Scope.Benchmark) 59 | public class JmhJavaBigDecimalFromByteArrayScalability { 60 | @Param({ 61 | "1" 62 | , "10" 63 | , "100" 64 | , "1000" 65 | , "10000" 66 | , "100000" 67 | , "1000000" 68 | , "10000000" 69 | , "100000000" 70 | // , "646391315"// The maximal number of non-zero digits in the significand 71 | }) 72 | 73 | public int digits; 74 | private byte[] input; 75 | 76 | @Setup(Level.Trial) 77 | public void setUp() { 78 | StringBuilder str = repeatStringBuilder("9", digits + 1); 79 | str.setCharAt(digits / 3, '.'); 80 | input = str.toString().getBytes(StandardCharsets.UTF_8); 81 | } 82 | 83 | 84 | @Benchmark 85 | public BigDecimal m() { 86 | return JavaBigDecimalParser.parseBigDecimal(input); 87 | } 88 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhJavaBigDecimalFromCharSequenceScalability.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhJavaBigDecimalFromCharSequenceScalability.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | import org.openjdk.jmh.annotations.Warmup; 19 | 20 | import java.math.BigDecimal; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static ch.randelshofer.fastdoubleparser.Strings.repeatStringBuilder; 24 | 25 | /** 26 | * Benchmarks for selected floating point strings. 27 | *
28 |  * # JMH version: 1.37
29 |  * # VM version: JDK 21.0.1, OpenJDK 64-Bit Server VM, 21.0.1+12-LTS
30 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
31 |  *
32 |  *     (digits)  Mode  Cnt            Score   Error  Units
33 |  * m          1  avgt                11.760          ns/op
34 |  * m         10  avgt                24.240          ns/op
35 |  * m        100  avgt               641.794          ns/op
36 |  * m       1000  avgt              6619.499          ns/op
37 |  * m      10000  avgt            220716.603          ns/op
38 |  * m     100000  avgt           5959907.182          ns/op
39 |  * m    1000000  avgt         112629492.933          ns/op
40 |  * m   10000000  avgt        1719489749.000          ns/op
41 |  * m  100000000  avgt       28698919868.000          ns/op
42 |  * 
43 | */ 44 | 45 | @Fork(value = 1, jvmArgsAppend = { 46 | "-XX:+UnlockExperimentalVMOptions", "--add-modules", "jdk.incubator.vector" 47 | , "--enable-preview" 48 | //, "-XX:+UnlockDiagnosticVMOptions" 49 | // "-XX:PrintAssemblyOptions=intel", "-XX:CompileCommand=print,ch/randelshofer/fastdoubleparser/*.*" 50 | //, "-XX:-PrintInlining" 51 | 52 | }) 53 | @Measurement(iterations = 1) 54 | @Warmup(iterations = 1) 55 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 56 | @BenchmarkMode(Mode.AverageTime) 57 | @State(Scope.Benchmark) 58 | public class JmhJavaBigDecimalFromCharSequenceScalability { 59 | 60 | 61 | @Param({ 62 | "1" 63 | , "10" 64 | , "100" 65 | , "1000" 66 | , "10000" 67 | , "100000" 68 | , "1000000" 69 | , "10000000" 70 | , "100000000" 71 | // , "646391315"// The maximal number of non-zero digits in the significand 72 | }) 73 | public int digits; 74 | private String input; 75 | 76 | @Setup(Level.Trial) 77 | public void setUp() { 78 | StringBuilder str = repeatStringBuilder("9", digits + 1); 79 | str.setCharAt(digits / 3, '.'); 80 | input = str.toString(); 81 | } 82 | 83 | @Benchmark 84 | public BigDecimal m() { 85 | return JavaBigDecimalParser.parseBigDecimal(input); 86 | } 87 | 88 | } 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhJavaDoubleFromByteArrayEmpirical.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhJavaDoubleFromByteArrayEmpirical.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Measurement; 11 | import org.openjdk.jmh.annotations.Mode; 12 | import org.openjdk.jmh.annotations.OutputTimeUnit; 13 | import org.openjdk.jmh.annotations.Param; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | 19 | import java.nio.charset.StandardCharsets; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * Benchmarks for selected floating point strings. 24 | *
25 |  * # JMH version: 1.36
26 |  * # VM version: JDK 20, OpenJDK 64-Bit Server VM, 20+36-2344
27 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
28 |  *                       (str)  Mode  Cnt   Score   Error  Units
29 |  * m                         0  avgt    4   4.057 ± 0.126  ns/op
30 |  * m                       365  avgt    4   9.017 ± 0.093  ns/op
31 |  * m                      10.1  avgt    4  12.221 ± 1.717  ns/op
32 |  * m  -1.2345678901234568E-121  avgt    4  27.247 ± 0.221  ns/op
33 |  * m      -0.29235596393453456  avgt    4  19.884 ± 0.300  ns/op
34 |  * m     0x123.456789abcdep123  avgt    4  22.941 ± 0.275  ns/op
35 |  * 
36 | */ 37 | @Fork(value = 1, jvmArgsAppend = { 38 | "-XX:+UnlockExperimentalVMOptions", "--add-modules", "jdk.incubator.vector", 39 | "--enable-preview", 40 | 41 | // Options for analysis with https://github.com/AdoptOpenJDK/jitwatch 42 | //"-XX:+UnlockDiagnosticVMOptions", 43 | //"-Xlog:class+load=info", 44 | //"-XX:+LogCompilation", 45 | //"-XX:+PrintAssembly" 46 | }) 47 | @Measurement(iterations = 4) 48 | @Warmup(iterations = 2) 49 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 50 | @BenchmarkMode(Mode.AverageTime) 51 | @State(Scope.Benchmark) 52 | public class JmhJavaDoubleFromByteArrayEmpirical { 53 | @Param({ 54 | "0" 55 | , "365" 56 | , "10.1" 57 | , "-1.2345678901234568E-121" 58 | , "-0.29235596393453456" 59 | , "0x123.456789abcdep123" 60 | }) 61 | public String str; 62 | private byte[] byteArray; 63 | 64 | @Setup 65 | public void prepare() { 66 | byteArray = str.getBytes(StandardCharsets.ISO_8859_1); 67 | } 68 | 69 | @Benchmark 70 | public double m() { 71 | return JavaDoubleParser.parseDouble(byteArray); 72 | } 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhJavaDoubleFromCharSequenceEmpirical.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhJavaDoubleFromCharSequenceEmpirical.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Measurement; 11 | import org.openjdk.jmh.annotations.Mode; 12 | import org.openjdk.jmh.annotations.OutputTimeUnit; 13 | import org.openjdk.jmh.annotations.Param; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.State; 16 | import org.openjdk.jmh.annotations.Warmup; 17 | 18 | import java.util.concurrent.TimeUnit; 19 | 20 | /** 21 | * Benchmarks for selected floating point strings. 22 | *
23 |  * # JMH version: 1.35
24 |  * # VM version: JDK 20-ea, OpenJDK 64-Bit Server VM, 20-ea+27-2213
25 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
26 |  * 
27 | * (str) Mode Cnt Score Error Units 28 | * m 0 avgt 2 5.131 ns/op 29 | * m 365 avgt 2 12.730 ns/op 30 | * m 10.1 avgt 2 14.839 ns/op 31 | * m -1.2345678901234568E-121 avgt 2 34.010 ns/op 32 | * m -0.29235596393453456 avgt 2 25.021 ns/op 33 | * m -0x123.456789abcdep-123 avgt 2 28.997 ns/op 34 | * m 95773983001708984375E-308 avgt 2 9455.148 ns/op 35 | *
36 | */ 37 | @Fork(value = 1, jvmArgsAppend = {"-XX:+UnlockExperimentalVMOptions", "--add-modules", "jdk.incubator.vector" 38 | , "--enable-preview", 39 | 40 | // Options for analysis with https://github.com/AdoptOpenJDK/jitwatch 41 | //"-XX:+UnlockDiagnosticVMOptions", 42 | //"-Xlog:class+load=info", 43 | //"-XX:+LogCompilation", 44 | //"-XX:+PrintAssembly" 45 | }) 46 | @Measurement(iterations = 2) 47 | @Warmup(iterations = 2) 48 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 49 | @BenchmarkMode(Mode.AverageTime) 50 | @State(Scope.Benchmark) 51 | public class JmhJavaDoubleFromCharSequenceEmpirical { 52 | @Param({ 53 | "0" 54 | , "365" 55 | , "10.1" 56 | , "-1.2345678901234568E-121" 57 | , "-0.29235596393453456" 58 | , "-0x123.456789abcdep-123" 59 | , "2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375E-308" 60 | 61 | 62 | }) 63 | public String str; 64 | 65 | @Benchmark 66 | public double m() { 67 | return JavaDoubleParser.parseDouble(str); 68 | } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhJavaFloatFromCharSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhJavaFloatFromCharSequence.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Measurement; 11 | import org.openjdk.jmh.annotations.Mode; 12 | import org.openjdk.jmh.annotations.OutputTimeUnit; 13 | import org.openjdk.jmh.annotations.Param; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * Benchmarks for selected floating point strings. 23 | *
24 |  * # JMH version: 1.28
25 |  * # VM version: JDK 16, OpenJDK 64-Bit Server VM, 16+36-2231
26 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
27 |  *
28 |  * Benchmark    (str)  Mode  Cnt    Score   Error  Units
29 |  * m                0  avgt    2    6.570          ns/op
30 |  * m              365  avgt    2   14.525          ns/op
31 |  * m             10.1  avgt    2   16.646          ns/op
32 |  * m        3.1415927  avgt    2   24.022          ns/op
33 |  * m    1.6162552E-35  avgt    2   28.465          ns/op
34 |  * m  0x1.57bd4ep-116  avgt    2  336.391          ns/op
35 |  * 
36 | */ 37 | 38 | @Fork(value = 1) 39 | @Measurement(iterations = 2) 40 | @Warmup(iterations = 2) 41 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 42 | @BenchmarkMode(Mode.AverageTime) 43 | @State(Scope.Benchmark) 44 | public class JmhJavaFloatFromCharSequence { 45 | 46 | 47 | @Param({ 48 | "0", 49 | "365", 50 | "10.1", 51 | "3.14159267", 52 | "1.6162552E-35", 53 | "0x1.57bd4ep-116" 54 | }) 55 | public String str; 56 | 57 | @Setup 58 | public void setup() { 59 | System.out.println(str + "=" + JavaFloatParser.parseFloat(str)); 60 | } 61 | 62 | @Benchmark 63 | public float m() { 64 | return JavaFloatParser.parseFloat(str); 65 | } 66 | } 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhJsonDoubleFromByteArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhJsonDoubleFromByteArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Measurement; 11 | import org.openjdk.jmh.annotations.Mode; 12 | import org.openjdk.jmh.annotations.OutputTimeUnit; 13 | import org.openjdk.jmh.annotations.Param; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | 19 | import java.nio.charset.StandardCharsets; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * Benchmarks for selected floating point strings. 24 | *
25 |  * # JMH version: 1.35
26 |  * # VM version: JDK 19-ea, OpenJDK 64-Bit Server VM, 20-ea+22-1594
27 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
28 |  *
29 |  *                        (str)  Mode  Cnt   Score   Error  Units
30 |  * m                         0  avgt    5   4.719 ± 1.005  ns/op
31 |  * m                       365  avgt    5  11.517 ± 0.119  ns/op
32 |  * m                      10.1  avgt    5  13.060 ± 0.101  ns/op
33 |  * m    123.45678901234567e123  avgt    5  28.984 ± 0.207  ns/op
34 |  * m      123.4567890123456789  avgt    5  24.307 ± 0.358  ns/op
35 |  * m  123.4567890123456789e123  avgt    5  31.024 ± 0.190  ns/op
36 |  * m      -0.29235596393453456  avgt    5  20.588 ± 0.077  ns/op
37 |  * 
38 | */ 39 | @Fork(value = 1, jvmArgsAppend = { 40 | "-XX:+UnlockExperimentalVMOptions", "--add-modules", "jdk.incubator.vector" 41 | , "--enable-preview" 42 | , "-XX:+UnlockDiagnosticVMOptions" 43 | // "-XX:PrintAssemblyOptions=intel", "-XX:CompileCommand=print,ch/randelshofer/fastdoubleparser/*.*" 44 | //,"-XX:+PrintInlining" 45 | 46 | }) 47 | @Measurement(iterations = 5) 48 | @Warmup(iterations = 2) 49 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 50 | @BenchmarkMode(Mode.AverageTime) 51 | @State(Scope.Benchmark) 52 | public class JmhJsonDoubleFromByteArray { 53 | @Param({ 54 | "0" 55 | , "365" 56 | , "10.1" 57 | , "123.45678901234567e123" 58 | , "123.4567890123456789" 59 | , "123.4567890123456789e123" 60 | , "-0.29235596393453456" 61 | }) 62 | public String str; 63 | private byte[] byteArray; 64 | 65 | @Setup 66 | public void prepare() { 67 | byteArray = str.getBytes(StandardCharsets.ISO_8859_1); 68 | } 69 | 70 | @Benchmark 71 | public double m() { 72 | return JsonDoubleParser.parseDouble(byteArray); 73 | } 74 | } 75 | 76 | 77 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhJsonDoubleFromCharArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhJsonDoubleFromCharArray.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Measurement; 11 | import org.openjdk.jmh.annotations.Mode; 12 | import org.openjdk.jmh.annotations.OutputTimeUnit; 13 | import org.openjdk.jmh.annotations.Param; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * Benchmarks for selected floating point strings. 23 | *
24 |  * # JMH version: 1.35
25 |  * # VM version: JDK 20-ea, OpenJDK 64-Bit Server VM, 20-ea+27-2213
26 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
27 |  *
28 |  *                       (str)  Mode  Cnt   Score   Error  Units
29 |  * m                         0  avgt    5   3.440 ± 0.113  ns/op
30 |  * m                       365  avgt    5  10.405 ± 0.162  ns/op
31 |  * m                      10.1  avgt    5  12.453 ± 1.461  ns/op
32 |  * m  -1.2345678901234568E-121  avgt    5  26.581 ± 0.256  ns/op
33 |  * m      -0.29235596393453456  avgt    5  18.163 ± 0.140  ns/op
34 |  * 
35 | */ 36 | @Fork(value = 1, jvmArgsAppend = { 37 | "--enable-preview", 38 | 39 | // Options for analysis with https://github.com/AdoptOpenJDK/jitwatch 40 | // "-XX:+UnlockDiagnosticVMOptions", 41 | // "-Xlog:class+load=info", 42 | // "-XX:+LogCompilation", 43 | // "-XX:+PrintAssembly" 44 | 45 | }) 46 | @Measurement(iterations = 5) 47 | @Warmup(iterations = 2) 48 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 49 | @BenchmarkMode(Mode.AverageTime) 50 | @State(Scope.Benchmark) 51 | public class JmhJsonDoubleFromCharArray { 52 | @Param({ 53 | "0" 54 | , "365" 55 | , "10.1" 56 | , "-1.2345678901234568E-121" 57 | , "-0.29235596393453456" 58 | }) 59 | public String str; 60 | private char[] charArray; 61 | 62 | @Setup 63 | public void prepare() { 64 | charArray = str.toCharArray(); 65 | } 66 | 67 | @Benchmark 68 | public double m() { 69 | return JsonDoubleParser.parseDouble(charArray); 70 | } 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/JmhSplitFloor16.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)JmhSplitFloor16.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Measurement; 11 | import org.openjdk.jmh.annotations.Mode; 12 | import org.openjdk.jmh.annotations.OperationsPerInvocation; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Warmup; 15 | import org.openjdk.jmh.infra.Blackhole; 16 | 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | *
21 |  * # JMH version: 1.37
22 |  * # VM version: JDK 22-ea, OpenJDK 64-Bit Server VM, 22-ea+34-2360
23 |  * # Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
24 |  *
25 |  * Benchmark                        Mode  Cnt   Score   Error  Units
26 |  * JmhSplitFloor16.oldSplitFloor16  avgt    2  58.752          ns/op
27 |  * JmhSplitFloor16.splitFloor16     avgt    2  42.323          ns/op
28 |  * 
29 | */ 30 | @Fork(value = 1) 31 | @Measurement(iterations = 2, time = 1) 32 | @Warmup(iterations = 2, time = 1) 33 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 34 | @BenchmarkMode(Mode.AverageTime) 35 | public class JmhSplitFloor16 { 36 | private static final int TO_MAX = 1024 + 1; 37 | private static final int FROM_MAX = 100; 38 | private static final int DATA_LENGTH = TO_MAX - FROM_MAX; 39 | 40 | @Benchmark 41 | @OperationsPerInvocation(DATA_LENGTH) 42 | public void oldSplitFloor16(Blackhole blackhole) { 43 | for (int i = 0; i < FROM_MAX; i++) { 44 | for (int j = i; j < TO_MAX; j++) { 45 | blackhole.consume(oldSplitFloor16(i, j)); 46 | } 47 | } 48 | } 49 | 50 | @Benchmark 51 | @OperationsPerInvocation(DATA_LENGTH) 52 | public void splitFloor16(Blackhole blackhole) { 53 | for (int i = 0; i < FROM_MAX; i++) { 54 | for (int j = i; j < TO_MAX; j++) { 55 | blackhole.consume(splitFloor16(i, j)); 56 | } 57 | } 58 | } 59 | 60 | static int oldSplitFloor16(int from, int to) { 61 | int mid = (from + to) >>> 1;// split in half 62 | mid = to - (((to - mid + 15) >> 4) << 4);// make numDigits of low a multiple of 16 63 | return mid; 64 | } 65 | 66 | static int splitFloor16(int from, int to) { 67 | // divide length by 2 as we want the middle, round up range half to multiples of 16 68 | int range = (((to - from + 31) >>> 5) << 4); 69 | return to - range; 70 | } 71 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/MiniTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)MiniTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | public final class MiniTest { 8 | public static void main(String... args) { 9 | //ConfigurableDoubleParser p = new ConfigurableDoubleParser(); 10 | //double v=p.parseDouble("7.3177701707893310e+15");//Outside Clinger fast path, bail-out in semi-fast path, 7.3177701707893310e+15 11 | //System.out.println(v); 12 | /* 13 | JmhUseBigDecimalForSlowPath jmh=new JmhUseBigDecimalForSlowPath(); 14 | jmh.str="7.3177701707893310e15"; 15 | jmh.setup(); 16 | System.out.println(jmh.bigDecimal); 17 | 18 | */ 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/NumberTestDataSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)NumberTestDataSupplier.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.util.function.Supplier; 8 | 9 | public record NumberTestDataSupplier(String title, 10 | Supplier supplier) { 11 | public NumberTestDataSupplier(String inputValue) { 12 | this(inputValue, () -> new NumberTestData(inputValue)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/Strings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)Strings.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Arrays; 9 | 10 | public final class Strings { 11 | public static StringBuilder repeatStringBuilder(String str, int count) { 12 | StringBuilder b = new StringBuilder(str.length() * count); 13 | while (count-- > 0) { 14 | b.append(str); 15 | } 16 | return b; 17 | } 18 | 19 | public static String repeat(String str, int count) { 20 | return repeatStringBuilder(str, count).toString(); 21 | } 22 | 23 | public static String repeat(char ch, int count) { 24 | if (ch < 256) { 25 | byte[] chars = new byte[count]; 26 | Arrays.fill(chars, (byte) ch); 27 | return new String(chars, StandardCharsets.ISO_8859_1); 28 | } 29 | char[] chars = new char[count]; 30 | Arrays.fill(chars, ch); 31 | return String.copyValueOf(chars); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/Utf8DecoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)Utf8DecoderTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.ValueSource; 9 | 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.Arrays; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static org.junit.jupiter.api.Assertions.assertThrows; 16 | 17 | public final class Utf8DecoderTest { 18 | 19 | @SuppressWarnings("UnnecessaryUnicodeEscape") 20 | @ParameterizedTest 21 | @ValueSource(strings = { 22 | "lowerbounds\u0000\u0080\u0800\ud800\udc00", 23 | "upperbounds\u007f\u07ff\uffff\udbff\udfff"}) 24 | public void shouldDecode(String str) { 25 | byte[] bytes = str.getBytes(StandardCharsets.UTF_8); 26 | Utf8Decoder.Result actual = Utf8Decoder.decode(bytes, 0, bytes.length); 27 | char[] expected = str.toCharArray(); 28 | 29 | String actualStr = new String(actual.chars(), 0, actual.length()); 30 | assertEquals(str, actualStr); 31 | 32 | assertEquals(expected.length, actual.length()); 33 | assertArrayEquals(expected, Arrays.copyOf(actual.chars(), actual.length())); 34 | } 35 | 36 | @SuppressWarnings("UnnecessaryUnicodeEscape") 37 | @ParameterizedTest 38 | @ValueSource(strings = { 39 | "80", 40 | "c0", 41 | "c0 80", 42 | "e0 80", 43 | "e0 80 80", 44 | "e0 80 e0", 45 | "f0 80 80", 46 | "f0 80 80 80", 47 | "f0 80 80 80", 48 | "f0 80 a0 a0", 49 | }) 50 | public void shouldNotDecode(String str) { 51 | String[] hexes = str.split(" "); 52 | byte[] bytes = new byte[hexes.length]; 53 | for (int i = 0; i < hexes.length; i++) { 54 | bytes[i] = (byte) Integer.parseInt(hexes[i], 16); 55 | 56 | } 57 | assertThrows(NumberFormatException.class, () -> Utf8Decoder.decode(bytes, 0, bytes.length)); 58 | } 59 | } -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteToIntMapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharToIntMapTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | 15 | public final class ByteToIntMapTest { 16 | @Test 17 | public void shouldFindDigit() { 18 | List digits = Arrays.asList('h', 'a', 'm', 'b', 'u', 'r', 'g', 'e', 'f', 'o'); 19 | ByteToIntMap charMap = new ByteToIntMap(digits.size()); 20 | for (int i = 0, digitsSize = digits.size(); i < digitsSize; i++) { 21 | charMap.put((byte) (char) digits.get(i), i); 22 | } 23 | 24 | for (int i = 0, digitsSize = digits.size(); i < digitsSize; i++) { 25 | byte d = (byte) (char) digits.get(i); 26 | int actual = charMap.getOrDefault(d, 10); 27 | assertEquals(i, actual, "for d=" + d); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteTrieOfFewIgnoreCaseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharToIntMapTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.util.Arrays; 11 | import java.util.LinkedHashSet; 12 | import java.util.Set; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | 16 | public final class ByteTrieOfFewIgnoreCaseTest { 17 | @Test 18 | public void shouldMatchString() { 19 | Set strings = new LinkedHashSet<>(Arrays.asList("NaN", "інф")); 20 | ByteTrieOfFewIgnoreCase trie = new ByteTrieOfFewIgnoreCase(strings); 21 | 22 | assertEquals(0, trie.match(new byte[]{'a'})); 23 | assertEquals(0, trie.match(new byte[]{'A'})); 24 | 25 | // NAN, nan, nAn 26 | assertEquals(3, trie.match(new byte[]{0x4e, 0x41, 0x4e})); 27 | assertEquals(3, trie.match(new byte[]{0x6e, 0x61, 0x6e})); 28 | assertEquals(3, trie.match(new byte[]{0x6e, 0x41, 0x6e})); 29 | 30 | // "ІНФ", "інф", "іНф" 31 | assertEquals(6, trie.match(new byte[]{(byte) 0xd0, (byte) 0x86, (byte) 0xd0, (byte) 0x9d, (byte) 0xd0, (byte) 0xa4})); 32 | assertEquals(6, trie.match(new byte[]{(byte) 0xd1, (byte) 0x96, (byte) 0xd0, (byte) 0xbd, (byte) 0xd1, (byte) 0x84})); 33 | assertEquals(6, trie.match(new byte[]{(byte) 0xd1, (byte) 0x96, (byte) 0xd0, (byte) 0x9d, (byte) 0xd1, (byte) 0x84})); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/bte/ByteTrieTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.bte; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.util.Arrays; 10 | import java.util.HashSet; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | public final class ByteTrieTest { 15 | @Test 16 | public void shouldAddAndRetrieve() { 17 | ByteTrieOfFew trie = new ByteTrieOfFew(new HashSet<>(Arrays.asList("e", "E", "Exp"))); 18 | assertEquals(0, trie.match(new byte[]{'a'})); 19 | assertEquals(1, trie.match(new byte[]{'e'})); 20 | assertEquals(1, trie.match(new byte[]{'E'})); 21 | assertEquals(3, trie.match(new byte[]{'E', 'x', 'p'})); 22 | assertEquals(3, trie.match(new byte[]{'E', 'x', 'p', 'o', 'n', 'e', 'n', 't'})); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharToIntMapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharToIntMapTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | public final class CharToIntMapTest { 15 | @Test 16 | public void shouldFindDigit() { 17 | List digits = Arrays.asList('〇', '一', '二', '三', '四', '五', '六', '七', '八', '九'); 18 | CharToIntMap charMap = new CharToIntMap(digits.size()); 19 | for (int i = 0, digitsSize = digits.size(); i < digitsSize; i++) { 20 | charMap.put(digits.get(i), i); 21 | } 22 | 23 | for (int i = 0, digitsSize = digits.size(); i < digitsSize; i++) { 24 | char d = digits.get(i); 25 | int actual = charMap.getOrDefault(d, 10); 26 | assertEquals(i, actual, "for d=" + d); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieOfFewIgnoreCaseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieOfFewIgnoreCaseTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.util.Arrays; 11 | import java.util.LinkedHashSet; 12 | import java.util.Set; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | 16 | public final class CharTrieOfFewIgnoreCaseTest { 17 | @Test 18 | public void shouldMatchString() { 19 | Set strings = new LinkedHashSet<>(Arrays.asList("NaN", "інф")); 20 | CharTrieOfFewIgnoreCase trie = new CharTrieOfFewIgnoreCase(strings); 21 | 22 | assertEquals(0, trie.match(new char[]{'a'})); 23 | assertEquals(0, trie.match(new char[]{'A'})); 24 | 25 | // NAN, nan, nAn 26 | assertEquals(3, trie.match(new char[]{'N', 'A', 'N'})); 27 | assertEquals(3, trie.match(new char[]{'n', 'a', 'n'})); 28 | assertEquals(3, trie.match(new char[]{'n', 'A', 'n'})); 29 | 30 | // "ІНФ", "інф", "іНф" 31 | assertEquals(3, trie.match(new char[]{'І', 'Н', 'Ф'})); 32 | assertEquals(3, trie.match(new char[]{'і', 'н', 'ф'})); 33 | assertEquals(3, trie.match(new char[]{'і', 'Н', 'ф'})); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /fastdoubleparser-dev/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/chr/CharTrieTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)CharTrieTest.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser.chr; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.util.Arrays; 10 | import java.util.HashSet; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | public final class CharTrieTest { 15 | @Test 16 | public void shouldAddAndRetrieve() { 17 | CharTrieOfFew trie = new CharTrieOfFew(new HashSet<>(Arrays.asList("e", "E", "Exp"))); 18 | assertEquals(0, trie.match("a")); 19 | assertEquals(1, trie.match("e")); 20 | assertEquals(1, trie.match("E")); 21 | assertEquals(3, trie.match("Exp")); 22 | assertEquals(3, trie.match("Exponent")); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fastdoubleparser-java11/src/main/java/ch.randelshofer.fastdoubleparser/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)module-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * Provides fast parsers for Java {@code FloatingPointLiteral}s, 8 | * and JSON {@code number}s. 9 | */ 10 | module ch.randelshofer.fastdoubleparser { 11 | exports ch.randelshofer.fastdoubleparser; 12 | } -------------------------------------------------------------------------------- /fastdoubleparser-java11/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/NumberTestDataSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)NumberTestDataSupplier.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.util.Objects; 8 | import java.util.function.Supplier; 9 | 10 | public final class NumberTestDataSupplier { 11 | private final String title; 12 | private final Supplier supplier; 13 | 14 | public NumberTestDataSupplier(String title, 15 | Supplier supplier) { 16 | this.title = title; 17 | this.supplier = supplier; 18 | } 19 | 20 | public NumberTestDataSupplier(String inputValue) { 21 | this(inputValue, () -> new NumberTestData(inputValue)); 22 | } 23 | 24 | public String title() { 25 | return title; 26 | } 27 | 28 | public Supplier supplier() { 29 | return supplier; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object obj) { 34 | if (obj == this) { 35 | return true; 36 | } 37 | if (obj == null || obj.getClass() != this.getClass()) { 38 | return false; 39 | } 40 | NumberTestDataSupplier that = (NumberTestDataSupplier) obj; 41 | return Objects.equals(this.title, that.title) && 42 | Objects.equals(this.supplier, that.supplier); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return Objects.hash(title, supplier); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "NumberTestDataSupplier[" + 53 | "title=" + title + ", " + 54 | "supplier=" + supplier + ']'; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /fastdoubleparser-java17/src/main/java/ch.randelshofer.fastdoubleparser/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)module-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * Provides fast parsers for Java {@code FloatingPointLiteral}s, 8 | * and JSON {@code number}s. 9 | */ 10 | module ch.randelshofer.fastdoubleparser { 11 | exports ch.randelshofer.fastdoubleparser; 12 | } -------------------------------------------------------------------------------- /fastdoubleparser-java17/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparser-java17/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparser-java21/src/main/java/ch.randelshofer.fastdoubleparser/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)module-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * Provides fast parsers for Java {@code FloatingPointLiteral}s, 8 | * and JSON {@code number}s. 9 | */ 10 | module ch.randelshofer.fastdoubleparser { 11 | exports ch.randelshofer.fastdoubleparser; 12 | } -------------------------------------------------------------------------------- /fastdoubleparser-java21/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparser-java21/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparser-java23/src/main/java/ch.randelshofer.fastdoubleparser/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)module-info.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | 6 | /** 7 | * Provides fast parsers for Java {@code FloatingPointLiteral}s, 8 | * and JSON {@code number}s. 9 | */ 10 | module ch.randelshofer.fastdoubleparser { 11 | exports ch.randelshofer.fastdoubleparser; 12 | } -------------------------------------------------------------------------------- /fastdoubleparser-java23/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparser-java23/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparser-java8/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/BigSignificand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)BigSignificand.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.math.BigInteger; 8 | import java.nio.ByteBuffer; 9 | import java.nio.IntBuffer; 10 | 11 | /** 12 | * A mutable significand with a fixed number of bits. 13 | */ 14 | final class BigSignificand { 15 | private static final long LONG_MASK = 0xffffffffL; 16 | private final int numInts; 17 | private final int[] x; 18 | private int firstNonZeroInt; 19 | 20 | public BigSignificand(long numBits) { 21 | if (numBits <= 0 || numBits >= Integer.MAX_VALUE) { 22 | throw new IllegalArgumentException("numBits=" + numBits); 23 | } 24 | int numLongs = (int) ((numBits + 63) >>> 6) + 1; 25 | numInts = numLongs << 1; 26 | x = new int[numInts]; 27 | firstNonZeroInt = numInts; 28 | } 29 | 30 | /** 31 | * Adds the specified value to the significand in place. 32 | * 33 | * @param value the addend, must be a non-negative value 34 | * @throws ArrayIndexOutOfBoundsException on overflow 35 | */ 36 | public void add(int value) { 37 | if (value == 0) { 38 | return; 39 | } 40 | long carry = value & LONG_MASK; 41 | int i = numInts - 1; 42 | for (; carry != 0; i--) { 43 | long sum = (x(i) & LONG_MASK) + carry; 44 | x(i, (int) sum); 45 | carry = sum >>> 32; 46 | } 47 | firstNonZeroInt = Math.min(firstNonZeroInt, i + 1); 48 | } 49 | 50 | /** 51 | * Multiplies the significand with the specified factor in place, 52 | * and then adds the specified addend to it (also in place). 53 | * 54 | * @param factor the multiplication factor, must be a non-negative value 55 | * @param addend the addend, must be a non-negative value 56 | * @throws ArrayIndexOutOfBoundsException on overflow 57 | */ 58 | public void fma(int factor, int addend) { 59 | long factorL = factor & LONG_MASK; 60 | long carry = addend; 61 | int i = numInts - 1; 62 | for (; i >= firstNonZeroInt; i--) { 63 | long product = factorL * (x(i) & LONG_MASK) + carry; 64 | x(i, (int) product); 65 | carry = product >>> 32; 66 | } 67 | if (carry != 0) { 68 | x(i, (int) carry); 69 | firstNonZeroInt = i; 70 | } 71 | } 72 | 73 | public BigInteger toBigInteger() { 74 | byte[] bytes = new byte[x.length << 2]; 75 | IntBuffer buf = ByteBuffer.wrap(bytes).asIntBuffer(); 76 | for (int i = 0; i < x.length; i++) { 77 | buf.put(i, x[i]); 78 | } 79 | return new BigInteger(bytes); 80 | } 81 | 82 | private void x(int i, int value) { 83 | x[i] = value; 84 | } 85 | 86 | private int x(int i) { 87 | return x[i]; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /fastdoubleparser-java8/src/test/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/NumberTestDataSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)NumberTestDataSupplier.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparser; 6 | 7 | import java.util.Objects; 8 | import java.util.function.Supplier; 9 | 10 | public final class NumberTestDataSupplier { 11 | private final String title; 12 | private final Supplier supplier; 13 | 14 | public NumberTestDataSupplier(String title, 15 | Supplier supplier) { 16 | this.title = title; 17 | this.supplier = supplier; 18 | } 19 | 20 | public NumberTestDataSupplier(String inputValue) { 21 | this(inputValue, () -> new NumberTestData(inputValue)); 22 | } 23 | 24 | public String title() { 25 | return title; 26 | } 27 | 28 | public Supplier supplier() { 29 | return supplier; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object obj) { 34 | if (obj == this) { 35 | return true; 36 | } 37 | if (obj == null || obj.getClass() != this.getClass()) { 38 | return false; 39 | } 40 | NumberTestDataSupplier that = (NumberTestDataSupplier) obj; 41 | return Objects.equals(this.title, that.title) && 42 | Objects.equals(this.supplier, that.supplier); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return Objects.hash(title, supplier); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "NumberTestDataSupplier[" + 53 | "title=" + title + ", " + 54 | "supplier=" + supplier + ']'; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-dev/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ch.randelshofer 7 | fastdoubleparser-parentproject 8 | ${revision} 9 | 10 | fastdoubleparserdemo-dev 11 | jar 12 | 13 | 23 14 | ${javaVersion} 15 | ${javaVersion} 16 | true 17 | true 18 | true 19 | 20 | 21 | fastdoubleparserdemo-dev 22 | 23 | 24 | ${basedir}/src/main/java/ch.randelshofer.fastdoubleparserdemo 25 | ${basedir}/src/test/java/ch.randelshofer.fastdoubleparserdemo 26 | 27 | 28 | 29 | 30 | ch.randelshofer 31 | fastdoubleparser-dev 32 | ${revision} 33 | 34 | 35 | com.ibm.icu 36 | icu4j 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-dev/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparserdemo/DoubleSum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)DoubleSum.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparserdemo; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.function.DoubleConsumer; 10 | 11 | import static java.lang.Math.abs; 12 | 13 | /** 14 | * Computes the sum of doubles with the Neumaier compensation algorithm. 15 | *

16 | * Usage with a double stream: 17 | *

18 |  * DoubleSum stats = doubleStream.collect(DoubleSum::new,
19 |  *                                               DoubleSum::accept,
20 |  *                                               DoubleSum::combine);
21 |  * 
22 | *

23 | * References: 24 | *

    25 | *
  • Neumaier Sum.
    26 | * Wikipedia.
    27 | * link
  • 28 | *
29 | *

30 | */ 31 | public final class DoubleSum implements DoubleConsumer { 32 | private double sum = 0.0; 33 | private double c = 0.0; 34 | private List values = new ArrayList<>(); 35 | /** 36 | * Adds a value to the sample. 37 | * 38 | * @param value a new value 39 | */ 40 | @Override 41 | public void accept(double value) { 42 | sumWithCompensation(value); 43 | } 44 | 45 | /** 46 | * Combines the state of another {@code VarianceStatistics} into this one. 47 | * 48 | * @param other another {@code VarianceStatistics} 49 | * @return this 50 | */ 51 | public DoubleSum combine(DoubleSum other) { 52 | sumWithCompensation(other.sum); 53 | sumWithCompensation(other.c); 54 | return this; 55 | } 56 | 57 | /** 58 | * Returns the sum. 59 | * 60 | * @return the sum of square 61 | */ 62 | public double getSum() { 63 | return sum + c; 64 | } 65 | 66 | /** 67 | * Performs the Neumaier Sum algorithm. 68 | * 69 | * @param input the new input value 70 | */ 71 | private void sumWithCompensation(double input) { 72 | values.add(input); 73 | double t = sum + input; 74 | if (abs(sum) >= abs(input)) { 75 | c += (sum - t) + input;// If sum is bigger, low-order digits of input are lost. 76 | } else { 77 | c += (input - t) + sum;// Else low-order digits of sum are lost 78 | } 79 | sum = t; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-dev/src/main/java/ch.randelshofer.fastdoubleparserdemo/module-info.java: -------------------------------------------------------------------------------- 1 | module ch.randelshofer.fastdoubleparserdemo { 2 | requires ch.randelshofer.fastdoubleparser; 3 | requires java.management; 4 | requires com.ibm.icu; 5 | } -------------------------------------------------------------------------------- /fastdoubleparserdemo-dev/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-dev/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java11/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ch.randelshofer 7 | fastdoubleparser-parentproject 8 | ${revision} 9 | 10 | fastdoubleparserdemo-java11 11 | jar 12 | 13 | 11 14 | true 15 | true 16 | true 17 | 18 | 19 | fastdoubleparserdemo-Java11 20 | 21 | 22 | ${basedir}/src/main/java/ch.randelshofer.fastdoubleparserdemo 23 | 24 | src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparserdemo/test/java/ch.randelshofer.fastdoubleparserdemo 25 | 26 | 27 | 28 | maven-resources-plugin 29 | 30 | 31 | copy-sources-from-fastdoubleparserdemo-dev 32 | generate-sources 33 | 34 | copy-resources 35 | 36 | 37 | ${project.build.directory}/generated-sources/java 38 | 39 | 40 | 41 | ${basedir}/../fastdoubleparserdemo-dev/src/main/java/ch.randelshofer.fastdoubleparserdemo 42 | 43 | 44 | **/*.java 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.codehaus.mojo 54 | build-helper-maven-plugin 55 | 56 | 57 | generate-sources 58 | 59 | add-source 60 | 61 | 62 | 63 | ${project.build.directory}/generated-sources/java 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ch.randelshofer 75 | fastdoubleparser-java11 76 | ${revision} 77 | 78 | 79 | com.ibm.icu 80 | icu4j 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-java11/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparserdemo/DecimalFormatMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)DecimalFormatMain.java 3 | * Copyright © 2024 Werner Randelshofer, Switzerland. MIT License. 4 | */ 5 | package ch.randelshofer.fastdoubleparserdemo; 6 | 7 | import java.text.DecimalFormat; 8 | import java.text.NumberFormat; 9 | import java.util.Locale; 10 | 11 | public final class DecimalFormatMain { 12 | 13 | public static void main(String... args) { 14 | Locale locale = Locale.forLanguageTag("ar"); 15 | DecimalFormat f = (DecimalFormat) NumberFormat.getNumberInstance(locale); 16 | String formatted = f.format(-123_456.789); 17 | System.out.print(formatted + '\n'); 18 | for (char ch : formatted.toCharArray()) { 19 | System.out.print("U+" + Integer.toHexString(ch) + " "); 20 | } 21 | System.out.println(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-java11/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparserdemo/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java11/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparserdemo/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java17/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ch.randelshofer 7 | fastdoubleparser-parentproject 8 | ${revision} 9 | 10 | fastdoubleparserdemo-java17 11 | jar 12 | 13 | 17 14 | true 15 | true 16 | true 17 | 18 | 19 | fastdoubleparserdemo-Java17 20 | 21 | 22 | ${basedir}/src/main/java/ch.randelshofer.fastdoubleparserdemo 23 | ${basedir}/src/test/java/ch.randelshofer.fastdoubleparserdemo 24 | 25 | 26 | maven-resources-plugin 27 | 28 | 29 | copy-sources-from-fastdoubleparserdemo-dev 30 | generate-sources 31 | 32 | copy-resources 33 | 34 | 35 | ${project.build.directory}/generated-sources/java 36 | 37 | 38 | 39 | ${basedir}/../fastdoubleparserdemo-dev/src/main/java/ch.randelshofer.fastdoubleparserdemo 40 | 41 | 42 | **/*.java 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.codehaus.mojo 52 | build-helper-maven-plugin 53 | 54 | 55 | generate-sources 56 | 57 | add-source 58 | 59 | 60 | 61 | ${project.build.directory}/generated-sources/java 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ch.randelshofer 73 | fastdoubleparser-java17 74 | ${revision} 75 | 76 | 77 | com.ibm.icu 78 | icu4j 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-java17/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java17/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java17/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java17/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java21/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ch.randelshofer 7 | fastdoubleparser-parentproject 8 | ${revision} 9 | 10 | fastdoubleparserdemo-java21 11 | jar 12 | 13 | 21 14 | true 15 | true 16 | true 17 | 18 | 19 | fastdoubleparserdemo-Java21 20 | 21 | 22 | ${basedir}/src/main/java/ch.randelshofer.fastdoubleparserdemo 23 | ${basedir}/src/test/java/ch.randelshofer.fastdoubleparserdemo 24 | 25 | 26 | maven-resources-plugin 27 | 28 | 29 | copy-sources-from-fastdoubleparserdemo-dev 30 | generate-sources 31 | 32 | copy-resources 33 | 34 | 35 | ${project.build.directory}/generated-sources/java 36 | 37 | 38 | 39 | ${basedir}/../fastdoubleparserdemo-dev/src/main/java/ch.randelshofer.fastdoubleparserdemo 40 | 41 | 42 | **/*.java 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.codehaus.mojo 52 | build-helper-maven-plugin 53 | 54 | 55 | generate-sources 56 | 57 | add-source 58 | 59 | 60 | 61 | ${project.build.directory}/generated-sources/java 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ch.randelshofer 73 | fastdoubleparser-java21 74 | ${revision} 75 | 76 | 77 | com.ibm.icu 78 | icu4j 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-java21/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java21/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java21/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java21/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java23/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java23/src/main/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java23/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java23/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparser/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java8/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ch.randelshofer 7 | fastdoubleparser-parentproject 8 | ${revision} 9 | 10 | fastdoubleparserdemo-java8 11 | jar 12 | 13 | 8 14 | true 15 | true 16 | true 17 | 18 | 19 | fastdoubleparserdemo-Java8 20 | 21 | 22 | ${basedir}/src/main/java/ch.randelshofer.fastdoubleparserdemo 23 | ${basedir}/src/test/java/ch.randelshofer.fastdoubleparserdemo 24 | 25 | 26 | maven-resources-plugin 27 | 28 | 29 | copy-sources-from-fastdoubleparserdemo-dev 30 | generate-sources 31 | 32 | copy-resources 33 | 34 | 35 | ${project.build.directory}/generated-sources/java 36 | 37 | 38 | 39 | ${basedir}/../fastdoubleparserdemo-dev/src/main/java/ch.randelshofer.fastdoubleparserdemo 40 | 41 | 42 | **/*.java 43 | 44 | 45 | **/module-info.java 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.codehaus.mojo 55 | build-helper-maven-plugin 56 | 57 | 58 | generate-sources 59 | 60 | add-source 61 | 62 | 63 | 64 | ${project.build.directory}/generated-sources/java 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | ch.randelshofer 76 | fastdoubleparser-java8 77 | ${revision} 78 | 79 | 80 | com.ibm.icu 81 | icu4j 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /fastdoubleparserdemo-java8/src/main/java/ch.randelshofer.fastdoubleparserdemo/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java8/src/main/java/ch.randelshofer.fastdoubleparserdemo/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo-java8/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparserdemo/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrandelshofer/FastDoubleParser/302b4d097b50d49e7c7224948658f72f057db47a/fastdoubleparserdemo-java8/src/test/java/ch.randelshofer.fastdoubleparserdemo/ch/randelshofer/fastdoubleparserdemo/empty.txt -------------------------------------------------------------------------------- /fastdoubleparserdemo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ch.randelshofer 7 | fastdoubleparser-parentproject 8 | ${revision} 9 | 10 | fastdoubleparserdemo 11 | fastdoubleparserdemo multi-release jar 12 | 13 | 14 | false 15 | true 16 | 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 25 | default-compile 26 | compile 27 | 28 | compile 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-javadoc-plugin 39 | 40 | true 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-site-plugin 46 | 47 | true 48 | 49 | 50 | 51 | maven-assembly-plugin 52 | 53 | false 54 | 55 | src/assembly/mrjar.xml 56 | 57 | 58 | 59 | - 60 | - 61 | - 62 | ${project.scm.url} 63 | ${git.commit.time} 64 | ${git.commit.id.full} 65 | true 66 | ch.randelshofer.fastdoubleparserdemo 67 | 68 | 69 | 70 | 71 | 72 | make-assembly 73 | package 74 | 75 | single 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /fastdoubleparserdemo/src/assembly/mrjar.xml: -------------------------------------------------------------------------------- 1 | 2 | mvjar 3 | 4 | jar 5 | 6 | false 7 | 8 | 9 | true 10 | 11 | ch.randelshofer:fastdoubleparserdemo-java8 12 | 13 | 14 | true 15 | false 16 | 17 | 18 | 19 | true 20 | 21 | ch.randelshofer:fastdoubleparserdemo-java11 22 | 23 | 24 | META-INF/versions/11 25 | true 26 | false 27 | 28 | 29 | /META-INF/** 30 | 31 | 32 | 33 | 34 | 35 | true 36 | 37 | ch.randelshofer:fastdoubleparserdemo-java17 38 | 39 | 40 | META-INF/versions/17 41 | true 42 | false 43 | 44 | 45 | /META-INF/** 46 | 47 | 48 | 49 | 50 | 51 | true 52 | 53 | ch.randelshofer:fastdoubleparserdemo-fastdoubleparser-java21 54 | 55 | 56 | META-INF/versions/19 57 | true 58 | false 59 | 60 | 61 | /META-INF/** 62 | 63 | 64 | 65 | 66 | 67 | 68 | --------------------------------------------------------------------------------