Last active
July 12, 2022 18:14
-
-
Save hillmanli-seekers/a8ab8e463e7b60989e6d0c0900cf4d93 to your computer and use it in GitHub Desktop.
BigDecimal vs double: illustration of accuracy and performance impact
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.math.BigDecimal; | |
import java.util.function.Consumer; | |
import java.util.function.DoubleSupplier; | |
import java.util.stream.DoubleStream; | |
public class TestDouble { | |
private static double doubleTotal = 0; | |
private static double kahanDoubleTotal = 0; | |
private static BigDecimal bdTotal = BigDecimal.ZERO; | |
public static void main(String[] args) { | |
int iteration = 100000; | |
double[] values = construct(Math::random, iteration); | |
System.out.printf("Time taken for double calculation: %fms%n", time(TestDouble::sumToDouble, values)); | |
System.out.printf("Time taken for Kahan double calculation: %fms%n", time(TestDouble::kahanSumToDouble, values)); | |
System.out.printf("Time taken for BigDecimal calculation: %fms%n", time(TestDouble::sumToBigDecimal, values)); | |
System.out.printf("Value diff for simple sum: %s%n", bdTotal.subtract(BigDecimal.valueOf(doubleTotal)).abs().toString()); | |
System.out.printf("Value diff for Kahan sum: %s%n", bdTotal.subtract(BigDecimal.valueOf(kahanDoubleTotal)).abs().toString()); | |
} | |
private static double time(Consumer<double[]> consumer, double[] values) { | |
long start = System.nanoTime(); | |
consumer.accept(values); | |
long end = System.nanoTime(); | |
return 1.0 * (end - start) / (1000 * 1000); | |
} | |
private static double[] construct(DoubleSupplier supplier, int size) { | |
return DoubleStream | |
.iterate(supplier.getAsDouble(), operand -> supplier.getAsDouble()) | |
.limit(size) | |
.toArray(); | |
} | |
private static void kahanSumToDouble(double[] values) { | |
double approxError = 0; | |
for (double value : values) { | |
double adjustedValue = value - approxError; | |
double adjustedSum = kahanDoubleTotal + adjustedValue; | |
approxError = (adjustedSum - kahanDoubleTotal) - adjustedValue; | |
kahanDoubleTotal = adjustedSum; | |
} | |
} | |
private static void sumToDouble(double[] values) { | |
for (double value : values) { | |
doubleTotal += value; | |
} | |
} | |
private static void sumToBigDecimal(double[] values) { | |
BigDecimal ret = BigDecimal.ZERO; | |
for (double value : values) { | |
ret = ret.add(BigDecimal.valueOf(value)); | |
} | |
bdTotal = ret; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A sample run will produce following result.