问题
I need to represent some numbers in Java with perfect precision and fixed number of decimal points after decimal point; after that decimal point, I don't care. (More concretely - money and percentages.)
I used Java's own BigDecimal now, but I found out, that it's really slow and it starts to show in my application.
So I want to solve it with a "regular" integers and a fixed-point arithmetics (long integers have big enough precision for my purposes).
Now, I would think that I am not the first one who has this kind of problem and there would be already a library for that, that already has multiplication/division implemented - but it seems that it isn't.
Now, I very probably can write it myself (and I probably will), but really, am I really the first person that needs this? Isn't there already some library for that?
回答1:
Are you completely sure BigDecimal is the performance problem? Did you use a profiler to find out? If yes, two options that could help are:
1) Use long
and multiply all values by a factor (for example 100 if you are interested in cents).
2) Use a specially designed class that implements something similar to BigDecimal
, but using long
internally. I don't know if a good open source library exists (maybe the Java Math Fixed Point Library?). I wrote one such class myself quite a long time ago (2001 I believe) for J2ME. It's a bit tricky to get right. Please note BigDecimal
uses a long
internally as well except if high precision is needed, so this solution will only help a tiny bit in most cases.
Using double
isn't a good option in many cases, because of rounding and precision problems.
回答2:
decimal4j is a Java library for fast fixed precision arithmetic based on longs with support for up to 18 decimal places.
Disclaimer: I am involved in the decimal4j project.
回答3:
Although this is not exactly what you are asking about, this can speed up your app without leaving BigDecimal
:
Since Java 8, this is solved by BigDecimal
itself. A new class MathContext was added and limits the precision to which the operations are calculated.
var num = new BigDecimal("1234.56780", new MathContext(10, RoundingMode.DOWN));
The catch is that the precision 10
does not apply to digits after decimal point. It applies to the number of significant digits. For 1234.50
, 6 is needed.
For 1_500_000_000.100
, 13 is needed to keep the number as is.
So the precision might suffer when you had a precision of 10 and counted billions of Czech Korunas.
Still, a precision of, say, 1000, is way faster than unlimited precision (which is I think the default).
This can also be applied to the individual operations:
BigDecimal n = new BigDecimal("0.12345");
n = n.pow(2, new MathContext(1000, RoundingMode.DOWN));
n = n.pow(2, new MathContext(1000, RoundingMode.DOWN));
n = n.pow(2, new MathContext(1000, RoundingMode.DOWN));
n = n.pow(2, new MathContext(1000, RoundingMode.DOWN));
回答4:
Not sure why you need a library for it.
For example, say you want to add two longs with the same fixed precision
long c = a + b;
Say you have a fixed precision number you want to multiple by an integer
long c = a * i;
Say you want to divide a number by a integer rounding to zero
long c = a / i;
Say you want to print a fixed precision number with 3 decimal places.
System.out.println(c / 1e3);
Perhaps you are over thinking the problem and assuming you need a library for everything.
If you are using long
or double
you might want a small number helper methods for rounding, but you don't need a library as such.
来源:https://stackoverflow.com/questions/11916308/fixed-point-arithmetics-in-java-with-fast-performance