2

I was looking at the Python documentation of fractions and trying this code:

from fractions import Fraction

>>> print("Fraction (0.5):", Fraction(0.5))
Fraction (0.5): 1/2
>>> print("Fraction (0.1):", Fraction(0.1)) 
Fraction (0.1): 3602879701896397/36028797018963968
>>> print(1/10) 
0.1

Looking at the Fraction(0.1) result I thought it was my computer problem, but when I tried it on several computers the results were same.

My question

  1. is there any computational reason to choose these odd numbers 3602879701896397/36028797018963968 instead of 1/10 just like 1/2 as it chosen for Fraction(0.5).
  2. more of these exist in python?
MSeifert
  • 145,886
  • 38
  • 333
  • 352
nivas
  • 3,138
  • 3
  • 16
  • 15
  • 1
    The number `1/10` can't be represented exactly in binary, just as `1/7` can't be represented exactly in decimal. – Barmar Aug 28 '17 at 13:57
  • For the canonical discussion on this issue, see http://floating-point-gui.de. – Rob Napier Aug 28 '17 at 13:58
  • @Barmar oh i see, 1/7 not divisible exactly but 1/10 is mathematically exactly equals to 0.1 – nivas Aug 28 '17 at 14:03
  • That's true in decimal. Things are different in binary. 1/2 and 1/4 are divisible exactly, but 1/10 is not. – Barmar Aug 28 '17 at 14:04
  • 1/7 is just as "exactly divisible" as 1/10. In base 7, 1/7 is written "0.1". There is nothing special about base 10; it's just a common notational system. – Rob Napier Aug 28 '17 at 14:04
  • @moses-koledoye how do you think that really helps me (or) better answer my question??? – nivas Aug 28 '17 at 14:15

1 Answers1

2

Yes, that's because that's the integer ration for the float 0.1 (which can't be represented exactly with floats):

>>> (0.1).as_integer_ratio()
(3602879701896397, 36028797018963968)

>>> '{:.30f}'.format(0.1)   # just to show that it can't be represented exactly I print 30 digits of 0.1
'0.100000000000000005551115123126'

If you want correct Fractions you need to use both arguments or pass in a string:

>>> Fraction(1, 10)
Fraction(1, 10)

>>> Fraction('0.1')
Fraction(1, 10)

Or limit the denominator after creating it from a float (not guaranteed to work in all cases):

>>> Fraction(0.1).limit_denominator()
Fraction(1, 10)

As for your second question: There are infinitely many rational numbers (decimal numbers that could be represented exactly as Fraction) in math but a computer uses 64bits for doubles (the Python float type). That means only a few real numbers can have an exact representation as double. So there are a lot of other numbers with the same problem, just to name a few:

>>> Fraction(0.2)
Fraction(3602879701896397, 18014398509481984)

>>> Fraction(0.3)
Fraction(5404319552844595, 18014398509481984)

>>> Fraction(1/3)
Fraction(6004799503160661, 18014398509481984)
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • Thanks for answer, any other exist or just for `0.1` – nivas Aug 28 '17 at 14:00
  • @nivas: this happens for any number that can't be perfectly represented in the binary representation. `0.1` is only one of many, many numbers for which this is the case. It's the drawback of floating point numbers. – Zinki Aug 28 '17 at 14:02
  • 1
    There are infinitely many – Reblochon Masque Aug 28 '17 at 14:02
  • @MSeifert `Fraction('0.1')` or `Fraction(0.1)` which is right & whats the difference? – nivas Aug 28 '17 at 14:13
  • `Fraction(0.1)` is (almost) always wrong because it first creates a float (which is inexact) and only then converts that inexact float to a `Fraction`. The `Fraction('0.1')` approach is (almost) always better because then the Fraction parses the string as exactly as possible, which is why it returns `Fraction(1, 10)`. – MSeifert Aug 28 '17 at 14:14
  • @MSeifert thanks for clarifying – nivas Aug 28 '17 at 14:16