Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialJames N
17,864 Pointsint(5.999999999999999) i got 6? i don't get it.
according to kenneth, when you use the int()
method on a float
, it returns the whole number of the float
, and just chops of the decimal. however, when i tried to run int(5.9999999999999999999999)
, I expected the function to return 5. not 6.
is this just one of the "weird" things with floats
? or is this something more? I don't understand what's going on here. Can someone explain it for me? Thanks!
4 Answers
Chris Freeman
Treehouse Moderator 68,441 PointsNow I'm REALLY curious. Having the 16th 9 seems to make the difference. It's not the int
operation causing the change. It is a limitation of the floating point representation. A longer read on these limits in Python and all computer languages see Floating Point Arithmetic: Issues and Limitations
As for your case, Let's dig in...
$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 5.999999999999999 # 15 9's
5.999999999999999
>>> 5.9999999999999999 # 16 9's
6.0
# Starting with binary notation
# 5 in binary is
>>> bin(5)
'0b101'
# that's 1 * 2**2 + 0 * 2**1 + 1 * 2**0 = 4 + 0 + 1 = 5
# Now let's look at float representation.
# Floats are stored as 53 bits (or a 1 followed by 13 hexadecimal characters)
# Hexadecimal characters represent 4-bits, have 16 possible values:0-9a-f
>>> float.hex(5.0)
'0x1.4000000000000p+2'
# converting that to the full 53-bit version would be
# 0b1.0100000000000000000000000000000000000000000000000000p+2 binary
# the 'p+2 means it's shifted by two bit, so
# 0b1.01000000...p+2 == 0b101.000000...p+0
# Now the 5 in binary is clearly seen
# Back to float representation of 5 in hex
>>> float.hex(5.0)
'0x1.4000000000000p+2'
# as you increase the value, the HEX value increases
>>> float.hex(5.1)
'0x1.4666666666666p+2'
>>> float.hex(5.2)
'0x1.4cccccccccccdp+2'
>>> float.hex(5.3)
'0x1.5333333333333p+2'
>>> float.hex(5.4)
'0x1.599999999999ap+2'
>>> float.hex(5.5)
'0x1.6000000000000p+2'
>>> float.hex(5.6)
'0x1.6666666666666p+2'
>>> float.hex(5.7)
'0x1.6cccccccccccdp+2'
>>> float.hex(5.8)
'0x1.7333333333333p+2'
>>> float.hex(5.9)
'0x1.799999999999ap+2'
# As you add more 9's it will approach 0x1.8000...p+2 which is 6:
>>> float.hex(5.99)
'0x1.7f5c28f5c28f6p+2'
>>> float.hex(5.9999)
'0x1.7ffe5c91d14e4p+2'
>>> float.hex(5.99999999)
'0x1.7ffffff543389p+2'
>>> float.hex(5.999999999999)
'0x1.7fffffffffb9ap+2'
>>> float.hex(5.999999999999999) # 15 9's
'0x1.7ffffffffffffp+2'
# in binary that's
'0b1.111111111111111111111111111111111111111111111111111p+2
# this is the highest number before it rolls over
>>> float.hex(5.9999999999999999) # 16 9's
'0x1.8000000000000p+2'
>>> float.hex(6.0)
'0x1.8000000000000p+2' =
# in binary this is:
'0b1.1000...p+2' = '0b110.00...p+0'
# 6 in binary is
>>> bin(6)
'0b110'
As the integer portion of a number increases, fewer of the 53 bits remain for the fractional part. This means that fewer 9's are needed to round up to the next integer:
>>> float.hex(1024.0)
'0x1.0000000000000p+10'
>>> float.hex(1024.9)
'0x1.003999999999ap+10'
>>> float.hex(1024.999999999) # nine 9's
'0x1.003ffffffeed2p+10'
>>> float.hex(1024.999999999999) # 12 9's
'0x1.003fffffffffcp+10'
>>> float.hex(1024.9999999999999) # 13 9's
'0x1.0040000000000p+10'
>>> float.hex(1025.0)
'0x1.0040000000000p+10'
# starting with 2**40 = 1099511627776
>>> float.hex(1099511627776.0)
'0x1.0000000000000p+40'
>>> float.hex(1099511627776.9)
'0x1.0000000000e66p+40'
>>> float.hex(1099511627776.99)
'0x1.0000000000fd7p+40'
>>> float.hex(1099511627776.999)
'0x1.0000000000ffcp+40'
>>> float.hex(1099511627776.9999) # four 9's
'0x1.0000000001000p+40'
>>> float.hex(1099511627777.0)
'0x1.0000000001000p+40'
# Getting 2**50, very little precision remains:
>>> 1125899906842624.4 == 1125899906842624.5 == 1125899906842624.6
True
>>> 1125899906842624.9 == 1125899906842625.0
True
>>> float.hex(1125899906842624.4)
'0x1.0000000000002p+50'
>>> float.hex(1125899906842624.5)
'0x1.0000000000002p+50'
>>> float.hex(1125899906842624.6)
'0x1.0000000000002p+50'
>>> float.hex(1125899906842624.7)
'0x1.0000000000003p+50'
>>> float.hex(1125899906842624.8)
'0x1.0000000000003p+50'
>>> float.hex(1125899906842624.9)
'0x1.0000000000004p+50'
>>> float.hex(1125899906842625.0)
'0x1.0000000000004p+50'
As a final curiosity with binary numbers, when you multiply a number by 2, it's binary representation is simply shift by one bit position:
>>> float.hex(5.0)
'0x1.4000000000000p+2'
>>> float.hex(10.0)
'0x1.4000000000000p+3'
>>> float.hex(20.0)
'0x1.4000000000000p+4'
>>> float.hex(40.0)
'0x1.4000000000000p+5'
Notice how the signifcand value stays the same, just the shifted "power" number increase by 1
Nathan Tallack
22,160 PointsThat is strange. When I open a Python workspace here on TeamTreehouse I get 5 from that. Consider the output below.
treehouse:~/workspace$ python
Python 3.5.0 (default, Dec 8 2015, 20:56:53)
[GCC 4.8.3 20140911 (Red Hat 4.8.3-9)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> int(5.999999999999999)
5
>>>
Where are you getting your results from?
James N
17,864 PointsWorkspaces, just like you.
Here's the output:
Python 3.5.0 (default, Dec 8 2015, 20:
56:53)
[GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]
on linux
Type "help", "copyright", "credits" or
"license" for more information.
>>> int(5.999999999999999999)
6
Nathan Tallack
22,160 PointsMore 9's in yours that time. It seems that once you get to 16 decimal places it rounds up. I suspect this has to do with the INT type we are using where we are wrapping around a binary counter someplace.
Any guru's out there want to edumacate us? ;)
James N
17,864 PointsWow. I Started a popular thread. LOL
Randy Eichelberger
1,491 Points@Chris great explanations thanks I was curious the underlying reasoning behind why it behaves that way and explaining how binary bits work with numbers was extremely helpful.
Nathan Tallack
22,160 PointsNathan Tallack
22,160 PointsIn the immortal words of Keanu... Woah.
James N
17,864 PointsJames N
17,864 PointsI Noticed that you used 2 asterisks for the
2**2
portion of the comment. Just curious, is that an exponent sign? and if so, will it work in other languages?Chris Freeman
Treehouse Moderator 68,441 PointsChris Freeman
Treehouse Moderator 68,441 PointsYes, In Python and Perl and other languages, two asterisks (**) means "raised to the power". In some Math book notations a caret (^) is used, but this means bit-wise exclusive OR, in Python and Perl.
Sky Lu
1,260 PointsSky Lu
1,260 Pointsstill no idea... can someone make it simple can easy to understand...
Chris Freeman
Treehouse Moderator 68,441 PointsChris Freeman
Treehouse Moderator 68,441 PointsYour comment... Sky Lu, to be manipulated by computers, all numbers must be stored in the native binary format of computers. In the case of integers, each binary bit represents the values 1, 2, 4, 8, 16, 32, 64, 128, 256, and so on. Adding up each position to get the total value. 11 would be stored as
b1011
which is 4 + 2 + 1.Fractions work in a similar manner except each bit represents a fractional portion of the value. Each bit represents 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128, 1/256, and so on of the base value.
As mentioned in my answer above, 5 is represented as:
The "1." at the beginning and the "p+2" represent 1 times 2^2 (2 to the power 2). This is 4. The first 4 bits after the point ".0100" represent a fractional value of this starting value 4:
This 1, plus the starting 4, gives the desired value 5.
The limitation of using a 53-bit floating point representation is the precision granularity depends on the size of the smallest fractional bit.
There are not enough bits to differentiate numbers with a difference smaller than this smallest fraction.
As a last example, the number 5.0000000000000001 can not be differentiated from 5 because the last fractional bit is not small enough to represent the fractional part "0.0000000000000001"
Hope this helps.
Sky Lu
1,260 PointsSky Lu
1,260 PointsHi Chris, thank you very much for your detail reply. I just don't understand, why 5 is represented as 1.4000000000000p+2 #hex, DEC5 to HEX should be 5 again, right? DEC5 to binary should be 101 as I understand. How DEC5 shows 1.0100000000000000000000000000000000000000000000000000p+2 # binary
Chris Freeman
Treehouse Moderator 68,441 PointsChris Freeman
Treehouse Moderator 68,441 PointsHi Sky, The difference is whether the object is an int or a float. As an int, "5" in
Note the
hex()
function only works on integers and does not work on floats. To get the hex representation of afloat
use the methodfloat.hex()
.A float object is stored differently than an int object. The leading number on a float is always 1 unless the value is exactly Zero. The exponent "power" suffix indicates how much the point between the integer portion and the fraction portion is shifted in interpreting the value. The value 5 as a float and an int are related. Below I show how the float representation can be interpreted to see the binary value of the integer.