14

Recently I came across a bug/feature in several languages. I have a very basic knowledge about how it's caused (and I'd like some detailed explanation), but when I think of all the bugs I must have made over the years, the question is how can I determine "Hey, this might cause a riddiculous bug, I'd better use arbitrary precision functions", what other languages do have this bug (and those who don't, why). Also, why 0.1+0.7 does this and i.e. 0.1+0.3 doesn't, are there any other well-known examples?

PHP

//the first one actually doesn't make any sense to me,
//why 7 after typecast if it's represented internally as 8?
debug_zval_dump((0.1+0.7)*10); //double(8) refcount(1)
debug_zval_dump((int)((0.1+0.7)*10)); //long(7) refcount(1)
debug_zval_dump((float)((0.1+0.7)*10)); //double(8) refcount(1)

Python:

>>> ((0.1+0.7)*10)
7.9999999999999991
>>> int((0.1+0.7)*10)
7

Javascript:

alert((0.1+0.7)*10); //7.999999999999999
alert(parseInt((0.7+0.1)*10)); //7

Ruby:

>> ((0.1+0.7)*10).to_i                                                  
=> 7                                                                    
>>((0.1+0.7)*10)                                                       
=> 7.999999999999999                                                    
S.Lott
  • 384,516
  • 81
  • 508
  • 779
cypher
  • 6,822
  • 4
  • 31
  • 48
  • 2
    it's not a bug (and SELECT is not broken): look up precision and respresentation. asked in many forms, many times on SO – Mitch Wheat Jun 22 '11 at 11:41
  • That's not "bug/feature in several languages" but a "bug/feature in several processors". That's the limitation of double precision – JBernardo Jun 22 '11 at 11:42
  • I'm not going to downvote this question just because it's been asked so many times before. As a unit, it's a good question. Flag it as a duplicate if you wish, Mitch. – Lightness Races in Orbit Jun 22 '11 at 11:43
  • 3
    @JBernardo: No, it's a "bug/feature in mathematics". – Lightness Races in Orbit Jun 22 '11 at 11:43
  • To all the people answering something like "...and the number gets truncated to the nearest integer" - thanks, but I was basically looking for a more deep answer, "what happens internally to the number so it results in 7.999999999... instead of 8.0" and "why mysql> SELECT CAST(((0.1+0.7)*10) AS SIGNED)" outputs 8 instead of 7 as it "should". – cypher Jun 22 '11 at 11:58
  • @cypher, 0.1 can't be represented exactly, so ends up being slightly more than 0.1. Similarly 0.7 ends up being slightly less than 0.7, but it's a slightly bigger slightly, so the sum is slightly smaller than 0.8 – John La Rooy Jun 22 '11 at 12:07
  • Downvoting not because you didn't know about floating point arithmetic, but because you didn't try searching for an answer on Stack Overflow before asking. – Andrew Grimm Jun 22 '11 at 12:18

7 Answers7

33

What Every Computer Scientist Should Know About Floating-Point Arithmetic

Evgeny Shadchnev
  • 7,320
  • 4
  • 27
  • 30
  • Thank you, this is exactly the article I was looking for. – cypher Jun 22 '11 at 11:52
  • 1
    it's long and not very easy to read but you have to understand this stuff only once in your life :) – Evgeny Shadchnev Jun 22 '11 at 11:57
  • Yes, and that's exactly what I want, instead of just going with "the number gets truncated...". – cypher Jun 22 '11 at 11:58
  • 1
    It never ceases to amaze me just how much SO rep was gained by providing this link as the answer to a question. Despite all search and duplication hunt efforts, this question in various disguises just keeps getting asked – Eli Bendersky Jun 25 '11 at 14:00
7

It's not a language issue. It's general issue with float point arithmetic.

vartec
  • 131,205
  • 36
  • 218
  • 244
  • 1
    Yes, it is a language issue. The language designer could have chosen a more suitable default interpretation for numeric literals. In Fortress, for example, `0.7` is just syntactic sugar for the rational number `7/10` (note: that is the *number* "seven tenths", not the *computation* "seven divided by ten"). *And* the default base is decimal, not binary. This fixes pretty much 100% of the problems that almost everyone runs into. (Note: obviously, real computers don't have infinite memory, so even Fortress will have to truncate and round at some point. But not for cases like money or the OP's.) – Jörg W Mittag Jun 22 '11 at 12:05
  • @Jörg: There is `Decimal` type, which can be used **as needed**. I don't see a point in making that default in general purpose language. – vartec Jun 22 '11 at 12:08
5

Stop using floats. No, really.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
3

The floating point representation of numbers is not exact.

In Python, int truncates floats towards zero to the nearest integer. (int) in PHP, parseInt in Javascript, and to_i in Ruby do the same thing.

This is not a bug; it's just how these functions work.

For example, from the docs for Python's int:

Conversion of floating point numbers to integers truncates (towards zero).

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
1

This is a known problem that has to do with floating point representation, from which you can find more information here:

http://en.wikipedia.org/wiki/IEEE_754-2008

The specific problem is that 7.9 will be directly converted (trunc) to 7 while transforming it to an int. In Python you can solve this with:

int( round(((0.1+0.7)*10)) )

... and similarly in other languages.

But yes, this can be a problem in many situations. Floating point numbers are not reliable enough for payroll programs, for example.

Maybe others can give you other hints. Hpe this helps, anywway.

Baltasarq
  • 12,014
  • 3
  • 38
  • 57
1

Use the decimal module:

>>> int((decimal.Decimal('0.1')+decimal.Decimal('0.7'))*10)
8
JBernardo
  • 32,262
  • 10
  • 90
  • 115
0

PHP uses floating point numbers by default, you need to manually cast to integers.

You should be aware of floating point arithmetic. The other posts here provide enough links about that.

Personally I use round/ ceil/ float depending on what I expect as opposed to int

$a = (int) round((0.7 + 0.1) * 10);
Clintonio
  • 434
  • 5
  • 11