How to round 0.055 to 0.06 in Python? - python

Result of a simple division -34.11/2 returns -17.055 Is there a python function that would return round up decimal? I am expecting to see -17.06
And yes I checked these answers, but neither can answer this question:
Round up to Second Decimal Place in Python
How to round to nearest decimal in Python
Increment a Python floating point value by the smallest possible amount
Python round to nearest 0.25
Any help would be greatly appreciated.
Thanks!

This is a floating point limitation; get around it by using decimal.Decimal.
>>> from decimal import Decimal
>>> Decimal(-34.11/2) # See? Not quite the value you thought.
Decimal('-17.05499999999999971578290569595992565155029296875')
>>> Decimal(-34.11) / Decimal(2) # This doesn't quite work either, because -34.11 is already a little bit off.
Decimal('-17.05499999999999971578290570')
>>> Decimal("-34.11") / Decimal("2")
Decimal('-17.055')
>>> round(Decimal("-34.11") / Decimal("2"), 2)
Decimal('-17.06')

Related

Python Round not working as expected for some values [duplicate]

This question already has answers here:
How to properly round-up half float numbers?
(21 answers)
Closed 7 months ago.
In Python 3, I'm trying to round the value 4800.5, so I was expecting it to 4801
but it's giving me 4800. I'm not able to track why this is happening.
Any help will be appreciated.
That's by design.
If you have a look at round function documentation (https://docs.python.org/3/library/functions.html#round) you will find that:
For the built-in types supporting round(), values are rounded to
the closest multiple of 10 to the power minus ndigits; if two
multiples are equally close, rounding is done toward the even choice
(so, for example, both round(0.5) and round(-0.5) are 0, and
round(1.5) is 2).
In simple words, 0.5 is a special case which is always rounded toward an even number.
But there're more interesting things. Please have a look at that example:
The behavior of round() for floats can be surprising: for
example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This
is not a bug: it’s a result of the fact that most decimal fractions
can’t be represented exactly as a float.
What you might want to do is to use Decimal for more conventional rounding logic: https://docs.python.org/3/library/decimal.html
For example:
>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_UP)
Decimal('7.33')
There are lot of ways to round a number. round() behaves according to a particular rounding strategy which may or may not be the one you need for a given situation (see the first comment to your question).
If you want to round your number to the upper, you can try this:
import math
n = 4800.5
print(math.ceil(n))
you can do something like this:
from decimal import Decimal, ROUND_HALF_UP
def round_half_up(decimal_number, places=0):
if places == 0:
exp = Decimal('1')
else:
exp_str = '0' * places
exp_str = exp_str[:-1] + '1'
exp = Decimal('.{}'.format(exp_str))
return Decimal(decimal_number).quantize(exp, rounding=ROUND_HALF_UP)
print(round_half_up(4800.5)) -> 4801
print(round_half_up(4800.555, 2)) -> 4800.56
Round() function will round up to next value, if decimal is >.5
upto .5 it would round up to just the integer part.

expected value float, decimal rounding error

I'm trying to round to two decimal places with error.
How could I round, eg:
7.1450, should be: 7.15
7.144, should be 7.14
>>> round(7.1450,2)
7.14
I will appreciate your help, I have been looking for a long time, without finding the solution.
I just found the answer of this problem in the Python docs.
Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.
If you need more accurate method, you can use decimal module.
>>> import decimal
>>> decimal.getcontext().rounding = decimal.ROUND_HALF_UP # change round setting of decimal.
>>> round(decimal.Decimal('7.1450'), 2)
Decimal('7.15')
The decimal module lets you control what rounding mode is used. You're looking for ROUND_HALF_UP.
I'm not too familiar with decimal myself, but this is the simplest way I found to do it:
import decimal
with decimal.localcontext() as ctx:
ctx.rounding = decimal.ROUND_HALF_UP
a = decimal.Decimal('7.145')
print(round(a, 2)) # -> 7.15
You can add a very small value to make it a bit more than .5.
Here I use sys.float_info.min, which is the minimum representable positive normalized float:
import sys
round(4.115 + sys.float_info.min, 2)
How about this
def round2(number, digit=0):
return int(number*10**digit+0.5)/10**digit
Python stores a decimal in base 2 format. For example,
0.5 is stored as (1/2).
0.75 is stored as (1/2) + (1/4).
You observed? All decimals cannot be stored in this way. So, It maintains a large precision. For example, (0.1) cannot be stored exactly in this way. It rounds to nearest possible decimal.
For example, 7.1450 is stored as 7.144999999999999573674358543939888477325439453125. So, Naturally, when round() is applied on this It gives 7.14.
So, How to resolve this?
Add a very low value (Say 0.000000001) to your decimals before rounding it.
It does the job.
try this
import numpy as np
x = 2.78
print(np.round(x,1))
It will give desire output such as
>>> print(np.round(x,1))
2.8
And also try this.
import math
def round_nearest(num, a):
return round(round(num / a) * a, -int(math.floor(math.log10(a))))
round_nearest(1.145, 0.05)
>>> round_nearest(1.145, 0.05)
1.15

sometimes missing a cent when translating euros to euro cents

I have to translate euro's (in a string) to euro cents (int):
Examples:
'12,1' => 1210
'14,51' => 1451
I use this python function:
int(round(float(amount.replace(',', '.')), 2) * 100)
But with this amount '1229,84' the result is : 122983
Update
I use the solution from Wim, bacause I use integers in both Python / Jinja and javascript for currency artitmetic. See also the answer from Chepner.
int(round(100 * float(amout.replace(',', '.')), 2))
My questions was anwered by Mr. Me, who explained the above result.
What the Docs Say, and a simple explanation
I tried it out, and was surprised that this was happening. So I turned to the documentation, and there is a little note in there that says.
Note The behavior of round() for floats can be surprising: for
example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This
is not a bug: it’s a result of the fact that most decimal fractions
can’t be represented exactly as a float.
Now what does that mean, most decimal fractions can't be represented as a float. Well the documentations follows up with a great link at explains this, but since you probably didn't come here to read a nerdy technical document, let me summarize what is going on.
Python uses the IEEE-754 floating point standard to represent floats. This standard compromises accuracy for speed. Some numbers cannot be accurately represented. For example .1 is actually represented as 0.1000000000000000055511151231257827021181583404541015625. Interestingly, .1 in binary is actually an infinitely repeating number, just like 1/3 is an infinitely repeating .333333.
An Under the Hood Case Study
Now on to your particular case. This was pretty fun to look into, and this is what I discovered.
first lets simplify what you where trying to do
>>> amount = '1229,84'
>>> int(round(float(amount.replace(',', '.')), 2) * 100)
>>> 122983
to
>>>int(1229.84 * 100)
>>> 122983
Sometimes Python1 is unable to 100% accurately display binary floating point numbers, for the same reason we are unable to display the fraction 1/3 as a decimal. When this happens Python hides any extra digits. .1 is actually stored as -0.100000000000000092, but Python will display it as .1 if you type it into the console. We can see those extra digits by doing int(1.1) - 1.13. we can apply this int(myNum) - myNum formula to most floating point numbers to see the extra hidden digits behind them.4. In your case we would do the following.
>>> int(1229.84) - 1229.84
-0.8399999999999181
1229.84 is actually 1229.8399999999999181. Continuing on.5
>>> 1229.84, 2) * 100
122983.99999999999 #there's all of our hidden digits showing up.
Now on to the last step. This is the part we are concerned about. Changing it back to an integer.
>>> int(122983.99999999999)
122983
It rounds downwards instead of upwards, however, if we never had multiplied it by 100, we would still have 2 more 9s at the end, and Python would round up.
>>> int(122983.9999999999999)
122984
??? Now what is going on. Why is Python rounding 122983.99999999999 down, but it rounds 122983.9999999999999 up? Well whenever Python turns a float into a integer it rounds down. However, you have to remember that to Python 122983.9999999999999 with the extra two 99s at the end is the same thing as 122984.0 For example.
>>> 122983.9999999999999
122984.0
>>> a = 122983.9999999999999
>>> int(a) - a
0.0
and without the two extra 99s on the end.
>>> 122983.99999999999
122983.99999999999
>>> a=122983.99999999999
>>> int(a) - a
-0.9999999999854481
Python is definitely treating 122983.9999999999999 as 122984.0 but not 122983.99999999999. Now back to casting 122983.99999999999 to an integer. Because we have created ourselves a decimal portion that is less than 122984 that Python sees as being a seperate number from 122984, and because casting to an integer always causes Python to round down, we get 122983 as a result.
Whew. That was a lot to go through, but I sure learned a lot writing this out, and I hope you did to. The solution to all of this is to use decimal numbers instead of floats which compromises speed for accuracy.
What about rounding? The original problem had some rounding in it as well -- it's useless. See appendix item 6.
The Solution
a) The easiest solution is to use the decimal module instead of floating point numbers. This is the preferred way of doing things in any finance or accounting program.
The documentation also mentioned the following solutions which I've summarized.
b) The exact value can be expressed and retrieved in a hexadecimal form via myFloat.hex() and float.fromhex(myHex)
c) The exact value can also be retrieved as a fraction through myFloat.as_integer_ratio()
d) The documentation briefly mentions using SciPy for floating point arithmitic, however this SO question mentions that SciPy's NumPy floats are nothing more than aliases to the built-in float type. The decimal module would be a better solution.
Appendix
1 - Even though I will often refer to Python's behavior, the things I talk about are part of the IEEE-754 floating point standard which is what the major programming languages use for their floating point numbers.
2 - int(1.1) - 1.1 gives me -0.10000000000000009, but according to the documentation .1 is really 0.1000000000000000055511151231257827021181583404541015625
3 - We used int(1.1) - 1.1 instead of int(.1) - .1 because int(.1) - .1 does not give us the hidden digits, but according to the documentation they should still be there for .1, hence I say int(someNum) -someNum works most of the time, but not all of the time.
4 - When we use the formula int(myNum) - myNum what is happening is that casting the number to an integer will round the number down so int(3.9) becomes 3, and when we minus 3 from 3.9 we are left with -.9. However, for some reason that I do not know, when we get rid of all the whole numbers, and we're just left with the decimal portion, Python decides to show us everything -- the whole mantissa.
5 - this does not really affect the outcome of our analysis, but when multiplying by 100, instead of the hidden digits being shifted over by 2 decimal places, they changed a little as well.
>>> a = 1229.84
>>> int(a) - a
-0.8399999999999181
>>> a = round(1229.84, 2) * 100
>>> int(a) - a
-0.9999999999854481 #I expected -0.9999999999918100?
6 - It may seem like we can get rid of all those extra digits by rounding to two decimal places.
>>> round(1229.84, 2) # which is really round(1229.8399999999999181, 2)
1229.84
But when we use our int(someNum) - someNum formula to see the hidden digits, they are still there.
>>> a = round(1229.84, 2)
>>> int(a) - a
-0.8399999999999181
This is because Python cannot store 1229.84 as a binary floating point number. It can't be done. So... rounding 1229.84 does absolutely nothing.
Don't use floating-point arithmetic for currency; rounding error for values that cannot be represented exactly will cause the type of loss you are seeing. Instead, convert the string representation to an integer number of cents, which you can convert to euros-and-cents for display as needed.
euros, cents = '12,1'.split(',') # '12,1' -> ('12', '1')
cents = 100*int(euros) + int(cents * 10 if len(cents) == 1 else 1) # ('12', '1') -> 1210
(Notice you'll need a check to handle cents without a trailing 0.)
display_str = '%d,%d' % divMod(cents, 100) # 1210 -> (12, 10) -> '12.10'
You can also use the Decimal class from the decimal module, which essentially encapsulates all the logic for using integers to represent fractional values.
As #wim mentions in a comment, use the Decimal type from the stdlib decimal module instead of the built in float type. Decimal objects do not have the binary rounding behavior that floats have and also have a precision that can be user defined.
Decimal should be used anywhere you are doing financial calculations or anywhere you need floating point calculations that behave like the decimal math people learn in school (as opposed to the binary floating point behavior of the built in float type).

Python 3.0+ setting to max 4 decimal and rounding

So I have an assignment to match an expected output: 34.7381. But I am getting 34.73809999999999. I realize that it is just a rounded number with only 4 decimal places but I have no clue how to that in a short step.
I could brute force it and check for the placement of the decimal point, checking how many characters are after it, eliminating all of them and doing the rounding myself. But that seems rather complicated and stupid for something so simple.
Thanks you for your help!
Have you tried round function?
a = 34.73809999999999
print round(a, 4)
>>> 34.7381
You can use string formatting:
>>> '{:0.4f}'.format(34.7381)
'34.7381'
>>> '{:0.4f}'.format(34.73809999999999)
'34.7381'
Don't worry about it, and just specify the precision you want in your printing:
>>> print('{:.4f}'.format(34.73809999999999))
34.7381

Simple Basic Python compare

I found this interesting question when I was doing homework
we know, 47.36/1.6**2 == 18.5
but when I try to run the following code, it gives me a False(should be true)
print 47.36/1.6**2 == 18.5
Do anyone know what's going on?
You're probably getting an answer like 18.49999999999, which is not exactly equal to 18.5.
As always, the relevant reference for this is What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Short answer: IEEE 754 floating point can't exactly represent fractions where the denominator isn't a power of two, like 1/4, 1/16, 1/256, etc. You can get awfully close, given enough digits, but never quite exactly there.
You compare floating point numbers by defining "equals" as "within a certain delta". You could write something like:
def almost_equals(a, b, delta=0.0005):
return abs(a - b) <= delta
and then test for "probably equal" with:
>>> almost_equals(47.36/1.6**2, 18.5)
True
I would avoid checking for exact equality when comparing two floats. Instead take the difference and see if it is smaller than a value you consider close to zero.
(47.36/1.6**2 - 18.5) < 0.00000000001
will be
True
>>> 47.36/1.6**2
18.499999999999996
See this page on Floating Point Arithmetic: Issues and Limitations.
Here is how you can calculate this to exactly 18.5 without using any rounding or "close enough" behavior by using the decimal module:
>>> from decimal import Decimal
>>> Decimal('47.36') / Decimal('1.6')**2 == Decimal('18.5')
True
>>> float(Decimal('47.36') / Decimal('1.6')**2) == 18.5
True
As others have said:
>>> 47.36/1.6**2
18.499999999999996
But, this is NOT due to a floating-point arithmetic problem as far as I can tell. Even if you use decimal math by wrapping the operands in Decimal() (after from decimal import Decimal) you will still get Decimal('18.49999999999999772404279952') as the answer.
It's possible I'm using Decimal() wrong here and my result also has some sort of floating point error; however, if I'm correct, that expression flat out does not equal 18.5, no matter what kind of math you use.
Edit: As Greg points out in the comments, the problem with my approach here is that Decimal(1.6) will just convert the float representation of 1.6, inaccuracies intact, into a Decimal. This gives the correct answer:
>>> Decimal('47.36') / Decimal('1.6')**2
Decimal('18.5')
Better still would be to use the fractions module as suggested by Kirk.
47.36/1.6*2 return integer. So 47.36/1.6*2 would be 18, which is not equal to 18.5.
Edit
Sorry about that, actually it is being stored as 18.499999.
You should do this
import numpy as np
print np.around((47.36/1.6**2), decimals=1) == 18.5
This would return True.

Categories

Resources