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:
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.
The Problem Since real numbers cannot be represented accurately in a fixed space, when operating with floating-point numbers, the result might not be able to be fully represented with the required precision. This inaccuracy ends up as information lost.
Floating-point decimal values generally do not have an exact binary representation. This is a side effect of how the CPU represents floating point data. For this reason, you may experience some loss of precision, and some floating-point operations may produce unexpected results.
Floating-point representations are not necessarily unique. For example, both 0.01 × 101 and 1.00 × 10-1 represent 0.1. If the leading digit is nonzero (d0 0 in equation (1) above), then the representation is said to be normalized. The floating-point number 1.00 × 10-1 is normalized, while 0.01 × 101 is not.
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.
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 afloat
x
is shorter in many cases: it’s now based on the shortest decimal string that’s guaranteed to round back tox
. As in previous versions of Python, it’s guaranteed thatfloat(repr(x))
recoversx
.
The behaviour of str(f)
was changed in Python 3.2:
In 2.x, 3.0 and 3.1: str(f)
gives decimal values rounded to only 12 significant digits, as if formatted with %12g
; the precision was controlled by the PyFloat_STR_PRECISION
macro in Objects/floatobject.h.
In 3.2+, str(f)
behaves identically to repr(f)
- as repr
output since 3.1 was significantly more human-friendly, and since the str(f)
was losing precision, it was decided that starting from Python 3.2 str(f)
should be identical to repr(f)
.
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 float
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With