2

Note: question still not answered thoroughly! This questions does not deal with the issue of truncation of floating point parts!!!

In Java I have this simple code:

double sum = 0.0;
for(int i = 1; i <= n; i++){
    sum += 1.0/n
}
System.out.println("Sum should be: 1");
System.out.println("The result is: " + sum);

Where n can be any integer. For numbers like 7,9, the expected value for sum is to have difference in the last digits of sum, and the result is 0.999999999998 or something but the output when I use 3 is 1.0.

If you add 1/3 3 times, you would expect a number close to 1, but I get exactly 1.0.

Why?

lekroif
  • 992
  • 3
  • 12
  • 24

4 Answers4

3

This is because the division is made in integer.

1/n always gives 0 for n > 1.

Therefore, you always end up with sum = 0 + 1/1 + 0 + 0...

Try with 1.0 / n

J_D
  • 3,526
  • 20
  • 31
2

If you add 1/3 3 times, you would expect a number close to 1, but I get exactly 1.0.

Actually a normal person uncontaminated by programming experience would expect n * 1 / n to equal 1, but we're not normal here.

I can't reproduce your problem exactly, I get

groovy:000> def foo(n) {
groovy:001>   sum = 0.0
groovy:002>   for (int i = 0; i < n; i++) {
groovy:003>     sum += 1.0 / n
groovy:004>   }
groovy:005>   sum
groovy:006> }
===> true
groovy:000> foo(3)
===> 0.9999999999

There may be 2 issues here, at least you will want to be aware of them.

One is that doubles are not exact, they cannot represent some values exactly, and you just have to expect stuff to be off by a little bit. Your goal isn't 100% accuracy, it's to keep the error within acceptable bounds. (Peter Lawrey has an interesting article on doubles that you might want to check out.) If that's not ok for you, you'll want to avoid doubles. For a lot of uses BigDecimal is good enough. If you want a library where the division problems in your question give accurate answers you might check out the answers to this question.

The other issue is that System.out.println doesn't tell you the exact value of a double, it fudges a bit. If you add a line like:

System.out.println(new java.math.BigDecimal(sum));

then you will get an accurate view of what the double contains.

Community
  • 1
  • 1
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • BigDecimal cannot give perfect accuracy for numbers like one third. For that, you would need a rational number arithmetic package. On the other hand, it is perfectly accurate for printing doubles, because every double is exactly representable as a BigDecimal. – Patricia Shanahan Mar 22 '13 at 17:39
  • More answers like this, please! – lekroif Mar 22 '13 at 21:47
  • @lekroif What questions do you still have that this answer did not cover? The biggest issue is that if you really want to know the value of a float or double, convert it to BigDecimal before printing. Try changing your program to do that. – Patricia Shanahan Mar 23 '13 at 03:43
  • @lekroif: also if you make the code sample more complete (for instance, n's declaration is missing) so we can paste it into Eclipse and run it, then we might be able to duplicate your results and provide a better answer. – Nathan Hughes Mar 23 '13 at 04:26
1

I'm not sure whether this will help clarify things, because I'm not sure what you consider to be the problem.

Here is a test program that uses BigDecimal, as previously suggested, to display the values of the intermediate answers. At the final step, adding the third copy of 1.0/3 to the sum of two copies, the exact answer is half way between 1.0 and the next double lower than it. In that situation the round-to-even rounding rule picks 1.0.

Given that, I think it should round to 1.0, contradicting the question title.

Test program:

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    final double oneThirdD = 1.0/3;
    final BigDecimal oneThirdBD = new BigDecimal(oneThirdD);
    final double twoThirdsD = oneThirdD + oneThirdD;
    final BigDecimal twoThirdsBD = new BigDecimal(twoThirdsD);
    final BigDecimal exact = twoThirdsBD.add(oneThirdBD);
    final double nextLowerD = Math.nextAfter(1.0, 0);
    final BigDecimal nextLowerBD = new BigDecimal(nextLowerD);
    System.out.println("1.0/3: "+oneThirdBD);
    System.out.println("1.0/3+1.0/3: "+twoThirdsBD);
    System.out.println("Exact sum: "+exact);
    System.out.println("Rounding error rounding up to 1.0: "+BigDecimal.ONE.subtract(exact));
    System.out.println("Largest double that is less than 1.0: "+nextLowerBD);
    System.out.println("Rounding error rounding down to next lower double: "+exact.subtract(nextLowerBD));
  }
}

Output:

1.0/3: 0.333333333333333314829616256247390992939472198486328125
1.0/3+1.0/3: 0.66666666666666662965923251249478198587894439697265625
Exact sum: 0.999999999999999944488848768742172978818416595458984375
Rounding error rounding up to 1.0: 5.5511151231257827021181583404541015625E-17
Largest double that is less than 1.0: 0.99999999999999988897769753748434595763683319091796875
Rounding error rounding down to next lower double: 5.5511151231257827021181583404541015625E-17
Patricia Shanahan
  • 25,849
  • 4
  • 38
  • 75
0

An int divided by an int will always produce another int. Now int has no place to store the fractional part of the number so it is discarded. Keep in mind that it is discarded not rounded.

Therefore 1 / 3 = 0.3333333, and the fractional part is discarded meaning that it becomes 0.

If you specify the number as a double (by including the decimal point, ex. 1. or 1.0) then the result will be a double (because java automatically converts an int to a double) and the fractional part will be preserved.

In your updated question, you are setting i to 1.0 but i is still an int. So that 1.0 is getting truncated to 1 and for further calculations, it is still an int. You need to change the type of i to double as well otherwise there will be no difference in the code.

Alternatively you can use sum += 1.0/n

This will have the effect of converting n to a double before performing the calculation

Cedric Mamo
  • 1,724
  • 2
  • 18
  • 33