Python - round a float to 2 digits - python

I would need to have a float variable rounded to 2 significant digits and store the result into a new variable (or the same of before, it doesn't matter) but this is what happens:
>>> a
981.32000000000005
>>> b= round(a,2)
>>> b
981.32000000000005
I would need this result, but into a variable that cannot be a string since I need to insert it as a float...
>>> print b
981.32
Actually truncate would also work I don't need extreme precision in this case.

What you are trying to do is in fact impossible. That's because 981.32 is not exactly representable as a binary floating point value. The closest double precision binary floating point value is:
981.3200000000000500222085975110530853271484375
I suspect that this may come as something of a shock to you. If so, then I suggest that you read What Every Computer Scientist Should Know About Floating-Point Arithmetic.
You might choose to tackle your problem in one of the following ways:
Accept that binary floating point numbers cannot represent such values exactly, and continue to use them. Don't do any rounding at all, and keep the full value. When you wish to display the value as text, format it so that only two decimal places are emitted.
Use a data type that can represent your number exactly. That means a decimal rather than binary type. In Python you would use decimal.

Try this :
Round = lambda x, n: eval('"%.' + str(int(n)) + 'f" % ' + repr(x))
print Round(0.1, 2)
0.10
print Round(0.1, 4)
0.1000
print Round(981,32000000000005, 2)
981,32
Just indicate the number of digits you want as a second kwarg

I wrote a solution of this problem.
Plz try
from decimal import *
from autorounddecimal.core import adround,decimal_round_digit
decimal_round_digit(Decimal("981.32000000000005")) #=> Decimal("981.32")
adround(981.32000000000005) # just wrap decimal_round_digit
More detail can be found in https://github.com/niitsuma/autorounddecimal

There is a difference between the way Python prints floats and the way it stores floats. For example:
>>> a = 1.0/5.0
>>> a
0.20000000000000001
>>> print a
0.2
It's not actually possible to store an exact representation of many floats, as David Heffernan points out. It can be done if, looking at the float as a fraction, the denominator is a power of 2 (such as 1/4, 3/8, 5/64). Otherwise, due to the inherent limitations of binary, it has to make do with an approximation.
Python recognizes this, and when you use the print function, it will use the nicer representation seen above. This may make you think that Python is storing the float exactly, when in fact it is not, because it's not possible with the IEEE standard float representation. The difference in calculation is pretty insignificant, though, so for most practical purposes it isn't a problem. If you really really need those significant digits, though, use the decimal package.

Related

How to fix incorrect decimal places of a multiplication result

I'm having a problem with the following calculation:
>>> print(float('32.31') * 1e9)
32310000000.000004
I need this result to be 32310000000.0 (without the false decimal place). This also occurs when using a float directly:
>>> print(32.31 * 1e9)
32310000000.000004
Is there a way to avoid the false decimal place? Analysing the string and rounding to the number of decimal places is not a pefered solution.
Many thanks in advance.
NOTE: The following works fine:
>>> print(32.32 * 1e9)
32320000000.0
so I'm really happy I found the problem above during testing.
EDIT: Thank you for your quick answers! Sorry, I've missed an important point. The method must also work for when the result is less than one, e.g.:
32.31 * 1e-9
...in this case I cannot use round(32.31 * 1e-9, 1)
One way to avoid your problem is to use the decimal module, which works in base ten and thus works the way humans would work (if we were much faster).
from decimal import Decimal
value = float(Decimal('32.31') * Decimal(1e9))
This yields the value you want,
32310000000.0
Another way is to use the fractions module, which works with exact values:
from fractions import Fraction
value = float(Fraction('32.31') * Fraction(1e9))
Note that in both of these methods, we must convert 1e9 and not just the decimal value. Using 1e9 as a float converts the intermediate values to float and the approximation problem pops up again. In either method, you could leave off the final conversion to float type and just continue to work with a Decimal or Fraction value. Either of these methods is somewhat slower than using float types: you gain accuracy at the expense of speed. That speed decrease may matter in some situations.
Regarding your edit to your question: using the value 1e-9 in either of my methods will still result in what you want, namely the value 3.231e-08.
If you are just interested in printing with a single decimal place, you can use round by specifying the number after decimal you want to have, in this case 1
print(round(float('32.31') * 1e9, 1))
# 32310000000.0
This is caused by the representation limitation of the floats's binary mantissa. 32.31 cannot be represented by a division by a power of two. If you know your values will always be integers (or a known number of decimal places), you can use round() or int(round()) to circumvent the limitation.
I'm going to assume you're using Python 3.
If you only care about the result being printed, use string formatting:
print("{r:1.2f}".format(r=32.31 * 1e9))
Another way to do it is using the older formatting syntax:
print("%1.1f" % (32 * 1e9))
I prefer the first, but either will work.

What is the meaning of this following piece of code({:.3f}) used in a Decision Tree tutorial? [duplicate]

I don't understand why, by formatting a string containing a float value, the precision of this last one is not respected. Ie:
'%f' % 38.2551994324
returns:
'38.255199'
(4 signs lost!)
At the moment I solved specifying:
'%.10f' % 38.2551994324
which returns '38.2551994324' as expected… but should I really force manually how many decimal numbers I want? Is there a way to simply tell to python to keep all of them?! (what should I do for example if I don't know how many decimals my number has?)
but should I really force manually how many decimal numbers I want? Yes.
And even with specifying 10 decimal digits, you are still not printing all of them. Floating point numbers don't have that kind of precision anyway, they are mostly approximations of decimal numbers (they are really binary fractions added up). Try this:
>>> format(38.2551994324, '.32f')
'38.25519943239999776096738060005009'
there are many more decimals there that you didn't even specify.
When formatting a floating point number (be it with '%f' % number, '{:f}'.format(number) or format(number, 'f')), a default number of decimal places is displayed. This is no different from when using str() (or '%s' % number, '{}'.format(number) or format(number), which essentially use str() under the hood), only the number of decimals included by default differs; Python versions prior to 3.2 use 12 digits for the whole number when using str().
If you expect your rational number calculations to work with a specific, precise number of digits, then don't use floating point numbers. Use the decimal.Decimal type instead:
Decimal “is based on a floating-point model which was designed with people in mind, and necessarily has a paramount guiding principle – computers must provide an arithmetic that works in the same way as the arithmetic that people learn at school.” – excerpt from the decimal arithmetic specification.
Decimal numbers can be represented exactly. In contrast, numbers like 1.1 and 2.2 do not have exact representations in binary floating point. End users typically would not expect 1.1 + 2.2 to display as 3.3000000000000003 as it does with binary floating point.
I would use the modern str.format() method:
>>> '{}'.format(38.2551994324)
'38.2551994324'
The modulo method for string formatting is now deprecated as per PEP-3101

Weird behaviour for Python is_integer from floor

When checking if a floor is an int, the recommend method would be is_integer:
However, I get a weird behaviour with the results of the log function:
print(log(9,3)); #2.0
print((log(9,3)).is_integer()); #True
print((log(243,3))); #5.0
print((log(243,3)).is_integer()); #False
Furthermore:
print((int) (log(9,3))); #2
print((int) (log(243,3))); #4
Is this normal?
log(243,3) simply doesn't give you exactly 5:
>>> '%.60f' % log(243,3)
'4.999999999999999111821580299874767661094665527343750000000000'
As the docs say, log(x, base) is "calculated as log(x)/log(base)". And neither log(243) nor log(3) can be represented exactly, and you get rounding errors. Sometimes you're lucky, sometimes you're not. Don't count on it.
When you want to compare float numbers, use math.isclose().
When you want to convert a float number that is close to an integer, use round().
Float numbers are too subject to error for "conventional" methods to be used. Their precision (and the precision of functions like log) is too limited, unfortunately. What looks like a 5 may not be an exact 5.
And yes: it is normal. This is not a problem with Python, but with every language I'm aware of (they all use the same underlying representation). Python offers some ways to work around float problems: decimal and fractions. Both have their own drawbacks, but sometimes they help. For example, with fractions, you can represent 1/3 without loss of precision. Similarly, with decimal, you can represent 0.1 exactly. However, you'll still have problems with log, sqrt, irrational numbers, numbers that require many digits to be represented and so on.

subtracting integer python weird results

basically what I'm doing is downloading some date from a website using urllib. That number comes to be in what I believe is Byte form. So I change it to an integer by doing the following. This seems to work fine.
real_value = (int(real_value) / 100)
Then I create another variable which should equal the difference between two values.
add_to_value = real_value - last_real_value
print(add_to_value)
The weird thing is, this sometimes works and other times I get results with a lot of extra digits on the end or it will say "9.999999999999996e-05".
So I'm really confused. Any ideas?
Floating-point numbers can't represent most numbers exactly. Even with a very simple example:
>>> 0.1 + 0.1
0.20000000000000001
You can see it's not exact. If you use floating-point numbers, this is just something you'll have to deal with. Alternatively, you can use Python's decimal module:
>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.1')
Decimal('0.2')
Even decimal can't represent every number exactly, but it should give you much more reasonable results when dealing with lots of base-10 operations.
read up on issues with floating points in python
assuming you are yousing python3: you may want to use a double / for classic python2's 'integer division' behaviour where the result gets rounded.
real_value = (int(real_value) // 100)
The weired values are normal and should be correct.
This is because you are using floating point arithmetic. You can always limit the precision of the results, by, e.g., setting the number of digits that are used for the representation.
Refer to: http://en.wikipedia.org/wiki/Floating_point

Python default behavior of str(x)

I am depending on some code that uses the Decimal class because it needs precision to a certain number of decimal places. Some of the functions allow inputs to be floats because of the way that it interfaces with other parts of the codebase. To convert them to decimal objects, it uses things like
mydec = decimal.Decimal(str(x))
where x is the float taken as input. My question is, does anyone know what the standard is for the 'str' method as applied to floats?
For example, take the number 2.1234512. It is stored internally as 2.12345119999999999 because of how floats are represented.
>>> x = 2.12345119999999999
>>> x
2.1234511999999999
>>> str(x)
'2.1234512'
Ok, str(x) in this case is doing something like '%.6f' % x. This is a problem with the way my code converts to decimals. Take the following:
>>> d = decimal.Decimal('2.12345119999999999')
>>> ds = decimal.Decimal(str(2.12345119999999999))
>>> d - ds
Decimal('-1E-17')
So if I have the float, 2.12345119999999999, and I want to pass it to Decimal, converting it to a string using str() gets me the wrong answer. I need to know what are the rules for str(x) that determine what the formatting will be, because I need to determine whether this code needs to be re-written to avoid this error (note that it might be OK, because, for example, the code might round to the 10th decimal place once we have a decimal object)
There must be some set of rules in python's docs that hopefully someone here can point me to. Thanks!
In the Python source, look in "Include/floatobject.h". The precision for the string conversion is set a few lines from the top after an comment with some explanation of the choice:
/* The str() precision PyFloat_STR_PRECISION is chosen so that in most cases,
the rounding noise created by various operations is suppressed, while
giving plenty of precision for practical use. */
#define PyFloat_STR_PRECISION 12
You have the option of rebuilding, if you need something different. Any changes will change formatting of floats and complex numbers. See ./Objects/complexobject.c and ./Objects/floatobject.c. Also, you can compare the difference between how repr and str convert doubles in these two files.
There's a couple of issues worth discussing here, but the summary is: you cannot extract information that is not stored on your system already.
If you've taken a decimal number and stored it as a floating point, you'll have lost information, since most decimal (base 10) numbers with a finite number of digits cannot be stored using a finite number of digits in base 2 (binary).
As was mentioned, str(a_float) will really call a_float.__str__(). As the documentation states, the purpose of that method is to
return a string containing a nicely printable representation of an object
There's no particular definition for the float case. My opinion is that, for your purposes, you should consider __str__'s behavior to be undefined, since there's no official documentation on it - the current implementation can change anytime.
If you don't have the original strings, there's no way to extract the missing digits of the decimal representation from the float objects. All you can do is round predictably, using string formatting (which you mention):
Decimal( "{0:.5f}".format(a_float) )
You can also remove 0s on the right with resulting_string.rstrip("0").
Again, this method does not recover the information that has been lost.

Categories

Resources