LibJ Math is an extension to the java.math
Java API. The classes in LibJ Math can be split in two categories:
- Higher-performance alternatives to functionalities present in
java.math
. - Supplementary functionalities missing in
java.math
.
An arbitrary-precision integer replacement for java.math.BigInteger
, with the following differences:
- Mutable:
BigInt
is mutable, allowing for reuse of allocated magnitude arrays. - Little-endian:
BigInt
's magnitude array is in little-endian order, allowing for faster operations concerning changes to a number's scale. - Faster arithmetic: The arithmetic algorithms in
BigInt
are implemented with the optimization of memory (heap allocation) and runtime performance in mind. - Faster multiplication of large numbers: Support parallel multiplication algorithm for large numbers.
- In-place multiplication algorithms: Employs optimized algorithms that perform calculations in place, rather than instantiating transient magnitude arrays to defer to the GC to free later.
- Support for
int
andlong
parameters and return types:BigInt
does not require its parameters or return types to beBigInt
, avoiding unnecessary instantiation of transientBigInt
objects andint[]
arrays. - Support for "object-less" operation: All methods in
BigInt
are available in static form, allowing bareint[]
value-encoded number arrays to be used without aBigInt
instance, leading to further reduction in heap memory allocation. - Significantly reduced heap allocation:
BigInt
was designed to reduce the number of instances allocated purely for the purpose of transient calculation, and significantly outperformsBigInteger
with regard to memory and GC load. - No preemptive exception checking:
BigInt
does not preemptively check for exceptions. If a programmer divides by zero he has only himself to blame. And, it is ok to have undefined behavior. - Native bindings:
BigInt
provides select algorithms in 3 forms:
* Native Bindings are present in the built JAR for MacOS (x64 and Arm64), Linux (x64), and Windows (x64), and are loaded by default on system startup.
** To use Critical Native JNI bindings, the JVM must be launched with -Xcomp
.
The BigInt
architecture exposes the underlying int[]
array, and provides static function equivalents for all of its instance methods. This is possible because the standalone int[]
array contains all information regarding the magnitude of an arbitrary precision integer. The int[]
has the following encoding:
- Signed length: The sign and number of base-232 digits (limbs) in the magnitude.
val[0]
∈[-1 << 26, .., -1, 0, 1, .., 1 << 26]
- Absolute length: The absolute number of base-232 digits (limbs) in the magnitude.
Math.abs(val[0])
- Sign: The sign of the magnitude (
-1
for negative, and1
for positive).Math.signum(val[0])
- Zero: Magnitude is zero.
val[0] == 0
- Magnitude digits: The base-232 digits (limbs).
val[2,val[0]-1]
∈[Integer.MIN_VALUE, Integer.MAX_VALUE]
- The base 232 digits (limbs) of the number are in little-endian order.
The bare int[]
array can therefore be used as a feature-equivalent replacement for BigInt
, with one notable difference: no BigInt
instance is required.
BigInt
is bundled with this module, which is available in the Maven Central Repository. The BigInt
implementation provides JNI bindings for MacOS (x64 and Arm64), Linux and Windows platforms (x64), which can improve performance significantly. The JNI bindings are activated automatically, unless -Dorg.libj.math.BigInt.noNative
is specified as a system property. The JNI bindings were built with Intel compilers, and are as statically linked as can be. The bindings also rely on the following shared libraries:
- Linux: libcilkrts5
- MacOS: None
- Windows: None
The following matrix provides a comparison of functions offered by BigInteger
vs BigInt
and bare int[]
array. The values in the matrix have the following rules:
- 0: Represents the baseline.
- +###%: Prepresents "percent better* compared to the baseline".
* "Better" means: 'faster' for runtime performance, and 'less instances' for heap memory allocation.
It is also important to note the following:
- These numbers are derived from the detailed benchmark tests that are linked on each row. Please refer to Benchmarks for further information.
- These numbers have an error margin, and should be considered as estimates. It is possible to achieve better precision in the results, but would require significant time running these tests in order to increase the sample size.
- For functions that are not inherently available in the particular subject (i.e. in
BigInteger
), external utility functions were used to bridge the gap.
Runtime Performance Heap Allocation
╔════════════════════╦════════════╦═════════╦═════════╗ ╔════════════╦══════════╦══════════╗
║ ║ BigInteger ║ BigInt ║ int[] ║ ║ BigInteger ║ BigInt ║ int[] ║
╠════════════════════╬════════════╬═════════╬═════════╣ ╠════════════╬══════════╬══════════╣
║ add(int,int) ║ 0 ║ +117% ║ +112% ║ ║ 0 ║ +191% ║ +113% ║
║ add(int) ║ 0 ║ +191% ║ +363% ║ ║ 0 ║ +191% ║ +113% ║
║ add(int,long) ║ 0 ║ +203% ║ +139% ║ ║ 0 ║ +297% ║ +208% ║
║ add(long) ║ 0 ║ +87% ║ +230% ║ ║ 0 ║ +197% ║ +126% ║
║ add(T) ║ 0 ║ +10% ║ +87% ║ ║ 0 ║ +75% ║ +33% ║
║ sub(int,int) ║ 0 ║ +111% ║ +172% ║ ║ 0 ║ +191% ║ +113% ║
║ sub(int) ║ 0 ║ +151% ║ +392% ║ ║ 0 ║ +191% ║ +113% ║
║ sub(int,long) ║ 0 ║ +253% ║ +285% ║ ║ 0 ║ +297% ║ +208% ║
║ sub(long) ║ 0 ║ +183% ║ +203% ║ ║ 0 ║ +197% ║ +126% ║
║ sub(T) ║ 0 ║ +8% ║ +55% ║ ║ 0 ║ +75% ║ +33% ║
║ not() ║ 0 ║ +645% ║ +1173% ║ ║ 0 ║ +113% ║ +38% ║
║ xor(T) ║ 0 ║ +236% ║ +461% ║ ║ 0 ║ +76% ║ +34% ║
║ or(T) ║ 0 ║ +332% ║ +453% ║ ║ 0 ║ +85% ║ +43% ║
║ and(T) ║ 0 ║ +384% ║ +455% ║ ║ 0 ║ +75% ║ +33% ║
║ andNot(T) ║ 0 ║ +353% ║ +511% ║ ║ 0 ║ +75% ║ +33% ║
║ bitCount() ║ +7% ║ 0 ║ +88% ║ ║ +50% ║ +50% ║ 0 ║
║ bitLength() ║ 0 ║ +47% ║ +72% ║ ║ 0 ║ +80% ║ +210% ║
║ toByteArray() BE ║ 0 ║ +36% ║ +14% ║ ║ +50% ║ +50% ║ 0 ║
║ toByteArray() LE ║ 0 ║ +89% ║ +96% ║ ║ +50% ║ +50% ║ 0 ║
║ testBit(int) ║ +8% ║ +31% ║ +13% ║ ║ +50% ║ +50% ║ 0 ║
║ setBit(int) ║ 0 ║ +492% ║ +619% ║ ║ 0 ║ +111% ║ +36% ║
║ flipBit(int) ║ 0 ║ +400% ║ +460% ║ ║ 0 ║ +111% ║ +36% ║
║ clearBit(int) ║ 0 ║ +375% ║ +438% ║ ║ 0 ║ +111% ║ +36% ║
║ shiftLeft(int) ║ +30% ║ +2% ║ +59% ║ ║ 0 ║ +100% ║ +25% ║
║ shiftRight(int) ║ 0 ║ +59% ║ +102% ║ ║ 0 ║ +100% ║ +25% ║
║ div(int,int) ║ 0 ║ +84% ║ +109% ║ ║ 0 ║ +142% ║ +72% ║
║ div(int,long) ║ 0 ║ +180% ║ +142% ║ ║ 0 ║ +247% ║ +158% ║
║ div(int) ║ 0 ║ +135% ║ +153% ║ ║ 0 ║ +147% ║ +74% ║
║ div(long) ║ 0 ║ +113% ║ +139% ║ ║ 0 ║ +150% ║ +85% ║
║ div(T) ║ 0 ║ +36% ║ +49% ║ ║ +43% ║ +68% ║ +13% ║
║ divRem(int,int) ║ 0 ║ +50% ║ +71% ║ ║ 0 ║ +142% ║ +72% ║
║ divRem(int,long) ║ 0 ║ +187% ║ +171% ║ ║ 0 ║ +247% ║ +158% ║
║ divRem(int) ║ 0 ║ +125% ║ +136% ║ ║ 0 ║ +147% ║ +74% ║
║ divRem(long) ║ 0 ║ +133% ║ +143% ║ ║ 0 ║ +150% ║ +85% ║
║ divRem(T) ║ 0 ║ +2% ║ +13% ║ ║ +50% ║ +28% ║ +9% ║
║ log10(UP) ║ 0 ║ +5553% ║ +10217% ║ ║ 0 ║ +618% ║ +334% ║
║ log10(DOWN) ║ 0 ║ +7531% ║ +10869% ║ ║ 0 ║ +618% ║ +334% ║
║ log10(FLOOR) ║ 0 ║ +8085% ║ +10956% ║ ║ 0 ║ +611% ║ +329% ║
║ log10(CEILING) ║ 0 ║ +7724% ║ +10177% ║ ║ 0 ║ +615% ║ +331% ║
║ log10(HALF_UP) ║ 0 ║ +8733% ║ +11200% ║ ║ 0 ║ +863% ║ +497% ║
║ log10(HALF_DOWN) ║ 0 ║ +8846% ║ +11706% ║ ║ 0 ║ +874% ║ +504% ║
║ log10(HALF_EVEN) ║ 0 ║ +7972% ║ +10026% ║ ║ 0 ║ +874% ║ +504% ║
║ log10(UNNECESSARY) ║ 0 ║ +11783% ║ +17292% ║ ║ 0 ║ +624% ║ +338% ║
║ log2(UP) ║ 0 ║ +7632% ║ +9239% ║ ║ +50% ║ +250% ║ +100% ║
║ log2(DOWN) ║ 0 ║ +18591% ║ +19961% ║ ║ +50% ║ +250% ║ +100% ║
║ log2(FLOOR) ║ 0 ║ +16631% ║ +18523% ║ ║ +50% ║ +250% ║ +100% ║
║ log2(CEILING) ║ 0 ║ +7722% ║ +9174% ║ ║ +50% ║ +245% ║ +97% ║
║ log2(HALF_UP) ║ 0 ║ +7455% ║ +8727% ║ ║ +5% ║ +295% ║ +122% ║
║ log2(HALF_DOWN) ║ 0 ║ +7574% ║ +8423% ║ ║ 0 ║ +295% ║ +122% ║
║ log2(HALF_EVEN) ║ 0 ║ +6996% ║ +8522% ║ ║ +5% ║ +286% ║ +118% ║
║ log2(UNNECESSARY) ║ 0 ║ +28168% ║ +34450% ║ ║ +50% ║ +250% ║ +100% ║
║ log(DOWN) ║ 0 ║ +82% ║ +105% ║ ║ +50% ║ +50% ║ 0 ║
║ log(UP) ║ 0 ║ +29% ║ +28% ║ ║ +50% ║ +50% ║ 0 ║
║ log(FLOOR) ║ 0 ║ +19% ║ +9% ║ ║ +50% ║ +50% ║ 0 ║
║ log(CEILING) ║ 0 ║ +37% ║ +26% ║ ║ +50% ║ +50% ║ 0 ║
║ log(HALF_UP) ║ 0 ║ +26% ║ +22% ║ ║ +50% ║ +50% ║ 0 ║
║ log(HALF_DOWN) ║ 0 ║ +25% ║ +20% ║ ║ +50% ║ +50% ║ 0 ║
║ log(HALF_EVEN) ║ 0 ║ +33% ║ +21% ║ ║ +50% ║ +50% ║ 0 ║
║ log(UNNECESSARY) ║ 0 ║ +20% ║ +32% ║ ║ +50% ║ +50% ║ 0 ║
║ sqrt ║ 0 ║ +300% ║ +372% ║ ║ ║ ║ ║
║ mul(T): 1 ║ +9% ║ 0 ║ +53% ║ ║ +41% ║ +75% ║ +12% ║
║ mul(T): 2 ║ 0 ║ +2% ║ +21% ║ ║ +45% ║ +75% ║ +12% ║
║ mul(T): 4 ║ +30% ║ 0 ║ +7% ║ ║ +45% ║ +75% ║ +12% ║
║ mul(T): 8 ║ +89% ║ 0 ║ +2% ║ ║ +50% ║ +75% ║ +12% ║
║ mul(T): 16 ║ +116% ║ 0 ║ +4% ║ ║ +18% ║ +102% ║ +26% ║
║ mul(T): 32 ║ +86% ║ 0 ║ +4% ║ ║ 0 ║ +531% ║ +300% ║
║ mul(T): 64 ║ +54% ║ 0 ║ +3% ║ ║ 0 ║ +2065% ║ +1303% ║
║ mul(T): 128 ║ +40% ║ 0 ║ +3% ║ ║ 0 ║ +8491% ║ +5508% ║
║ mul(T): 256 ║ 0 ║ +3% ║ +8% ║ ║ 0 ║ +23570% ║ +15365% ║
║ mul(T): 512 ║ 0 ║ +9% ║ +17% ║ ║ 0 ║ +61184% ║ +39956% ║
║ mul(T): 1024 ║ 0 ║ +42% ║ +46% ║ ║ 0 ║ +192303% ║ +125677% ║
║ pow(int) ║ +65% ║ 0 ║ +4% ║ ║ 0 ║ +902% ║ +162% ║
║ mul(int,int) ║ 0 ║ +38% ║ +98% ║ ║ 0 ║ +175% ║ +75% ║
║ mul(int) ║ 0 ║ +80% ║ +167% ║ ║ 0 ║ +175% ║ +75% ║
║ mul(int,long) ║ 0 ║ +143% ║ +193% ║ ║ 0 ║ +250% ║ +125% ║
║ mul(long) ║ 0 ║ +44% ║ +83% ║ ║ 0 ║ +165% ║ +65% ║
║ mul(T,T): 1 ║ +12% ║ 0 ║ +4% ║ ║ +34% ║ +100% ║ +25% ║
║ mul(T,T): 2 ║ +22% ║ 0 ║ +4% ║ ║ +41% ║ +100% ║ +25% ║
║ mul(T,T): 4 ║ +47% ║ 0 ║ +6% ║ ║ +34% ║ +100% ║ +25% ║
║ mul(T,T): 8 ║ +88% ║ 0 ║ +3% ║ ║ +14% ║ +100% ║ +25% ║
║ mul(T,T): 16 ║ +111% ║ 0 ║ +1% ║ ║ +7% ║ +100% ║ +25% ║
║ mul(T,T): 32 ║ +96% ║ 0 ║ +1% ║ ║ 0 ║ +411% ║ +231% ║
║ mul(T,T): 64 ║ +81% ║ 0 ║ 0 ║ ║ 0 ║ +1997% ║ +1295% ║
║ mul(T,T): 128 ║ +76% ║ 0 ║ +2% ║ ║ 0 ║ +6515% ║ +4313% ║
║ mul(T,T): 256 ║ +11% ║ 0 ║ +5% ║ ║ 0 ║ +17440% ║ +11624% ║
║ mul(T,T): 512 ║ 0 ║ +2% ║ +9% ║ ║ 0 ║ +49977% ║ +33377% ║
║ mul(T,T): 1024 ║ 0 ║ +57% ║ +62% ║ ║ 0 ║ +149643% ║ +100036% ║
║ neg() ║ 0 ║ +231% ║ +223% ║ ║ +50% ║ +100% ║ +25% ║
║ abs() ║ 0 ║ +126% ║ +118% ║ ║ +50% ║ +72% ║ +11% ║
║ byteValue() ║ 0 ║ +40% ║ +102% ║ ║ +50% ║ +50% ║ 0 ║
║ shortValue() ║ +24% ║ 0 ║ +81% ║ ║ +50% ║ +50% ║ 0 ║
║ intValue() ║ +71% ║ 0 ║ +268% ║ ║ +50% ║ +50% ║ 0 ║
║ longValue() ║ 0 ║ +90% ║ +106% ║ ║ +50% ║ +50% ║ 0 ║
║ floatValue() ║ +40% ║ 0 ║ +78% ║ ║ +50% ║ +50% ║ 0 ║
║ doubleValue() ║ 0 ║ +23% ║ +39% ║ ║ +50% ║ +50% ║ 0 ║
║ toString() ║ 0 ║ +356% ║ +366% ║ ║ +100% ║ +427% ║ +109% ║
║ hashCode() ║ +4% ║ +4% ║ 0 ║ ║ +50% ║ +50% ║ 0 ║
║ signum() ║ 0 ║ +3% ║ +50% ║ ║ +50% ║ +50% ║ 0 ║
║ precision() ║ 0 ║ +1922% ║ +2466% ║ ║ 0 ║ +745% ║ +445% ║
║ compareTo(T) ║ +23% ║ 0 ║ +25% ║ ║ +25% ║ +50% ║ +16% ║
║ equals(T) ║ 0 ║ +12% ║ +33% ║ ║ +25% ║ +50% ║ +16% ║
║ max(T) ║ +6% ║ 0 ║ +41% ║ ║ +25% ║ +50% ║ +16% ║
║ min(T) ║ +16% ║ 0 ║ +31% ║ ║ +25% ║ +50% ║ +16% ║
║ rem(int,int) ║ 0 ║ +135% ║ +123% ║ ║ 0 ║ +183% ║ +97% ║
║ rem(int,int):T ║ 0 ║ +62% ║ +109% ║ ║ 0 ║ +161% ║ +83% ║
║ rem(int,long) ║ 0 ║ +198% ║ +207% ║ ║ 0 ║ +274% ║ +176% ║
║ rem(int,long):T ║ 0 ║ +200% ║ +184% ║ ║ 0 ║ +247% ║ +158% ║
║ rem(int) ║ 0 ║ +174% ║ +146% ║ ║ 0 ║ +161% ║ +83% ║
║ rem(int):T ║ 0 ║ +165% ║ +135% ║ ║ 0 ║ +161% ║ +83% ║
║ rem(long) ║ 0 ║ +140% ║ +111% ║ ║ 0 ║ +164% ║ +93% ║
║ rem(long):T ║ 0 ║ +136% ║ +149% ║ ║ 0 ║ +164% ║ +93% ║
║ rem(T) ║ 0 ║ +54% ║ +69% ║ ║ +25% ║ +75% ║ +33% ║
║ mod(int) ║ 0 ║ +182% ║ +227% ║ ║ 0 ║ +197% ║ +111% ║
║ mod(long) ║ 0 ║ +161% ║ +182% ║ ║ 0 ║ +201% ║ +123% ║
║ mod(T) ║ 0 ║ +58% ║ +79% ║ ║ +11% ║ +86% ║ +40% ║
║ sqrt(DOWN) ║ 0 ║ +651% ║ +973% ║ ║ 0 ║ +497% ║ +141% ║
║ sqrt(UP) ║ 0 ║ +700% ║ +868% ║ ║ 0 ║ +516% ║ +145% ║
║ sqrt(FLOOR) ║ 0 ║ +758% ║ +946% ║ ║ 0 ║ +494% ║ +141% ║
║ sqrt(CEILING) ║ 0 ║ +686% ║ +855% ║ ║ 0 ║ +522% ║ +153% ║
║ sqrt(HALF_UP) ║ 0 ║ +569% ║ +704% ║ ║ 0 ║ +610% ║ +198% ║
║ sqrt(HALF_DOWN) ║ 0 ║ +671% ║ +865% ║ ║ 0 ║ +622% ║ +204% ║
║ sqrt(HALF_EVEN) ║ 0 ║ +675% ║ +861% ║ ║ 0 ║ +632% ║ +208% ║
║ sqrt(UNNECESSARY) ║ 0 ║ +62% ║ +70% ║ ║ 0 ║ +544% ║ +68% ║
╚════════════════════╩════════════╩═════════╩═════════╝ ╚════════════╩══════════╩══════════╝
The BigInt
implementation is accompanied by a custom test framework that provides:
-
Assert correctness of function results
The results of each test are asserted to be equal amongst all test subjects. -
Compare runtime performance
The custom test framework provides a mechanism for the isolation of a specific method to be tested. The actual tests are thus written to compare the feature-equivalent contextually appropriate function that is intended to be benchmarked. -
Compare heap memory allocation
The custom test framework detects how many instances of a particular type are created during the execution of a test. This is accomplished with bytecode instrumentation via the Byteman and ByteBuddy instrumentation agents. The instrumentation enables a callback to be invoked after the instantiation of the types:BigInteger
,BigInt
, andint[]
.
The custom test framework achieves (2) and (3) with a "phased execution" approach, whereby the runtime performance is evaluated first, afterwhich the runtime is instrumented for the evaluation of the heap memory allocations in a repeated execution. It is necessary to evaluate the runtime performance before instrumentation, because instrumentation adds significant overhead to the runtime.
The following section provides benchmarks that compare the performance of BigInteger
, BigInt
, and bare int[]
value-encoded number arrays.
The benchmark results provide runtime and memory heap allocation performance comparisons in the following kinds of tables and charts:
- Summary of runtime performance
This table shows the relative performance of each function tested in the relevant test class (BigIntAdditionTest
,BigIntMultiplicationTest
, etc.). The values in this table represent the "percent less time spent in the function being tested", as compared to the baseline, which is the test subject (BigInteger
,BigInt
, orint[]
) containing the0
value (i.e. that test subject spends 0% less time than the baseline (itself) for the test).
- Summary of heap memory allocation
This table shows the relative heap allocation performance of each function tested in the relevant test class (BigIntAdditionTest
,BigIntMultiplicationTest
, etc.). The values in this table represent the "percent less number of instances allocated on the heap during the execution of the function being tested", as compared to the baseline, which is the test subject (BigInteger
,BigInt
, orint[]
) containing the0
value (i.e. that test subject allocates 0% less instances than the baseline (itself) for the test). The columns for heap allocation results provide 2 values:T
andint[]
. Here,T
represents the type of the test subject (BigInteger
,BigInt
, orint[]
), andint[]
represents theint[]
type itself, as bothBigInteger
andBigInt
useint[]
as the underlying representation of the number's magnitude. Therefore, for bareint[]
value-encoded number arrays,T
andint[]
refer to the same thing.
- Detailed runtime performance
This table provides a detailed view of the runtime performance test results as delineated by the precision of the input(s) being tested. This table shows 3 meta-columns:
a. Length: The length of the underlyingint[]
(i.e.int[].length
).
b. Precision: The precision (number of digits) of the number -- positive precision is for positive values, and negative precision is for negative values.
c. Count: Number of tests that were run.
The values in the columns of the test subject (BigInteger
,BigInt
, orint[]
) represent the mean time spent (nanoseconds) in the test method. If the test method being tested applies to one input (likeBigInteger.abs()
, where the one input isthis
), then the table will provide a single mean time spent value. If the test method being tested applies to two inputs (likeBigInteger.add(BigInteger)
, where the first input isthis
and the second is some otherBigInteger
instance), then the table will provide two mean time spent values -- one for each input. This feature of the test framework allows one to more easily identify performance discrepancies between "when a particular value of a particular precision is the first argument vs if it's the second argument".
The table provides 2 additional rows at the bottom:
a. Sum: The sum of the mean time spent in all precision sections.
a. +%: The relative improvement in performance, as represented by the "percent less time spent in the function being tested" by evaluating the sum mean time spent in all precision sections. Like in the table for the Summary of runtime performance, this percentage is compared to the baseline, which is the test subject (BigInteger
,BigInt
, orint[]
) containing the0
value (i.e. that test subject spends 0% less time than the baseline (itself) for the test).
- Runtime performance chart
This chart provides a visual representation of the Detailed runtime performance table. Note that the y values are inverted, whereby the higher y values in the graph are better than lower.
- Detailed heap memory alloction
This table provides a detailed view of the heap memory alloction test results as delineated by the precision of the input(s) being tested. This table shows 3 meta-columns:
a. Length: The length of the underlyingint[]
(i.e.int[].length
).
b. Precision: The precision (number of digits) of the number -- positive precision is for positive values, and negative precision is for negative values.
c. Count: Number of tests that were run.
The values in the columns of the test subject (BigInteger
,BigInt
, orint[]
) represent the total number of instances allocated during the execution of the tested function. Like in the table for the Summary of heap memory allocation, the columns for heap allocation results provide 2 values:T
andint[]
. Here,T
represents the type of the test subject (BigInteger
,BigInt
, orint[]
), andint[]
represents theint[]
type itself, as bothBigInteger
andBigInt
useint[]
as the underlying representation of the number's magnitude. Therefore, for bareint[]
value-encoded number arrays,T
andint[]
refer to the same thing.
The table provides 2 additional rows at the bottom:
a. Sum: The sum of the total number of instances in all precision sections.
a. +%: The relative improvement in performance, as represented by the "percent less number of instances allocated on the heap during the execution of the function being tested" by evaluating the sum total number of instances in all precision sections. Like in the table for the Summary of heap memory allocation, this percentage is compared to the baseline, which is the test subject (BigInteger
,BigInt
, orint[]
) containing the0
value (i.e. that test subject allocates 0% less instances than the baseline (itself) for the test). Similarly, the columns for heap allocation results provide 2 values:T
andint[]
. Here,T
represents the type of the test subject (BigInteger
,BigInt
, orint[]
), andint[]
represents theint[]
type itself, as bothBigInteger
andBigInt
useint[]
as the underlying representation of the number's magnitude. Therefore, for bareint[]
value-encoded number arrays,T
andint[]
refer to the same thing.
- All tests were run with Java 1.8.0_231 on Mac OS 10.15.6.
- All tests were run with
-Xcomp
argument, for precompilation of JIT optimizations. - All tests were run with Critical Native bindings loaded, which automatically happens when
-Xcomp
is present on the JVM argument list.
Benchmark tests are organized in 8 test classes, each responsible for its context of functions:
-
Is
BigInt
error free?With respect to the correctness of its algorithms,
BigInt
has a custom test framework specifically designed to test the full breadth and adjustable depth of inputs into its algorithms. The test framework separates test inputs into "special" and "random". Special inputs are those that involve numbers representing special edges insofar as the number'sint
encoding, or the result of operations leading to propagating carrys. Random inputs are generated on a "breadth first" basis with respect to the input's decimal precision, allowing the depth (testing of more random values for a single precision) to be adjustable for the purposes of development vs comprehensive analysis.With respect to exception checking, the
BigInt
does not preemptively check for exceptions. If a programmer divides by zero he has only himself to blame. And, it is ok to have undefined behavior. -
What is
BigInt
's biggest advantage?BigInt
was created for one specific purpose: to lower the heap memory allocations for regular arithmetic operations.BigInteger
liberally creates transient instances for the purpose of calculations, which results in a significant memory load and subsequent runtime performance load when the GC turns on. This situation is particularly relevant in applications that work with very many instances of arbitrary precision numbers. An example of such an application is an Asset Trading Systems (Level 3) that consumes live streams of order data. Arbitrary precision arithmetic is necessary for Asset Trading Systems in lieu of the need for fixed point arithmetic.See
Decimal
. -
What is
BigInt
's biggest disadvantage?Though
BigInt
outperformsBigInteger
in most operations, there are a few in which it is lacking. One particular operation that is important to note ismul(T)
(i.e. multiplication of an arbitrary precision number by another arbitrary precision number).BigInt
'smul(T)
cannot match the performance ofBigInteger
'smultiply(T)
for "medium-sized numbers", becauseBigInteger
'smultiply(T)
is implemented as an intrinsic function.BigInt
comes close to this performance with its Critical Native implementation of its multiplication algorithms, but it is still not as fast. Nonetheless, whereBigInt
loses in isolated runtime performance, it gains back with its superior ability in reducing unnecessary heap memory allocation for transient calculations.Please refer to Multiplication for benchmark statistics.
-
What is Critical Native JNI?
Calling a JNI method from Java is rather expensive compared to a simple C function call. Specifically when dealing with arrays, the JNI architecture necessitates expensive operations to convert Java arrays into "critical" native arrays and back. By converting a Java array to a "critical" native array, the JVM is notified to disallow the GC from freeing the respective memory. This overhead is significant, and all but disqualifies JNI as an optimization for algorithms that work with arrays.
The JDK has a private API called Critical Native to reduce the overhead function calls that do not require much JNI functionality. This feature was designed for internal use in the JDK. There is no public specification, and the only documentation you may find is in the comments to JDK-7013347.
For
BigInt
, the use of Critical Native JNI results in ~25% faster performance invoking JNI functions.For further information about Critical Native JNI, please refer to this StackOverflow post.
A 64-bit precision decimal alternative to java.math.BigDecimal
, with the following differences:
- Avoids Heap Allocation: The
Decimal
represents a fixed-point number inside along
primitive, thus limiting the precision of aDecimal
-encoded number to 64 bits, and eliminating the cost of heap allocation entirely. Please refer to Long Encoding for further details onDecimal
's 64-bit Precision. - Support for "object-less" operation: All methods in
Decimal
are available in static form, allowing applications to work solely withlong
-encoded decimals. - Mutable:
Decimal
objects are mutable, allowing for reuse of allocated references. - Faster arithmetic: In lieu of
Decimal
's 64-bit precision limit, many arithmetic operations are faster due to inherent termination thresholds. Similarly, however,Decimal
's 64-bit precision limit also demands it to perform precision and scale optimization on each operation, thus resulting in some arithmetic operations to be more expensive. Please refer to Function Matrix for further details onDecimal
's performance. - Significantly reduced heap allocation:
Decimal
was designed to reduce the number of instances allocated purely for the purpose of transient calculation, and significantly outperformsBigDecimal
with regard to memory and GC load. When utilizinglong
-encoded decimals, memory and GC load is reduced to zero. - No preemptive exception checking:
Decimal
does not preemptively check for exceptions. If a programmer divides by zero he has only himself to blame. And, it is ok to have undefined behavior.
The following matrix provides a comparison of functions offered by BigDecimal
vs Decimal
and long
-encoded numbers. The values in the matrix have the following rules:
- 0: Represents the baseline.
- +###%: Prepresents "percent better* compared to the baseline".
* "Better" means: 'faster' for runtime performance, and 'less instances' for heap memory allocation.
It is also important to note the following:
- These numbers are derived from the detailed benchmark tests that are linked on each row. Please refer to Benchmarks for further information.
- These numbers have an error margin, and should be considered as estimates. It is possible to achieve better precision in the results, but would require significant time running these tests in order to increase the sample size.
- For functions that are not inherently available in the particular subject (i.e. in
BigDecimal
), external utility functions were used to bridge the gap.
A few other points:
Decimal
's 64-bit precision limit demands it to perform precision and scale optimization on each operation, thus resulting in some arithmetic operations to be slower than withBigDecimal
. This effectively means that for each arithmetic operation,Decimal
is also performing a reducedsetScale(scale)
adjustment to ensure its number can fit in along
-encoded number.- Operations on
long
-encoded numbers are systematically slower than theirDecimal
equivalents. This is due to the need for thevalue
andscale
to be first decoded from along
-encoded number prior to the arithmetic operation. Similarly, thevalue
andscale
of the result of the operation need to be then re-encoded intolong
-encoded number. Though these overhead operations are miniscule, they still do reduce the overall runtime perform of algorithms onlong
-encoded decimals. - The use of the
∞
symbol in Heap Allocation stats is due to zero heap allocation for algorithms onlong
-encoded decimals.
Note: The benchmark results in the following table show arithmetic algorithms specified with a value in parentheses that ranges from 0
(or 3
) to 16
. This value represents the number of bits reserved for the scale in the long
-encoded number representation. Please refer to Long Encoding for further details.
Runtime Performance Heap Allocation
╔═════════════════╦════════════╦══════════╦══════════╗ ╔════════════╦═════════╦═════════╗
║ ║ BigDecimal ║ Decimal ║ long ║ ║ BigDecimal ║ Decimal ║ long ║
╠═════════════════╬════════════╬══════════╬══════════╣ ╠════════════╬═════════╬═════════╣
║ sub(3) ║ +103% ║ 0 ║ +3% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(4) ║ +95% ║ 0 ║ +2% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(5) ║ +62% ║ 0 ║ +6% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(6) ║ +17% ║ 0 ║ +10% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(7) ║ 0 ║ +17% ║ +28% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(8) ║ 0 ║ +43% ║ +56% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(9) ║ 0 ║ +153% ║ +176% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(10) ║ 0 ║ +393% ║ +448% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(11) ║ 0 ║ +783% ║ +879% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(12) ║ 0 ║ +1756% ║ +1953% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(13) ║ 0 ║ +5242% ║ +5656% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(14) ║ 0 ║ +15717% ║ +16045% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(15) ║ 0 ║ +38562% ║ +35384% ║ ║ 0 ║ +50% ║ ∞ ║
║ sub(16) ║ 0 ║ +89016% ║ +76773% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(3) ║ +171% ║ +52% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ add(4) ║ +156% ║ +48% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ add(5) ║ +90% ║ +38% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ add(6) ║ +28% ║ +37% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ add(7) ║ 0 ║ +50% ║ +4% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(8) ║ 0 ║ +97% ║ +30% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(9) ║ 0 ║ +247% ║ +130% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(10) ║ 0 ║ +641% ║ +375% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(11) ║ 0 ║ +1242% ║ +748% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(12) ║ 0 ║ +2765% ║ +1706% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(13) ║ 0 ║ +7919% ║ +5039% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(14) ║ 0 ║ +23627% ║ +14658% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(15) ║ 0 ║ +61436% ║ +37517% ║ ║ 0 ║ +50% ║ ∞ ║
║ add(16) ║ 0 ║ +136900% ║ +81121% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(3) ║ 0 ║ +94% ║ +80% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(4) ║ 0 ║ +87% ║ +74% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(5) ║ 0 ║ +90% ║ +80% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(6) ║ 0 ║ +93% ║ +87% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(7) ║ 0 ║ +91% ║ +90% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(8) ║ 0 ║ +90% ║ +89% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(9) ║ 0 ║ +91% ║ +91% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(10) ║ 0 ║ +92% ║ +94% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(11) ║ 0 ║ +90% ║ +90% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(12) ║ 0 ║ +89% ║ +91% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(13) ║ 0 ║ +89% ║ +91% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(14) ║ 0 ║ +90% ║ +90% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(15) ║ 0 ║ +85% ║ +85% ║ ║ 0 ║ +50% ║ ∞ ║
║ div(16) ║ 0 ║ +82% ║ +81% ║ ║ 0 ║ +50% ║ ∞ ║
║ rem(3) ║ 0 ║ +848% ║ +497% ║ ║ 0 ║ +172% ║ ∞ ║
║ rem(4) ║ 0 ║ +754% ║ +476% ║ ║ 0 ║ +168% ║ ∞ ║
║ rem(5) ║ 0 ║ +614% ║ +423% ║ ║ 0 ║ +168% ║ ∞ ║
║ rem(6) ║ 0 ║ +803% ║ +633% ║ ║ 0 ║ +172% ║ ∞ ║
║ rem(7) ║ 0 ║ +1369% ║ +1107% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(8) ║ 0 ║ +1969% ║ +1595% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(9) ║ 0 ║ +2441% ║ +1929% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(10) ║ 0 ║ +2754% ║ +2165% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(11) ║ 0 ║ +3003% ║ +2306% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(12) ║ 0 ║ +3116% ║ +2351% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(13) ║ 0 ║ +3187% ║ +2383% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(14) ║ 0 ║ +3191% ║ +2440% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(15) ║ 0 ║ +3285% ║ +2416% ║ ║ 0 ║ +177% ║ ∞ ║
║ rem(16) ║ 0 ║ +3306% ║ +2419% ║ ║ 0 ║ +177% ║ ∞ ║
║ mul(3) ║ +78% ║ +11% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(4) ║ +86% ║ +16% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(5) ║ +93% ║ +19% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(6) ║ +96% ║ +19% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(7) ║ +92% ║ +22% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(8) ║ +91% ║ +24% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(9) ║ +95% ║ +26% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(10) ║ +95% ║ +26% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(11) ║ +102% ║ +31% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(12) ║ +103% ║ +33% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(13) ║ +105% ║ +34% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(14) ║ +102% ║ +40% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(15) ║ +104% ║ +43% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ mul(16) ║ +113% ║ +41% ║ 0 ║ ║ 0 ║ +50% ║ ∞ ║
║ eq(3) ║ +55% ║ 0 ║ +143% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(4) ║ +38% ║ 0 ║ +117% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(5) ║ +63% ║ 0 ║ +162% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(6) ║ +61% ║ 0 ║ +146% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(7) ║ +57% ║ 0 ║ +119% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(8) ║ +64% ║ 0 ║ +140% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(9) ║ +59% ║ 0 ║ +139% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(10) ║ +67% ║ 0 ║ +145% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(11) ║ +61% ║ 0 ║ +134% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(12) ║ +49% ║ 0 ║ +116% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(13) ║ +63% ║ 0 ║ +126% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(14) ║ +56% ║ 0 ║ +125% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(15) ║ +65% ║ 0 ║ +142% ║ ║ 0 ║ 0 ║ 0 ║
║ eq(16) ║ +64% ║ 0 ║ +140% ║ ║ 0 ║ 0 ║ 0 ║
║ setScale(3) ║ 0 ║ +326% ║ +264% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(4) ║ 0 ║ +342% ║ +279% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(5) ║ 0 ║ +298% ║ +268% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(6) ║ 0 ║ +291% ║ +275% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(7) ║ 0 ║ +313% ║ +281% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(8) ║ 0 ║ +292% ║ +270% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(9) ║ 0 ║ +484% ║ +450% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(10) ║ 0 ║ +1696% ║ +1596% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(11) ║ 0 ║ +3300% ║ +3203% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(12) ║ 0 ║ +6336% ║ +6336% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(13) ║ 0 ║ +16051% ║ +16314% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(14) ║ 0 ║ +49593% ║ +51742% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(15) ║ 0 ║ +140003% ║ +143240% ║ ║ 0 ║ +100% ║ ∞ ║
║ setScale(16) ║ 0 ║ +284765% ║ +296303% ║ ║ 0 ║ +100% ║ ∞ ║
║ compare(3) ║ +56% ║ +156% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(4) ║ +45% ║ +150% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(5) ║ +38% ║ +141% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(6) ║ +32% ║ +154% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(7) ║ +28% ║ +158% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(8) ║ +25% ║ +171% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(9) ║ +24% ║ +175% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(10) ║ +23% ║ +175% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(11) ║ +23% ║ +172% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(12) ║ +26% ║ +171% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(13) ║ +25% ║ +171% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(14) ║ +23% ║ +170% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(15) ║ +28% ║ +165% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ compare(16) ║ +26% ║ +166% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(3) ║ +59% ║ +128% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(4) ║ +48% ║ +131% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(5) ║ +49% ║ +137% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(6) ║ +49% ║ +136% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(7) ║ +51% ║ +144% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(8) ║ +52% ║ +144% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(9) ║ +49% ║ +136% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(10) ║ +50% ║ +147% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(11) ║ +52% ║ +147% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(12) ║ +52% ║ +142% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(13) ║ +49% ║ +131% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(14) ║ +50% ║ +129% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(15) ║ +52% ║ +136% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lt(16) ║ +52% ║ +139% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(3) ║ +52% ║ +115% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(4) ║ +47% ║ +135% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(5) ║ +45% ║ +127% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(6) ║ +46% ║ +134% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(7) ║ +43% ║ +131% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(8) ║ +47% ║ +137% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(9) ║ +46% ║ +144% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(10) ║ +44% ║ +132% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(11) ║ +45% ║ +128% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(12) ║ +45% ║ +131% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(13) ║ +46% ║ +131% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(14) ║ +45% ║ +140% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(15) ║ +42% ║ +124% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gt(16) ║ +46% ║ +125% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(3) ║ +97% ║ +191% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(4) ║ +88% ║ +193% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(5) ║ +82% ║ +182% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(6) ║ +83% ║ +192% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(7) ║ +82% ║ +196% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(8) ║ +82% ║ +182% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(9) ║ +88% ║ +203% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(10) ║ +85% ║ +202% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(11) ║ +82% ║ +149% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(12) ║ +77% ║ +163% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(13) ║ +79% ║ +168% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(14) ║ +82% ║ +175% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(15) ║ +83% ║ +180% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ lte(16) ║ +81% ║ +184% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(3) ║ +72% ║ +160% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(4) ║ +65% ║ +172% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(5) ║ +66% ║ +182% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(6) ║ +64% ║ +176% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(7) ║ +66% ║ +178% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(8) ║ +68% ║ +187% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(9) ║ +67% ║ +187% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(10) ║ +66% ║ +172% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(11) ║ +69% ║ +179% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(12) ║ +68% ║ +181% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(13) ║ +66% ║ +173% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(14) ║ +66% ║ +176% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(15) ║ +70% ║ +188% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ gte(16) ║ +63% ║ +155% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(3) ║ +47% ║ +158% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(4) ║ +43% ║ +147% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(5) ║ +47% ║ +151% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(6) ║ +40% ║ +143% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(7) ║ +35% ║ +138% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(8) ║ +28% ║ +114% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(9) ║ +32% ║ +119% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(10) ║ +29% ║ +118% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(11) ║ +25% ║ +109% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(12) ║ +34% ║ +123% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(13) ║ +37% ║ +150% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(14) ║ +34% ║ +144% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(15) ║ +35% ║ +149% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ max(16) ║ +34% ║ +141% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(3) ║ +161% ║ +291% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(4) ║ +179% ║ +313% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(5) ║ +172% ║ +312% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(6) ║ +131% ║ +247% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(7) ║ +152% ║ +286% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(8) ║ +157% ║ +284% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(9) ║ +129% ║ +255% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(10) ║ +140% ║ +272% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(11) ║ +153% ║ +276% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(12) ║ +132% ║ +261% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(13) ║ +138% ║ +261% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(14) ║ +130% ║ +248% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(15) ║ +132% ║ +241% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ min(16) ║ +138% ║ +241% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ precision(3) ║ 0 ║ +26% ║ +27% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(4) ║ 0 ║ +30% ║ +28% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(5) ║ 0 ║ +26% ║ +27% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(6) ║ 0 ║ +34% ║ +34% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(7) ║ 0 ║ +32% ║ +33% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(8) ║ 0 ║ +33% ║ +29% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(9) ║ 0 ║ +27% ║ +23% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(10) ║ 0 ║ +38% ║ +38% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(11) ║ 0 ║ +30% ║ +33% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(12) ║ 0 ║ +24% ║ +25% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(13) ║ 0 ║ +26% ║ +25% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(14) ║ 0 ║ +26% ║ +25% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(15) ║ 0 ║ +21% ║ +22% ║ ║ 0 ║ 0 ║ 0 ║
║ precision(16) ║ 0 ║ +24% ║ +23% ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(3) ║ +77% ║ +97% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(4) ║ +91% ║ +114% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(5) ║ +87% ║ +68% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(6) ║ +71% ║ +75% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(7) ║ +77% ║ +93% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(8) ║ +69% ║ +85% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(9) ║ +92% ║ +64% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(10) ║ +88% ║ +76% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(11) ║ +90% ║ +112% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(12) ║ +82% ║ +112% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(13) ║ +89% ║ +107% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(14) ║ +90% ║ +108% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(15) ║ +96% ║ +114% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ hashCode(16) ║ +81% ║ +108% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(3) ║ +134% ║ 0 ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(4) ║ +129% ║ +2% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(5) ║ +154% ║ +4% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(6) ║ +134% ║ +4% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(7) ║ +131% ║ 0 ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(8) ║ +150% ║ +3% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(9) ║ +123% ║ +9% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(10) ║ +144% ║ +13% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(11) ║ +103% ║ +7% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(12) ║ +146% ║ +8% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(13) ║ +163% ║ +17% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(14) ║ +130% ║ +8% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(15) ║ +151% ║ +12% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ scale(16) ║ +150% ║ +10% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(3) ║ +19% ║ +35% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(4) ║ +19% ║ +37% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(5) ║ +21% ║ +43% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(6) ║ +17% ║ +34% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(7) ║ +16% ║ +31% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(8) ║ +19% ║ +32% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(9) ║ +14% ║ +28% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(10) ║ +16% ║ +23% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(11) ║ +22% ║ +45% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(12) ║ +17% ║ +31% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(13) ║ +20% ║ +37% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(14) ║ +20% ║ +36% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(15) ║ +20% ║ +37% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ signum(16) ║ +16% ║ +28% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(3) ║ 0 ║ +128% ║ +87% ║ ║ 0 ║ +81% ║ ∞ ║
║ byteValue(4) ║ 0 ║ +143% ║ +99% ║ ║ 0 ║ +81% ║ ∞ ║
║ byteValue(5) ║ 0 ║ +108% ║ +88% ║ ║ 0 ║ +81% ║ ∞ ║
║ byteValue(6) ║ 0 ║ +68% ║ +57% ║ ║ 0 ║ +81% ║ ∞ ║
║ byteValue(7) ║ 0 ║ +44% ║ +34% ║ ║ 0 ║ +54% ║ ∞ ║
║ byteValue(8) ║ +17% ║ +5% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(9) ║ +70% ║ +4% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(10) ║ +130% ║ 0 ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(11) ║ +243% ║ 0 ║ +7% ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(12) ║ +418% ║ 0 ║ +13% ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(13) ║ +790% ║ 0 ║ +12% ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(14) ║ +1360% ║ 0 ║ +10% ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(15) ║ +2634% ║ 0 ║ +11% ║ ║ 0 ║ 0 ║ 0 ║
║ byteValue(16) ║ +5078% ║ 0 ║ +12% ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(3) ║ 0 ║ +132% ║ +91% ║ ║ 0 ║ +81% ║ ∞ ║
║ shortValue(4) ║ 0 ║ +130% ║ +90% ║ ║ 0 ║ +81% ║ ∞ ║
║ shortValue(5) ║ 0 ║ +99% ║ +82% ║ ║ 0 ║ +90% ║ ∞ ║
║ shortValue(6) ║ 0 ║ +68% ║ +59% ║ ║ 0 ║ +72% ║ ∞ ║
║ shortValue(7) ║ 0 ║ +47% ║ +40% ║ ║ 0 ║ +81% ║ ∞ ║
║ shortValue(8) ║ +13% ║ +4% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(9) ║ +55% ║ +1% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(10) ║ +118% ║ 0 ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(11) ║ +224% ║ 0 ║ +6% ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(12) ║ +408% ║ 0 ║ +13% ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(13) ║ +745% ║ 0 ║ +10% ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(14) ║ +1302% ║ 0 ║ +9% ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(15) ║ +2473% ║ 0 ║ +9% ║ ║ 0 ║ 0 ║ 0 ║
║ shortValue(16) ║ +4433% ║ 0 ║ +8% ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(3) ║ 0 ║ +159% ║ +111% ║ ║ 0 ║ +81% ║ ∞ ║
║ intValue(4) ║ 0 ║ +168% ║ +125% ║ ║ 0 ║ +81% ║ ∞ ║
║ intValue(5) ║ 0 ║ +122% ║ +98% ║ ║ 0 ║ +81% ║ ∞ ║
║ intValue(6) ║ 0 ║ +60% ║ +49% ║ ║ 0 ║ +72% ║ ∞ ║
║ intValue(7) ║ 0 ║ +39% ║ +32% ║ ║ 0 ║ +63% ║ ∞ ║
║ intValue(8) ║ +12% ║ +5% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(9) ║ +57% ║ +4% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(10) ║ +102% ║ 0 ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(11) ║ +179% ║ 0 ║ +5% ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(12) ║ +332% ║ 0 ║ +11% ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(13) ║ +603% ║ 0 ║ +8% ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(14) ║ +1128% ║ 0 ║ +8% ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(15) ║ +2168% ║ 0 ║ +8% ║ ║ 0 ║ 0 ║ 0 ║
║ intValue(16) ║ +3953% ║ 0 ║ +7% ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(3) ║ 0 ║ +296% ║ +109% ║ ║ 0 ║ +81% ║ ∞ ║
║ longValue(4) ║ 0 ║ +284% ║ +111% ║ ║ 0 ║ +81% ║ ∞ ║
║ longValue(5) ║ 0 ║ +210% ║ +101% ║ ║ 0 ║ +81% ║ ∞ ║
║ longValue(6) ║ 0 ║ +140% ║ +75% ║ ║ 0 ║ +72% ║ ∞ ║
║ longValue(7) ║ 0 ║ +106% ║ +58% ║ ║ 0 ║ +54% ║ ∞ ║
║ longValue(8) ║ 0 ║ +53% ║ +22% ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(9) ║ 0 ║ +24% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(10) ║ +17% ║ +20% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(11) ║ +52% ║ +7% ║ 0 ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(12) ║ +100% ║ 0 ║ +1% ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(13) ║ +234% ║ 0 ║ +2% ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(14) ║ +441% ║ 0 ║ +4% ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(15) ║ +872% ║ 0 ║ +6% ║ ║ 0 ║ 0 ║ 0 ║
║ longValue(16) ║ +1568% ║ 0 ║ +5% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(3) ║ 0 ║ +653% ║ +656% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(4) ║ 0 ║ +820% ║ +777% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(5) ║ 0 ║ +976% ║ +941% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(6) ║ 0 ║ +1149% ║ +1085% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(7) ║ 0 ║ +989% ║ +985% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(8) ║ 0 ║ +756% ║ +841% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(9) ║ 0 ║ +626% ║ +746% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(10) ║ 0 ║ +586% ║ +718% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(11) ║ 0 ║ +577% ║ +713% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(12) ║ 0 ║ +578% ║ +743% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(13) ║ 0 ║ +585% ║ +739% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(14) ║ 0 ║ +539% ║ +705% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(15) ║ 0 ║ +484% ║ +720% ║ ║ 0 ║ 0 ║ 0 ║
║ floatValue(16) ║ 0 ║ +387% ║ +701% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(3) ║ 0 ║ +31% ║ +24% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(4) ║ 0 ║ +31% ║ +17% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(5) ║ 0 ║ +25% ║ +18% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(6) ║ 0 ║ +40% ║ +45% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(7) ║ 0 ║ +41% ║ +58% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(8) ║ 0 ║ +38% ║ +59% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(9) ║ 0 ║ +34% ║ +54% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(10) ║ 0 ║ +61% ║ +86% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(11) ║ 0 ║ +124% ║ +151% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(12) ║ 0 ║ +218% ║ +243% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(13) ║ 0 ║ +339% ║ +340% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(14) ║ 0 ║ +465% ║ +421% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(15) ║ 0 ║ +583% ║ +494% ║ ║ 0 ║ 0 ║ 0 ║
║ doubleValue(16) ║ 0 ║ +645% ║ +527% ║ ║ 0 ║ 0 ║ 0 ║
║ toBigInt(3) ║ 0 ║ +65% ║ +34% ║ ║ +72% ║ +40% ║ 0 ║
║ toBigInt(4) ║ 0 ║ +77% ║ +37% ║ ║ +72% ║ +40% ║ 0 ║
║ toBigInt(5) ║ 0 ║ +75% ║ +36% ║ ║ +72% ║ +45% ║ 0 ║
║ toBigInt(6) ║ 0 ║ +85% ║ +72% ║ ║ +60% ║ +40% ║ 0 ║
║ toBigInt(7) ║ 0 ║ +104% ║ +98% ║ ║ +60% ║ +36% ║ 0 ║
║ toBigInt(8) ║ 0 ║ +111% ║ +110% ║ ║ +50% ║ +27% ║ 0 ║
║ toBigInt(9) ║ 0 ║ +101% ║ +110% ║ ║ +59% ║ +18% ║ 0 ║
║ toBigInt(10) ║ 0 ║ +600% ║ +733% ║ ║ 0 ║ +117% ║ +172% ║
║ toBigInt(11) ║ 0 ║ +923% ║ +1251% ║ ║ 0 ║ +220% ║ +404% ║
║ toBigInt(12) ║ 0 ║ +1214% ║ +1710% ║ ║ 0 ║ +253% ║ +480% ║
║ toBigInt(13) ║ 0 ║ +2079% ║ +2773% ║ ║ 0 ║ +365% ║ +704% ║
║ toBigInt(14) ║ 0 ║ +4261% ║ +5128% ║ ║ 0 ║ +1105% ║ +2166% ║
║ toBigInt(15) ║ 0 ║ +7637% ║ +8651% ║ ║ 0 ║ +3441% ║ +6828% ║
║ toBigInt(16) ║ 0 ║ +9088% ║ +12775% ║ ║ 0 ║ +7458% ║ +14835% ║
╚═════════════════╩════════════╩══════════╩══════════╝ ╚════════════╩═════════╩═════════╝
The representation of a decimal value in Decimal
encoding is achieved with the following model:
To represent a value such as 1234567.89
, a Decimal
-encoded long
has two numbers inside it:
value
This is a variable range value (maximum oflong
) representing the full unscaled precision (all significant digits), i.e.:
123456789
scale
This is a variable range value (maximum ofshort
) representing the position of the decimal point in the unscaledvalue
, i.e.:
2
A decimal represented with Decimal
encoding can be reconstituted with value
and scale
by multiplying the value
by ten to the power of the negation of the scale
(value ✕ 10⁻ˢᶜᵃˡᵉ
), i.e.:
123456789 ✕ 10⁻² = 1234567.89
To maximize precision, the Decimal
encoding implements a variable range for the scale
. The variable range is defined by a scaleBits
variable representing the number of bits inside the 64-bit long
that are reserved for the signed representation of the scale
. This effectively means that the variable range of value
is 64 - bits
.
Since both value
and scale
are signed, one bit is reserved for the sign in each. The following table provides sample value
and scale
ranges for various scale bits
values:
For example, consider the following table with scale scaleBits
as the variable:
scaleBits [ 0 , 16 ] |
Scale range [ -2ᵇⁱᵗˢ⁻¹ , 2ᵇⁱᵗˢ⁻¹ - 1 ] |
valueBits 64-bits |
Value range [ -2⁶³⁻ᵇⁱᵗˢ , 2⁶³⁻ᵇⁱᵗˢ - 1 ] |
Example |
---|---|---|---|---|
0 |
[0 , 0 ] |
64 |
[-2⁶³ , 2⁶³ - 1 ] |
Long.MAX_VALUE |
1 |
[0 , 0 ] |
63 |
[-2⁶² , 2⁶² - 1 ] |
4611686018427387904 |
2 |
[-1 , 0 ] |
62 |
[-2⁶¹ , 2⁶¹ - 1 ] |
2305843009213693952E1 |
3 |
[-2 , 1 ] |
61 |
[-2⁶⁰ , 2⁶⁰ - 1 ] |
11529215046068469.76 |
4 |
[-8 , 7 ] |
60 |
[-2⁵⁹ , 2⁵⁹ - 1 ] |
57646075230.3423488 |
8 |
[-128 , 127 ] |
56 |
[-2⁵⁵ , 2⁵⁵ - 1 ] |
3.6028797018963968E111 |
16 |
[-32768 , 32767 ] |
48 |
[-2⁴⁷ , 2⁴⁷ - 1 ] |
1.40737488355328E−32768 |
Technically, scaleBits
below 3
are not very useful. Therefore, Decimal
officially supports scaleBits
values between 3
and 16
with its unit tests. Some unit tests also test for scaleBits
values of 0
, 1
and 2
, but this is supplementary.
The following illustrates the way Decimal
encodes the value
and scale
inside a long
primitive:
scale sign bit (for scaleBits > 0)
/
1
.---+---+---- // --------+------------------------------ // -------------------------------.
| | ' | |
| | ' scale | value |
| | ' | |
'---+---+---- // --------+----------------------------- // --------------------------------'
0 [1, scaleBits+1] [scaleBits+1, 63-scaleBits]
\
value sign bit
- All tests were run with Java 1.8.0_231 on Mac OS 10.15.6.
- All tests were run with
-Xcomp
argument, for precompilation of JIT optimizations.
Benchmark tests are organized in 8 test classes, each responsible for its context of functions:
Link to results | Link to test code |
---|---|
Addition and subtraction | DecimalAdditionTest |
Multiplication | DecimalMultiplicationTest |
Division | DecimalDivisionTest |
Predicate functions | DecimalPredicateTest |
-
Is
Decimal
error free?The development of
Decimal
utilized the same test framework as that ofBigInt
. The test framework is specifically designed to test the full breadth and adjustable depth of inputs intoDecimal
's algorithms. The test framework separates test inputs into "special" and "random". Special inputs are those that involve numbers representing special edges insofar as the number'slong
encoding, or the result of operations leading to propagating carrys of remainders. Random inputs are generated on a "breadth first" basis with respect to the input's decimal precision, allowing the depth (testing of more random values for a single precision) to be adjustable for the purposes of development vs comprehensive analysis. -
What is
Decimal
's biggest advantage?Decimal
was created for one specific purpose: to lower the heap memory allocations for regular arithmetic operations. It achieves this with its single object allocation count in case ofDecimal
instances, or zero object allocation count in case oflong
-encoded numbers. In comparison,BigDecimal
utilizedBigInteger
algorithms that liberally creates transient instances for the purpose of calculations, which results in a significant memory load and subsequent runtime performance load when the GC turns on. This situation is particularly relevant in applications that work with very many fixed-point numbers. An example of such an application is an Asset Trading Systems (Level 3) that consumes live streams of order data. Arbitrary precision arithmetic is necessary for Asset Trading Systems in lieu of the need for fixed point arithmetic. -
What is
Decimal
's biggest disadvantage?Though
Decimal
outperformsBigDecimal
in many operations, there are a those in which it is lacking. One particular operation that is important to note ismul(T)
.Decimal
utilizesBigInt
'smul(T)
, which cannot match the performance ofBigInteger
'smultiply(T)
for "medium-sized numbers", becauseBigInteger
'smultiply(T)
is implemented as an intrinsic function.BigInt
comes close to this performance with its Critical Native implementation of its multiplication algorithms, but it is still not as fast. Nonetheless, whereBigInt
loses in isolated runtime performance, it gains back with its superior ability in reducing unnecessary heap memory allocation for transient calculations.Please refer to
BigInt
's Multiplication for benchmark statistics.
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
This project is licensed under the MIT License - see the LICENSE.txt file for details.