11

So I am reading this PDF tutorial called: "Learning Python Fourth Edition". Now I got to a part which I dont understand because I am pretty much a beginner in Python. I am talking about this part:

enter image description here

Now I dont get the explaining of the first example. It does say: It turns out that there are two ways to print every object: with full precision(as in the first result shown here)

but how is this with full precision?

It might just explain it very easily for python programmers in the text but I dont seem to get it.

Loko
  • 6,539
  • 14
  • 50
  • 78
  • 3
    See python doc to [floating point arithemtic](http://docs.python.org/2/tutorial/floatingpoint.html): "On a typical machine running Python, there are 53 bits of precision available for a Python float" – Jörg Mäder Dec 09 '13 at 15:19
  • 2
    It helps to understand that there's a difference between "precision" and "accuracy". IEEE floats can't accurately represent every number, but you can print out precisely what they do represent. – Wooble Dec 09 '13 at 15:20
  • Can you comment on my new answer? What do you want to be specifically be answered for your bounty – Antti Haapala -- Слава Україні Mar 21 '15 at 10:56
  • @AnttiHaapala I dont want anything specifically answered. I just wanted to put a bounty on this question to get more attention to the question. – Loko Mar 21 '15 at 18:52

7 Answers7

14

This isn't a Python issue but an issue with the nature of floating point numbers. Turns out that computers are bad at representing numbers. Who knew?

I recommend reading What Every Computer Scientist Should Know About Floating-Point Arithmetic if you have the time.

Now, as to the actual Python side of this, every object has a method called __str__ and one called __repr__. These are supposed to produce strings to be displayed in various circumstances. You will see these if you use the builtin repr or str functions on any object, or if you use the "%r" or "%s" formats in string formatting. When you evaluate something at the interactive prompt, you get the repr by default. When you pass something to print, you get the str by default.

Floating point number objects have their __repr__s defined in such a way to represent them at maximum precision (attainable in decimal, at least), while their __str__ is defined in such a way that they tend to look more like what you would want to show a user. Users don't want to know that floats aren't real numbers, so it doesn't show that extra precision to them.

kenm
  • 23,127
  • 2
  • 43
  • 62
4

The answer to "what is the difference between str and repr" and even "what does full precision mean", depends on the Python version.


The behaviour of repr(f) changed in 3.1 and 2.7.

  • Before 2.7 (including Python 3.0), repr(f) would give up to 17 significant digits, as if formatted with %17g. An IEEE-754 floating point value has 53 significant binary digits, which is approximately 16 decimal digits. 17 significant digits guarantee that each binary value produce a different decimal value.

  • In Pythons 2.7 and 3.1, the repr(f) was made human-friendly while still keeping the precision:

The repr() of a float x is shorter in many cases: it’s now based on the shortest decimal string that’s guaranteed to round back to x. As in previous versions of Python, it’s guaranteed that float(repr(x)) recovers x.


The behaviour of str(f) was changed in Python 3.2:


The following examples demonstrate changes in repr behaviour. The old behaviour was:

Python 2.6.8 (unknown, Jan 26 2013, 14:35:25) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 3.1415 * 2
6.2830000000000004
>>> 

whereas the new behaviour is:

Python 2.7.3 (default, Mar 13 2014, 11:03:55) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 3.1415 * 2
6.283

The old behaviour for str (before Python 3.2) was to round values to 12 significant digits, losing information:

Python 2.7.3 (default, Mar 13 2014, 11:03:55) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> str(0.1000000000000999)
'0.1'
>>> 0.1 == 0.1000000000000999
False
>>> repr(0.1000000000000999)
'0.1000000000000999'

The new behaviour since Python 3.2 is to behave like repr:

Python 3.2.3 (default, Feb 20 2013, 14:44:27) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> str(0.1000000000000999)
'0.1000000000000999'
>>> repr(0.1000000000000999)
'0.1000000000000999'

The reason for why rounding will occur, is because floating point numbers in Python are represented as IEEE-754 double precision; one number takes 64 bits, with 1 bit reserved for sign, 10 for exponent and 53 for the mantissa (the actual numbers).

Many values, such as π or 1/3 cannot be accurately represented as an IEEE-754 binary floating point value. Even such a common number as 0.01 cannot be represented exactly.

The Python 3 floats have the method hex() that will convert the number to hex representation, which can be used to easily see the problem:

>>> (0.01).hex()
'0x1.47ae147ae147bp-7'

So, as hex, the number 0.01 would be approximated in binary as 1.47AE147AE147A4147AE... · 2-7; rounded to the closest number in 53 significant bits, this is represented as 1.47AE147AE147B · 2-7


I have written some more gory details on how repr works in 2.7, 3.1 in my answer to question Precision of repr(f), str(f), print(f) when f is float.

Community
  • 1
  • 1
3

Basically, the computer's floating point calculations have rounding errors. So if you perform 1.*1000./1000., you can end up with 1.0000004 or something like that. It is what the computer stores in the memory. However, you probably don't want to see 1.0000004 as a result of that calculation. So when you print the result, the computer does the rounding, and you will get simply 1. But you have to know that it is not the real value in the computer's memory - it is only a comfortable visualization of your actual floating point number.

leeladam
  • 1,748
  • 10
  • 15
2

By full precision they mean with all decimals digits the number is stored as. Because of how numbers are stored on the computer (in binary), this will often not be 100% accurate.

M4rtini
  • 13,186
  • 4
  • 35
  • 42
  • leeladam's answer basicly says the same thing. Rounding errors to make it fit in the binary representation, causes floating points numbers to not always be 100% accurate. – M4rtini Dec 09 '13 at 15:23
  • Yep, since we use a decimal system and the computer a binary. A float which has a finite number of digits behind the decimal point in one system may have an infinite numbers of them in the other – Jörg Mäder Dec 09 '13 at 15:24
  • Yes, this is correct. What this means is, when representing a decimal floating point value using a fixed number of bits, you will generally get something that doesn't exactly match the original value. This represents some "loss of information", which shows up as a difference from the original value when you ask Python to print the number again in decimal. – Peter Dec 09 '13 at 15:26
  • Thanks for the answer but I would say leeladam's answer explains it better. – Loko Dec 09 '13 at 15:26
  • Okay I was wondering something else as well now. this:print(0.1+0.1+0.1-0.3) gives me 5.55111512313e-17 is this the same thing? – Loko Dec 10 '13 at 08:16
  • 1
    Yup. same thing. if you do print(0.1 + 0.1 + 0.1) you'll see it is 0.300000....4. so when you subtract 0.3 you get a very small number that is close to zero, but not quite. – M4rtini Dec 10 '13 at 09:07
  • If you try to calculate the sequence: x[0] = 1.0, x[1] = 0.5, x[n] = (5.0/2) * x[n-1] - x[n-2] This sequence should go to zero. But after around 3200+ iterations, Rounding errors will make it go to - infinity. This shows why it's important to be aware of this. – M4rtini Dec 10 '13 at 09:34
2

The book you're reading is imprecise. If you truly want to see a float to full precision, use the decimal module:

>>> import decimal
>>> decimal.Decimal(3.1415 * 2)
Decimal('6.28300000000000036237679523765109479427337646484375')

Every (finite) binary float can be exactly represented as a (finite) decimal float. The converse is not true - in fact, most decimal floats cannot be exactly represented as (finite) binary floats.

The difference for older versions of CPython is that repr(a_float) produced 17 significant decimal digits. While proving this is difficult, it turns out that 17 significant decimal digits is enough so that eval(repr(a_float)) == a_float is always true for floats implemented as IEEE-754 "double precision" binary floats (which virtually all machines now use) - and 16 significant decimal digits is not enough. 17 is not "full precision", it's "enough precision so that round-tripping always works".

In current versions of CPython, repr(a_float) produces the shortest decimal string such that eval(repr(a_float)) == a_float. That's much harder to get right. For "random" floats it's likely still to produce 17 decimal digits, but for the "simple floats" people tend to enter by hand, it's likely to produce the same string you typed in.

Tim Peters
  • 67,464
  • 13
  • 126
  • 132
  • Did you ever read the book btw? There's another edition out I was thinking about reading but the book is just bad quality apparently? – Loko Mar 18 '15 at 09:27
1

This can be a rather confusing issue! Mathematically 3.1415 * 2 = 6.283, but in floating-point arithmetic, small errors are introduced because of rounding off. Most systems that display results from such a calculation automatically correct for this and give you the result you expect. In python, this happens when you print a number. repr, on the other hand shows it as it is, including the tiny error. Usually the error is so small that it is not worth worrying about, but if you're working in a high-precision environment then you might prefer the decimal module which avoids the error.

aquavitae
  • 17,414
  • 11
  • 63
  • 106
0

There two special methods that python classes should define: __repr__ and __str__. The first is called when you want an "exact" representation of the object itself. Which means that if you copy/paste the output of __repr__ you will get exactly the same object. However this representation is not always human readable (especially when dealing with more complex object), so there is __str__. Its function is to give a textual representation of the value of the object which is human-friendly.

smeso
  • 4,165
  • 18
  • 27