FastDoubleParser
A Java port of Daniel Lemire's fast_float project
Install / Use
/learn @wrandelshofer/FastDoubleParserREADME
FastDoubleParser
This is a Java port of Daniel Lemire's fast_float project.
This project provides parsers for double, float, BigDecimal and BigInteger values.
The double and float parsers are optimised for speed for the most common inputs.
The BigDecimal and BigInteger parsers are optimised for speed on all inputs.
The code in this project contains optimised versions for Java SE 1.8, 11, 17, 21 and 22. The code is released in a single multi-release jar, which contains the code for all these versions except 20.
License
Project License
This project can be licensed under the MIT License.
Code License
Some code in this project is derived from the following projects:
- fast_float, licensed under MIT License
- bigint, licensed under BSD 2-clause License
The code is marked as such.
If you redistribute code, you must follow the terms of all involved licenses (MIT License, BSD 2-clause License).
The build scripts in this project do include the license files into the jar files. So that the released jar files automatically comply with the licenses, when you use them.
Dependency
You can download released Jar files from github, or from a public Maven using the following dependency descriptor:
<dependency>
<groupId>ch.randelshofer</groupId>
<artifactId>fastdoubleparser</artifactId>
<version>…version…</version>
</dependency>
Usage
module MyModule {
requires ch.randelshofer.fastdoubleparser;
}
import ch.randelshofer.fastdoubleparser.JavaDoubleParser;
import ch.randelshofer.fastdoubleparser.JavaFloatParser;
import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser;
import ch.randelshofer.fastdoubleparser.JavaBigIntegerParser;
import ch.randelshofer.fastdoubleparser.JsonDoubleParser;
import ch.randelshofer.fastdoubleparser.NumberFormatSymbols;
import ch.randelshofer.fastdoubleparser.ConfigurableDoubleParser;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormatSymbols;
import java.util.List;
import java.util.Locale;
import java.util.Set;
class MyMain {
public static void main(String... args) {
double d = JavaDoubleParser.parseDouble("1.2345e135");
System.out.println("Java double value: " + d);
float f = JavaFloatParser.parseFloat("1.2345f");
System.out.println("Java float value: " + f);
BigDecimal bd = JavaBigDecimalParser.parseBigDecimal("1.2345");
System.out.println("Java big decimal value: " + bd);
BigInteger bi = JavaBigIntegerParser.parseBigInteger("12345");
System.out.println("Java big integer value: " + bi);
double jsonD = JsonDoubleParser.parseDouble("1.2345e85");
System.out.println("JSON double value: " + jsonD);
var symbols = NumberFormatSymbols.fromDecimalFormatSymbols(new DecimalFormatSymbols(Locale.GERMAN));
boolean ignoreCase = true;
var confdParser = new ConfigurableDoubleParser(symbols, ignoreCase);
double confD1 = confdParser.parseDouble("123.456,89e5");
double confD2 = confdParser.parseDouble("-0.15425,89E-5");
System.out.println("Double value in German Locale: " + confD1);
System.out.println("Another double value in German Locale: " + confD2);
symbols = NumberFormatSymbols.fromDecimalFormatSymbols(new DecimalFormatSymbols(Locale.forLanguageTag("zh-CN")));
symbols = symbols
.withDigits(List.of('〇', '一', '二', '三', '四', '五', '六', '七', '八', '九'))
.withExponentSeparator((Set.of("*一〇^")));
confdParser = new ConfigurableDoubleParser(symbols, ignoreCase);
double confZh = confdParser.parseDouble("四一.五七五三七一六六二一四五九八*一〇^七");
System.out.println("Double value in Chinese Locale: " + confZh);
}
}
The parse...()-methods take a CharacterSequence. a char-array or a byte-array as argument. This way, you can
parse from a StringBuffer or an array without having to convert your input to a String. Parsing from an array is
faster, because the parser can process multiple characters at once using SIMD instructions.
Performance Tuning
The JVM does not reliably inline String.charAt(int). This may negatively impact the
parse...()-methods that take a CharacterSequence as an argument.
To ensure optimal performance, you can use the following java command line option:
-XX:CompileCommand=inline,java/lang/String.charAt
Performance Characteristics
float and double parsers
On common input data, the fast double and float parsers are about 4 times faster than
java.lang.Double.valueOf(String) and java.lang.Float.valueOf(String).
For less common inputs, the fast parsers can be slower than their java.lang counterparts.
A double value can always be specified exactly with up to 17 digits in the significand.
A float only needs up to 8 digits.
Therefore, inputs with more than 19 digits in the significand are considered less common.
Such inputs are expected to occur if the input data was created with more precision, and needs to be narrowed down
to the precision of a double or a float.
BigDecimal and BigInteger parsers
On common input data, the fast BigDecimal and BigInteger parsers are slightly faster than
java.math.BigDecimal(String) and java.math.BigInteger(String).
For less common inputs with many digits, the fast parsers can be a lot faster than their java.math counterparts.
The fast parsers can convert even the longest supported inputs in less than 6 minutes, whereas
their java.math counterparts need months!
The fast parsers convert digit characters from base 10 to a bit sequence in base 2
using a divide-and-conquer algorithm. Small sequences of digits are converted
individually to bit sequences and then gradually combined to the final bit sequence.
This algorithm needs to perform multiplications of very long bit sequences.
The multiplications are performed in the frequency domain using a discrete fourier transform.
The multiplications in the frequency domain can be performed in O(N log N (log log N)) time,
where N is the number of digits.
In contrast, conventional multiplication algorithms in the time domain need O(N²) time.
Memory usage and computation time
The memory usage depends on the result type and the maximal supported input character length.
The computation times are given for a Mac mini 2018 with Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz.
| Parser | Result Type | Maximal<br/>input length | Memory usage<br/>JVM -Xmx | Computation<br/>Time | |----------------------|----------------------|-------------------------:|--------------------------:|---------------------:| | JavaDoubleParser | java.lang.Double | 2^31 - 5 | 10 gigabytes | < 5 sec | | JavaFloatParser | java.lang.Float | 2^31 - 5 | 10 gigabytes | < 5 sec | | JavaBigIntegerParser | java.math.BigInteger | 1,292,782,622 | 16 gigabytes | < 6 min | | JavaBigDecimalParser | java.math.BigDecimal | 1,292,782,635 | 16 gigabytes | < 6 min |
Performance measurements
The data file canada.txt
This file contains numbers in the range from -128 to +128.
Most input lines look like this: 52.038048000000117.
CPU: Apple M2 Max<br> OS: Mac OS X, 14.7, 12 processors available<br> VM: Java 23, OpenJDK 64-Bit Server VM, Azul Systems, Inc., 23.0.1+11<br> -XX:CompileCommand=inline,java/lang/String.charAt
| Method | MB/s | stdev | Mfloats/s | ns/f | speedup | JDK | |-----------------------------------------|--------:|-------:|----------:|-------:|--------:|--------| | java.lang.Double | 107.96 | 2.0 % | 6.20 | 161.19 | 1.00=a | 23.0.1 | | java.lang.Float | 118.12 | 3.1 % | 6.79 | 147.32 | 1.00=b | 23.0.1 | | java.math.BigDecimal | 400.25 | 4.8 % | 23.00 | 43.48 | 1.00=c | 23.0.1 | | java.text.NumberFormat | 72.06 | 1.6 % | 4.14 | 241.49 | 1.00=d | 23.0.1 | | com.ibm.icu.text.NumberFormat | 24.32 | 2.7 % | 1.40 | 715.62 | 1.00=e | 23.0.1 | | JavaDoubleParser CharSequence | 532.05 | 4.3 % | 30.58 | 32.71 | 4.93a | 23.0.1 | | JavaDoubleParser char[] | 973.38 | 7.1 % | 55.94 | 17.88 | 9.02a | 23.0.1 | | JavaDoubleParser byte[] | 962.18 | 8.0 % | 55.29 | 18.09 | 8.91a | 23.0.1 | | JsonDoubleParser CharSequence | 575.45 | 5.8 % | 33.07 | 30.24 | 5.33a | 23.0.1 | | JsonDoubleParser char[] | 991.20 | 6.2 % | 56.96 | 17.56 | 9.18a | 23.0.1 | | JsonDoubleParser byte[] | 990.74 | 5.5 % | 56.93 | 17.56 | 9.18a | 23.0.1 | | JavaFloatParser CharSequence | 572.02 | 6.0 % | 32.87 | 30.42 | 4.84b | 23.0.1 | | JavaFloatParser char[] | 1007.96 | 21.5 % | 57.92 | 17.26 | 8.53b | 23.0.1 | | JavaFloatParser byte[] | 1011.75 | 6.3 % | 58.14 | 17.20 | 8.57*b | 23.0.1 | | JavaBigDecimalParser CharSequence
Related Skills
node-connect
351.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
110.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
351.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
351.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
