Why does str () round popups?

The Python built-in str () function prints some weird results when passed in floats with many decimals. Here's what happens:

>>> str(19.9999999999999999) >>> '20.0' 

I expect to receive:

 >>> '19.9999999999999999' 

Does anyone know why? and perhaps a workaround?

Thanks!

+4
source share
4 answers

It is not str() that rounds, it is a fact that you use floats in the first place. Floating types are fast, but have limited accuracy; in other words, they are inaccurate in design. This applies to all programming languages. For more information about floating point butts, please read " What Every Programmer Should Know About Floating Point Arithmetic "

If you want to save and use exact numbers , use the decimal module:

 >>> from decimal import Decimal >>> str(Decimal('19.9999999999999999')) '19.9999999999999999' 
+8
source

A float has 32 bits (at least C). One of these bits is allocated for the character, several allocated for the mantissa and several allocated for the exponent. You cannot match every decimal digit to an infinite number of digits of 32 bits. Therefore, floating point numbers are based on rounding.

If you try str(19.998) , it will probably give you something at least approximate to 19.998, because 32 bits are accurate enough to evaluate this, but something like 19.999999999999999 is too accurate to rate at 32 bits, so it is rounded to the nearest possible value, which is 20.

+6
source

Please note that this is a problem of understanding floating point numbers (fixed length). Most languages ​​do exactly (or very similar to) what Python does.

Python float IEEE 754 64-bit binary floating-point. It is limited to 53 bits of precision, that is, slightly less than 16 decimal digits of accuracy. 19.9999999999999999 contains 18 decimal digits; it cannot be represented exactly as a float . float("19.9999999999999999") displays the closest floating point value, which is the same as float("20.0") .

 >>> float("19.9999999999999999") == float("20.0") True 

If by “many decimals” you mean “many digits after the decimal point”, remember that the same “strange” results happen when there are many decimal digits before the decimal point:

 >>> float("199999999999999999") 2e+17 

If you need full float precision, don't use str (), use the repr () function:

 >>> x = 1. / 3. >>> str(x) '0.333333333333' >>> str(x).count('3') 12 >>> repr(x) '0.3333333333333333' >>> repr(x).count('3') 16 >>> 

Refresh It is interesting how often decimal prescribed as a treatment for the surprise caused by the floating point. This is often accompanied by simple examples like 0.1 + 0.1 + 0.1 != 0.3 . Nobody dwells on the fact that decimal has its share of disadvantages, for example

 >>> (1.0 / 3.0) * 3.0 1.0 >>> (Decimal('1.0') / Decimal('3.0')) * Decimal('3.0') Decimal('0.9999999999999999999999999999') >>> 

True, float limited to 53 binary precision digits. By default, decimal limited to 28 decimal digits of precision.

 >>> Decimal(2) / Decimal(3) Decimal('0.6666666666666666666666666667') >>> 

You can change the limit, but it is still limited by accuracy. You still need to know the characteristics of the number format in order to use it efficiently without “astounding” results, and the extra precision is bought by slow work (unless you use the third-party cdecimal module).

+3
source

For any given binary floating-point number, there is an infinite number of decimal fractions, which are rounded up to this number at the input. Python str goes over to some problems to create the shortest decimal fraction from this set; see the GLS document http://kurtstephens.com/files/p372-steele.pdf for the general algorithm (IIRC uses a fix that in most cases avoids mathematics of arbitrary accuracy). You had to enter a decimal fraction, which is rounded to float (IEEE double), the shortest possible decimal fraction of which does not coincide with the one you entered.

+1
source

All Articles