3

So, I have the following code:

    half= 1/2.0
    print(half)
    for x in range(1,1075):
        half=half/2.0
        print(half)

But right at the last part of the loop, python decides that half is now 0.0

    1.265e-321
    6.3e-322
    3.16e-322
    1.6e-322
    8e-323
    4e-323
    2e-323
    1e-323
    5e-324
    0.0

Did I reach python's limit? Do I need to install a package to go farther? I'm not sure why this is happening, but I'm assuming that python just reached limit

Gerges
  • 5,335
  • 1
  • 18
  • 31
Nate
  • 51
  • 7
  • 2
    Have you read this tutorial on numeric representation in python? https://docs.python.org/3/tutorial/floatingpoint.html – Jon Kiparsky May 23 '17 at 01:30
  • 3
    Yep, you reached python's limit; the reasons behind it have to do with the traditional limits of floating-point arithmetic. If you want to go "deeper", I suggest you explore packages that are specifically built to deal with arbitrary precision, like http://mpmath.org/ – Hamms May 23 '17 at 01:31

2 Answers2

5

TLDR: try Fraction

half = Fraction(1, 2)
for x in range(1, 1075):
    half = half / 2
    print(half)

will give

1/4
1/8
...
1/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784
1/404804506614621236704990693437834614099113299528284236713802716054860679135990693783920767402874248990374155728633623822779617474771586953734026799881477019843034848553132722728933815484186432682479535356945490137124014966849385397236206711298319112681620113024717539104666829230461005064372655017292012526615415482186989568

You can find out the smallest available positive float with

>>> import sys
>>> sys.float_info.min
2.2250738585072014e-308

then following your example we will find that

>>> 2 * sys.float_info.min > pow(2, -1074)
True

i.e. next division by 2 seems to be less than smallest available positive float.

Btw their difference is equal to

>>> diff = 2 * sys.float_info.min - pow(2, -1074)
>>> diff
4.4501477170144023e-308

but it is interesting that

>>> diff == 2 * sys.float_info.min
False

while

>>> diff / 2 == sys.float_info.min
True

P. S.: dividing Fraction objects by floats will give us float

>>> half = Fraction(1, 2)
>>> half = half / 2.0
>>> type(half)
<class 'float'>

so your code with dividing by 2.0 will give the same result and for correct working with Fractions you should add/subtract/divide/multiply it with ints or other Fractions like

half = Fraction(1, 2)
for x in range(1, 1075):
    half = half / Fraction(2.0)
    print(half)

P. P. S.: there is a convention about using underscore as a name for unused object, so it will be better to write

half = Fraction(1, 2)
for _ in range(1, 1075):
    half = half / 2
    print(half)
Azat Ibrakov
  • 7,007
  • 8
  • 31
  • 40
1

Yes, essentially, you have reached the Python's limit. The decimals lose precision as you go on.

One possible way would be to use the Fraction class, as suggested by Azat.

However, you can also use the Decimal class.

Here is the example provided on the page linked above. This treats the number as an object and not as a primitive/built-in variable:

>>> from decimal import *
>>> getcontext().prec = 6
>>> Decimal(1) / Decimal(7)
Decimal('0.142857')
>>> getcontext().prec = 28
>>> Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')

getcontext().prec = 6 line is the one that sets the precision. You can change the number 6 to whatever you need.

Usually, the precision to 324th decimal place (as in your example) is not necessary, so Python only stores a few digits as binary fractions. Using the Fraction or the Decimal classes allows you to extend the functionality, but it can also slow down the code significantly if you use it repetitively (in loops for example).

Electron
  • 298
  • 4
  • 13