[Tutor] Floating Point Craziness
Nathan
nathan2012 at 163.com
Mon Jun 13 14:02:38 CEST 2011
who can tell me how to unsubscribe the message.
At 2011-06-13 01:13:05,"Steven DAprano" <steve at pearwood.info> wrote:
>Ryan Strunk wrote:
>> Hi everyone,
>> I'm designing a timeline. When the user presses the right arrow, 0.1 is
>> added to the current position. The user can add events to the timeline, and
>> can later scroll back across those events to see what they are. But
>> something I absolutely don't understand is happening:
>> I used the program to place events at 1.1, 2.1, and 3.1. Here is the end of
>> the debug output for arrowing to 3.1 and placing the event:
>> position = 2.7
>> position = 2.8
>> position = 2.9
>> position = 3.0
>> position = 3.1
>> event placed.
>> Everything appears straight forward.
>
>
>It only *appears* straight forward, it actually isn't. That's because
>the printed output is rounded so as to look nice. Compare:
>
> >>> x = 1/10.0
> >>> print(x) # displayed as nicely as possible
>0.1
> >>> print('%.25f' % x) # display 25 decimal places
>0.1000000000000000055511151
>
>
>For speed and simplicity, floats are stored by the computer using
>fractional powers of two. That's fine for numbers which are powers of
>two (1/2, 1/4, 1/256, and sums of such) but numbers that humans like to
>use tend to be powers of 10, not 2.
>
>Unfortunately, many common fractions cannot be written exactly in
>binary. You're probably familiar with the fact that fractions like 1/3
>cannot be written exactly in decimal:
>
>1/3 = 0.33333333... goes on forever
>
>The same is true for some numbers in binary:
>
>1/10 in decimal = 1/16 + 1/32 + 1/256 + ...
>
>also goes on forever. Written in fractional bits:
>
>1/10 decimal = 0.00011001100110011... in binary
>
>Since floats can only store a finite number of bits, 1/10 cannot be
>stored exactly. It turns out that the number stored is a tiny bit larger
>than 1/10:
>
> >>> Fraction.from_float(0.1) - Fraction(1)/10
>Fraction(1, 180143985094819840)
>
>Rounding doesn't help: 0.1 is already rounded as close to 1/10 as it can
>possibly get.
>
>Now, when you print the dictionary containing those floats, Python
>displays (almost) the full precision:
>
> >>> print {1: 0.1}
>{1: 0.10000000000000001}
>
>
>In newer versions of Python, it may do a nicer job of printing the float
>so that the true value is hidden. E.g. in Python 3.1 I see:
>
>
> >>> print({1: 0.1})
>{1: 0.1}
>
>but don't be fooled: Python's print display is pulling the wool over
>your eyes, the actual value is closer to 0.10000000000000001.
>
>
>One consequence of that is that adding 0.1 ten times does not give 1
>exactly, but slightly less than 1:
>
> >>> x = 0.1
> >>> 1 - sum([x]*10)
>1.1102230246251565e-16
>
>
>(P.S. this is why you should never use floats for currency. All those
>missing and excess fractions of a cent add up to real money.)
>
>
>To avoid this, you can:
>
>
>* Upgrade to a newer version of Python that lies to you more often, so
>that you can go on living in blissful ignorance (until such time as you
>run into a more serious problem with floats);
>
>* Use the fraction module, or the decimal module, but they are slower
>than floats; or
>
>* Instead of counting your timeline in increments of 0.1, scale
>everything by 10 so the increment is 1.
>
>That is, instead of:
>
>2.0 2.1 2.2 2.3 ...
>
>you will have
>
>20 21 22 23 24 ...
>
>
>* Or you just get used to the fact that some numbers are not exact in
>floating point.
>
>
>
>
>--
>Steven
>
>_______________________________________________
>Tutor maillist - Tutor at python.org
>To unsubscribe or change subscription options:
>http://mail.python.org/mailman/listinfo/tutor
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tutor/attachments/20110613/ffaa2768/attachment.html>
More information about the Tutor
mailing list