Strange floating-point behaviour in a Java program

2019-01-01 01:02发布

问题:

This question already has an answer here:

  • Is floating point math broken? 28 answers

In my program I have one array with 25 double values 0.04 When I try to sum these values in a loop I get following results:

0.0 + 0.04 = 0.04
0.04 + 0.04 = 0.08
0.08 + 0.04 = 0.12
0.12 + 0.04 = 0.16
0.16 + 0.04 = 0.2
0.2 + 0.04 = 0.24000000000000002
0.24000000000000002 + 0.04 = 0.28
0.28 + 0.04 = 0.32
0.32 + 0.04 = 0.36
0.36 + 0.04 = 0.39999999999999997
0.39999999999999997 + 0.04 = 0.43999999999999995
0.43999999999999995 + 0.04 = 0.4799999999999999
0.4799999999999999 + 0.04 = 0.5199999999999999
0.5199999999999999 + 0.04 = 0.5599999999999999
0.5599999999999999 + 0.04 = 0.6
0.6 + 0.04 = 0.64
0.64 + 0.04 = 0.68
0.68 + 0.04 = 0.7200000000000001
0.7200000000000001 + 0.04 = 0.7600000000000001
0.7600000000000001 + 0.04 = 0.8000000000000002
0.8000000000000002 + 0.04 = 0.8400000000000002
0.8400000000000002 + 0.04 = 0.8800000000000002
0.8800000000000002 + 0.04 = 0.9200000000000003
0.9200000000000003 + 0.04 = 0.9600000000000003

Why on earth could that happen?!

回答1:

The most common storage for floating-point values in programming languages - IEEE singles and doubles - does not have exact representations for most decimal fractions.

The reason is that they store values in binary floating-point format, rather than decimal floating-point format. The only fractional values which can be represented exactly are those which are sums of negative powers of two. Numbers like:

  • 0.5 (2^-1)
  • 0.125 (2^-3)
  • 0.625 (2^-1 + 2^-3)

Etc.

What you are seeing is the fact that representations of numbers like 0.96 are not exactly representable, because they are not expressible as a sum of negative powers of two. Thus, when printed out with full precision as a decimal fraction, they won\'t match the original value.



回答2:

See also \"What Every Computer Scientist Should Know About Floating Point\"



回答3:

Other answers mentioned why, but not how to avoid it.

There are several solutions:

  • Scaling: if all your numbers are multiples of 0.01 (for instance), multiply everything by 100 and use integer arithmetic (which is exact).
  • Numeric type: if your language has a numeric type (like a numeric type in SQL), you can use it.
  • Arbitrary precision rationals: use a bignum library like GMP, which allows you to represent these numbers as the ratio of two integers.
  • Decimal floating point: if you have a decimal floating point like the one in IEEE-754r, you can use it.


回答4:

You may wish to check out the java BigDecimal class as an alternative to floats and doubles.



标签: java math