13

I'm wondering what the best way to fix precision errors is in Java. As you can see in the following example, there are precision errors:

class FloatTest
{
  public static void main(String[] args)
  {
    Float number1 = 1.89f;
    
    for(int i = 11; i < 800; i*=2)
    {
      System.out.println("loop value: " + i);
      System.out.println(i*number1);
      System.out.println("");
    }
  }
}

The result displayed is:

loop value: 11

20.789999

loop value: 22

41.579998

loop value: 44

83.159996

loop value: 88

166.31999

loop value: 176

332.63998

loop value: 352

665.27997

loop value: 704

1330.5599

Also, if someone can explain why it only does it starting at 11 and doubling the value every time. I think all other values (or many of them at least) displayed the correct result.

Problems like this have caused me headache in the past and I usually use number formatters or put them into a String.

Edit: As people have mentioned, I could use a double, but after trying it, it seems that 1.89 as a double times 792 still outputs an error (the output is 1496.8799999999999).

I guess I'll try the other solutions such as BigDecimal

2
  • both double and float are based on the binary system - simply speaking they are just sum of b1* 1/2 + b2*1/4 + b3*1/8 ... bn/2^n, they will never hold even one tenth precisely as you can't write down one tenth (0.1) in binary with finite number of nonzero digits - sorry for mentioning such obvious things Commented Jul 15, 2011 at 22:25
  • You don't have to use boxed class Commented Jan 10, 2022 at 10:06

9 Answers 9

10

If you really care about precision, you should use BigDecimal

https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/BigDecimal.html

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

Comments

10

The problem is not with Java but with the good standard float's (http://en.wikipedia.org/wiki/IEEE_floating-point_standard).

You can either:

  • use Double and have a bit more precision (but not perfect of course, it also has limited precision)

  • use a arbitrary-precision-library

  • use numerically stable algorithms and truncate/round digits of which you are not sure they are correct (you can calculate numeric precision of operations)

Comments

5

When you print the result of a double operation you need to use appropriate rounding.

System.out.printf("%.2f%n", 1.89 * 792);

prints

1496.88

If you want to round the result to a precision, you can use rounding.

double d = 1.89 * 792;
d = Math.round(d * 100) / 100.0;
System.out.println(d);

prints

1496.88

However if you see below, this prints as expected, as there is a small amount of implied rounding.


It worth nothing that (double) 1.89 is not exactly 1.89 It is a close approximation.

new BigDecimal(double) converts the exact value of double without any implied rounding. It can be useful in finding the exact value of a double.

System.out.println(new BigDecimal(1.89));
System.out.println(new BigDecimal(1496.88));

prints

1.8899999999999999023003738329862244427204132080078125
1496.8800000000001091393642127513885498046875

5 Comments

It works for printing it, but how about assigning it to another variable? Let's say I do: double test = number1*792; it still has the same error.
There is a rounding error most of the time with double. As long as your errors don't accumulate and you use appropriate round when printing it will be correct.
Just a quick comment Math.round(d * 100) / 100.0; 100.0 is important otherwise java will interpret it as int division.
What is the reason for "there is a small amount of implied rounding"?
@tina Java assumes you want to print the shortest/closest decimal number which has the same representation as the double c.f. BigDecimal can show you the actual value.
5

Most of your question has been pretty well covered, though you might still benefit from reading the [floating-point] tag wiki to understand why the other answers work.

However, nobody has addressed "why it only does it starting at 11 and doubling the value every time," so here's the answer to that:

for(int i = 11; i < 800; i*=2)
    ╚═══╤════╝           ╚╤═╝
        │                 └───── "double the value every time"
        │
        └───── "start at 11"

2 Comments

Thanks for the tag wiki link, I'll definitely read up on WHY it works like this. As for the 11 and doubling every time, I know how it works since I'm the one that made the sample code... What I meant is, if I made the usual loop starting at 0 or 1 and incrementing by 1 every time, the results from 0-10 were displaying the correct result without precision loss, from 12-21 same thing. So I made my example like this on purpose to show better what I meant. Sorry if it wasn't clear enough.
Ah, gotcha. I would suspect that, at the smaller values, the precision errors just weren't significant enough to show up in your results.
3

You could use doubles instead of floats

3 Comments

It does not work, try double number1 = 1.89; and then make the output: 792*number1, there's a precision error there.
@Adam floats are 4 bytes (if i remember correctly) and doubles are 8 (depends on the system you are on) gives you more accuracy but takes up more space
@Adam Smith, yes the problem is with the (good) standard (see my answer), the increase in precision is not always visible. As an example see what happens in decimal (base 10) if you represent 1/3 as 0.3 and then double the precision to 0.33 or tripple it to 0.333. The counter-intuitive problem is, that for us who were raised in decimal-land we think it's ok for 1/3 to have inaccurate representation while 1/10 should have precise representation; there are a lot of numbers that have inaccurate representation in finite floating point encoding.
3

If you really need arbitrary precision, use BigDecimal.

Comments

0

first of Float is the wrapper class for the primitive float

and doubles have more precision

but if you only want to calculate down to the second digit (for monetary purposes for example) use an integer (as if you are using cents as unit) and add some scaling logic when you are multiplying/dividing

or if you need arbitrary precision use BigDecimal

1 Comment

I know about the Float class, it was a mistake when I created the example. As for BigDecimal, I'll look into it since many people have suggested it. Thanks!
0

If precision is vital, you should use BigDecimal to make sure that the required precision remains. When you instantiate the calculation, remember to use strings to instantiate the values instead of doubles.

Comments

-2

I never had a problem with simple arithmetic precision in either Basic, Visual Basic, FORTRAN, ALGOL or other "primitive" languages. It is beyond comprehension that JAVA can't do simple arithmetic without introducing errors. I need just two digits to the right of the decimal point for doing some accounting. Using Float subtracting 1000 from 1355.65 I get 355.650002! In order to get around this ridiculous error I have implemented a simple solution. I process my input by separating the values on each side of the decimal point as character, convert each to integers, multiply each by 1000 and add the two back together as integers. Ridiculous but there are no errors introduced by the poor JAVA algorithms.

Comments

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.