8

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?

4
  • 1
    %100 precision? This is infinitive digit number Commented Aug 11, 2012 at 18:03
  • whats your needed digit number after the point? Commented Aug 11, 2012 at 18:04
  • Sorry, 100% was needed figuratively. I will add more info. Commented Aug 11, 2012 at 18:05
  • Possible duplicate of stackoverflow.com/questions/277309/… Commented Feb 22, 2016 at 8:48

4 Answers 4

12

decimal4j is a Java library for fast fixed precision arithmetic based on longs with support for up to 18 decimal places.

Disclosure: I am involved in the decimal4j project.

Sign up to request clarification or add additional context in comments.

Comments

7

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.

8 Comments

+1 All problems can be solved and rounding errors are not random errors and easily solved. IHMO I would use long if that is simpler, otherwise double and BigDecimal if you have really large numbers.
Use a specially designed class that implements something similar to BigDecimal, but using long internally. BigDecimal does exactly that, unless inflated (i.e. long ain't enough).
BigDecimal doesn't use long internally. It uses BigInteger, which in turn uses an int[] internally. That's why BigDecimal is a bit slower than using a long. However, in many (most) cases the performance difference is too small to to be important.
It uses BigInteger, which in turn uses an int[] internally untrue for values that fit long. Read the code again, I claim I know how it works.
/** * If the absolute value of the significand of this BigDecimal is * less than or equal to {@code Long.MAX_VALUE}, the value can be * compactly stored in this field and used in computations. */ private transient long intCompact;
|
0

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));

1 Comment

-2

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.

6 Comments

-1 library would be useful, 3 fraction digits example: 1.234 * 1.234 is 1234 * 1234 = 1522756, you have to divide by 1000, otherwise you get 1522.756 instead of correct 1.522
@Peenut It's so trivial to write a helper class for your applications needs, but I have never bothered, nor have I ever seen it done. Not sure why -1.
let me quote better answer: "It's a bit tricky to get right."
The worst part of this is dealing with arithmetic overflows. Say we need to track 5 decimal places (e.g. to track prices of currency pairs in 1/10 of a pip). Multiplication of two fixed-point numbers (e.g. price*size) can easily lead to overflow with decimal numbers as small as 100K.
Take a serious look at Spire (github.com/non/spire). It requires using Scala, not Java, but it has a really interesting feature set.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.