11

Possible Duplicate:
How to iterate between 0.1f and 1.0f with 0.1f increments in Java?

Part of my program needs to use values inside a while loop as:

0.1

0.2

0.3

...

0.9

so I need to provide them inside that loop. Here is the code:

double x = 0.0;
while ( x<=1 )
{
// increment x by 0.1 for each iteration
x += 0.1;
}

I need the output to be EXACTLY:

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

But it actually gives me something like:

0.1

0.2

0.300000000000000000000000004

0.4

0.5

0.6

0.79999999999999999999999999

0.89999999999999999999999999

0.99999999999999999999999999

Community
  • 1
  • 1
user1483799
  • 409
  • 4
  • 8
  • 17
  • You seem to be adding the right number. I don't see why this would not work. – Mukus Dec 12 '12 at 04:41
  • 3
    @TejaswiRana: It doesn't work because 0.1 is actually something like 0.0999999999999999999999 or 0.10000000000000000001. Decimal fractions can't be losslessly represented by a double. – cHao Dec 12 '12 at 04:43

6 Answers6

14

Welcome to the world of floating point, where 0.1 isn't 0.1. The problem is that many numbers, including 0.1, cannot be represented exactly in a double. So you aren't really adding exactly 0.1 to x each time through the loop.

One approach is to use integer arithmetic and divide by 10:

int i = 0;
while (i <= 10) {
    double x = i / 10.0;
    . . .
    i++;
}

Another approach is to make x a BigDecimal, where you can specify that you want a particular precision. It basically is doing what the above loop does (an integer plus a scale), but packaged up in a nice class with lots of bells and whistles. Oh, and it has arbitrary precision.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Really? When you add 0.1 you are not actually adding 0.1? That can throw off a lot of calculations in a lot of applications that exist till date. – Mukus Dec 12 '12 at 04:44
  • @TejaswiRana: Yep. That's why `double` is only a good idea for calculations that can tolerate the rounding error. Game physics calculations? Sure. Money? Hell no. Basically if the number is a *measurement*, doubles are generally ok; if it's a *count*, use a BigDecimal or integers instead. – cHao Dec 12 '12 at 04:45
  • So then if I were creating a banking app, what would I use as data type? Float? – Mukus Dec 12 '12 at 04:47
  • @cHao I take it that this is the same for C# too? – Mukus Dec 12 '12 at 04:48
  • 2
    @TejaswiRana - A lot of applications that use `double` are written using tolerances to compensate for this. For those that are not written that way, either the roundoff errors are not important or the applications are buggy. Alternative approaches include all-integer arithmetic, arbitrary precision packages (which are basically all-integer arithmetic at their core), or rational arithmetic packages (likewise integer-based). – Ted Hopp Dec 12 '12 at 04:48
  • 2
    @TejaswiRana: Floats are even worse than doubles as far as rounding error is concerned. For a banking app, you'd probably do best to have a certain number of decimal digits you want to keep, and multiply your amounts so that you can turn them into ints or longs. – cHao Dec 12 '12 at 04:49
  • 1
    @TejaswiRana: Yep. Same for most languages that support floating point numbers. C#, PHP, Java, C, C++, VB, etc. – cHao Dec 12 '12 at 04:50
  • http://stackoverflow.com/questions/2863388/what-is-the-equivalent-of-the-java-bigdecimal-class-in-c It gets even worse in C# – Mukus Dec 12 '12 at 04:52
  • 1
    @TejaswiRana: Not so much, actually. .net has a `Decimal` type, which is a decimal floating-point number (rather than the binary ones the CPU probably supports). `0.1` *can* be accurately and losslessly represented by a C# `decimal` (though it looks like `0.1m` or somethig like that in C#), so it's safe to use for money. Drawback is, a lot of operations (logs, non-integer exponentiation, basically anything that'd typically yield an irrational result) isn't supported for them without the help of third-party libs. – cHao Dec 12 '12 at 04:54
  • 2
    @cHao For a banking or accounting application it would be tantamount to criminal incompetence to use floating point at all. I've seen contractors hauled over the coals for it and required to remedy at their own expense. – user207421 Dec 12 '12 at 05:12
  • @EJP: There's a bit of difference with C#'s decimal floating-point, though. A `decimal`can accurately hold around 30 significant digits. That's enough for even a trillionth of a penny on amounts in the trillions. – cHao Dec 12 '12 at 05:16
2

you need to use the decimal formatter to get the expected output.

Below is the code for generating the expected output:

import java.text.DecimalFormat;


public class FloatIncrement {

    public static void main (String args[]){

        double x= 0.0;
        DecimalFormat form = new DecimalFormat("#.#");      
        while(x<0.9){
            x= x+0.1;
            System.out.println("X : "+Double.valueOf(form.format(x)));          

        }

    }
}
simbu94
  • 1,012
  • 2
  • 10
  • 22
  • That is some good looking code :) – squiguy Dec 12 '12 at 05:14
  • The error is accumulated in this example. Better to reduce number of floating point operations if you can (not relevant to this case, but matters if your loops goes to a large number). – Ivan Balashov May 13 '20 at 11:53
1

Using BigDecimal

double x = 0.0;
   int decimalPlaces = 2;           

  while ( x<=1 )
  {

    x += 0.1;
    BigDecimal bd = new BigDecimal(x);
    bd = bd.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP);
    x = bd.doubleValue();           

    System.out.println(x); 
  }
Mohammod Hossain
  • 4,134
  • 2
  • 26
  • 37
1

To get output you want, you could use DecimalFormat. Here is some sample code.

import java.text.DecimalFormat;

public class DF {

  public static void main(String [] args) {

    double x = 0.1;
    DecimalFormat form = new DecimalFormat("#.#");
    while (x <= .9) {
      System.out.println(Double.valueOf(form.format(x)));
      x += 0.1;
    }

  }

}

As far as the implementation you have now, there is no guarantee as to the precision of what gets printed due to the nature of floating point numbers.

squiguy
  • 32,370
  • 6
  • 56
  • 63
0

That's because you can use binary floating point to do precise decimal arithmetic because FP cannot precisely represent all decimal values.

You need to use an integer value representing some decimal fractional unit like hundredths or thousandths or use something like BigDecimal.

Lawrence Dol
  • 63,018
  • 25
  • 139
  • 189
0

Double is stored in binary

float and double store numbers as a certain number of significant figures and a radix point (kind of like scientific notation). The significant figures part is not always perfect, because it's stored as a certain number of binary digits - so you can't expect it to perform the way you're expecting it to. (for a better explanation see http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)

Consider using a class such as BigDecimal or a class that implements rational numbers, like the ones mentioned here - Is there a commonly used rational numbers library in Java?

You could also just turn i into an integer, and change it from 1 to 10, and compensate for this in your code.

Community
  • 1
  • 1
Shariq
  • 581
  • 1
  • 5
  • 12
  • The opening sentence is a bit misleading. Every `double` is a rational value; the issue is the converse: not every rational value is a `double` value. – Ted Hopp Dec 12 '12 at 04:46
  • You could also round to one significant figure using the code available here. http://stackoverflow.com/questions/202302/rounding-to-an-arbitrary-number-of-significant-digits – Shariq Dec 12 '12 at 04:46
  • @TedHopp - changed my answer; you're right. I didn't explain what I meant by rational - I meant in memory, the number isn't represented as a ratio of a numerator to a denominator. – Shariq Dec 12 '12 at 04:49