0

I have a float to represent the zoom factor on an image.

setZoomPercent( currentZoomPercent - 0.1f );

The trouble I'm having is that the decrementation is giving the following result. How can I avoid this ?

Zoom:100.0
Zoom:99.9
Zoom:99.8
Zoom:99.700005
Zoom:99.600006
Zoom:99.50001
Zoom:99.40001
Zoom:99.30001
Zoom:99.20001
Zoom:99.10001
Zoom:99.000015
Zoom:98.90002
Zoom:98.80002

P.S: I'm guessing it has to do with the binary representation of 0.1 in binary.

James P.
  • 19,313
  • 27
  • 97
  • 155
  • You are correct in your guess, this is called a [floating point rounding error](http://en.wikipedia.org/wiki/Round-off_error). – Kyranstar May 05 '14 at 03:36
  • 0.1 cannot be exactly represented in binary; it's `0.000110011001100...`, so exact subtraction can't be done the way you want. – awksp May 05 '14 at 04:07

1 Answers1

4

You can avoid it by using BigDecimal

    BigDecimal d1 = new BigDecimal("100.00");
    BigDecimal d2 = new BigDecimal("0.1");
    for(int i = 0; i < 100; i++) {
        d1 = d1.subtract(d2);
        System.out.println(d1);
    }

produces

99.90
99.80
99.70
99.60
99.50
99.40
99.30
99.20
...
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • This doesn't necessarily solve the problem, although bigdecimal will allow him to get a much higher accuracy and then round to the proper amount, you still can't properly and accurately decrement by 0.1. – WillBD May 05 '14 at 03:27
  • @ WillBD Take a look at my test – Evgeniy Dorofeev May 05 '14 at 03:34
  • curious, I had ran that exact same code, just instead of using the string input interpretation constructor for BigDecimal, I gave it two Double's instead, which gave me the following: 99.8999999999999999944488848768742172978818416595458984375 99.7999999999999999888977697537484345957636833190917968750 99.6999999999999999833466546306226518936455249786376953125 obviously your solution does solve OP's question, so +1 for that, but still curious. – WillBD May 05 '14 at 03:39
  • @WillBD Might it be because `0.1` cannot be exactly represented in binary? – awksp May 05 '14 at 03:52
  • @user3580294 It is indeed why it is happening, it's just interesting that java's handling of interpreting in a numeric string differs from how it handles reading in a double. – WillBD May 05 '14 at 03:54
  • @WillBD I used "0.1" this is exact representation – Evgeniy Dorofeev May 05 '14 at 03:54
  • @Evgeniy Dorofeev indeed, however you cannot exactly represent 0.1 in binary, therefore java is doing some magic behind the scenes when you use the string constructor to provide the appropriate output, likely with a type of rounding. I just found it interesting, that is all. – WillBD May 05 '14 at 03:58
  • @WillBD Might it be that `BigDecimal` parses the string in base 10, thus avoiding the infinite representation entirely? The `String` constructor calls the `char[]` constructor, which might point to this being the case... – awksp May 05 '14 at 04:05
  • @WillBD BigDecimal represents "0.1" as integer 1 with scale 1, this is why it is exact – Evgeniy Dorofeev May 05 '14 at 04:28