-2

So today I noticed something unexpected in java:

    long a = 1234567890000l;
    float b = 1.2f;
    long result_a = (long)(a - b * 1000);
    long result_b = a - ((long) (b * 1000));
    System.out.println("result_a: " + result_a);
    System.out.println("result_b: " + result_b);

You would expect result_a and result_b to be similar (not exactly the same since b is float and not precise by definition), right? right?? Well, no...

result_a: 1234567954432
result_b: 1234567888800

No idea why the huge difference. Any ideas guys?

the1plummie
  • 750
  • 2
  • 10
  • 21
  • Well, you say it's a huge difference... but the difference is in the 8th significant digit. How precise expect do you expect `float` to be, and why? – Jon Skeet Feb 24 '16 at 07:56
  • 4
    (The nearest float to 1234567890000 is 1234567954432, for starters... now take 1200 away from that, and nothing happens, as it's too tiny, relatively speaking, to make any difference.) – Jon Skeet Feb 24 '16 at 07:57
  • 2
    A `float` guarantees 7 significant figures of precision. A `double` 15. Learn to live with this. And do study @JonSkeet 's second comment carefully. – Bathsheba Feb 24 '16 at 07:59
  • @Bathsheba I would have said 6 digits of precision for `float` +1 – Peter Lawrey Feb 24 '16 at 08:56

1 Answers1

-1

float and double suffer of problems related to the internal representation in binary format with significand and coefficient.

To solve this problem you need to avoid use of float and double for precise (at the level choosen by the programmer) of calculations. You can use a BigDecimal instead.

From javadoc

Immutable, arbitrary-precision signed decimal numbers.

Just to show the problem of double and float also for little values consider the following code:

if (1.0 - 0.6 - 0.3 - 0.1 == 0.0) {
    // Will never enter here
}

The test is false because the operation 1.0 - 0.6 - 0.3 - 0.1 is not zero, but a value close to zero due to rounding problems.

Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56
  • 1
    I wouldn't really use the words "suffer" and "problems", as floating point numbers are extremely clever and such behaviour the OP observes is by design. – Bathsheba Feb 24 '16 at 08:01
  • Indeed - and it's not like `BigDecimal` will always be precise, either - you can't represent 1/3 precisely in `BigDecimal` any more than you can represent 1/10 precisely in `double`. – Jon Skeet Feb 24 '16 at 08:03
  • Is not precise,but is arbitrary precision. So it means that is the developer choosing the level of precision. – Davide Lorenzo MARINO Feb 24 '16 at 08:04
  • @Jon Skeet: I updated the response to explain what I mean for precise. – Davide Lorenzo MARINO Feb 24 '16 at 08:05
  • 1
    Fundamentally, I think trying to explain this in just a few sentences is unlikely to help much. If you don't already understand the difference between `BigDecimal` and `float`/`double`, I don't think this answer will help you get there. I've closed the question as a duplicate of another, which has more in-depth answers. – Jon Skeet Feb 24 '16 at 08:08
  • @Batsheba: "suffer" and "problems" is the right word to show that for the internal representation of this kind of data it is possible (by design) to have "strange" unexpected results. Obviously the choice of this representation is not an error, but is a good solution for most of situations. But if you like to know exactly how data are rounded it is better to use a BigDecimal – Davide Lorenzo MARINO Feb 24 '16 at 08:08
  • The representation being binary is not the real problem here. You would have the same problem with a 7 digit decimal floating point number. The problem is the limited precision. As such, the example you show is very misleading (as it does demonstrate the binary problem) – Thilo Feb 24 '16 at 08:20
  • @Thilo No the problem is not the limited precision. The real problem is strongly related on the kind of binary representation of this data. The data are not only with a limited precision, they are also "approximate". This is evident in the example shown in the answer that is in the range of precision, but is approximated. – Davide Lorenzo MARINO Feb 24 '16 at 08:32
  • So how do you express `1234567888800` with 7 significant decimal digits? You can only do `1234567000000` or `1234568000000`. – Thilo Feb 24 '16 at 08:36
  • @Thilo: Strange but true: `if (1234567888800f == 1234567888800L) {` `System.out.println("enter here");` `}` – Davide Lorenzo MARINO Feb 24 '16 at 08:40
  • What is strange about that? If you compare two numbers using only their first seven digits, then numbers that match in the first seven digits (such as the same number twice) will be equal. The problem is that numbers that only match in the first seven digits but differ after that are also considered equal. – Thilo Feb 24 '16 at 08:45
  • @Thilo you are right, it was not the best example. This one show better the real value: System.out.println((long) 1234567888800f); prints 1234567823360 that is not 1234567000000 nor 1234568000000 – Davide Lorenzo MARINO Feb 24 '16 at 08:50
  • Right. But you have the same problem even if you used a decimal floating point number. With binary, you can only get `1234567823360` or `1234567954432`, with decimal you can only get `1234567000000` or `1234568000000`. – Thilo Feb 24 '16 at 08:56