Another similar solution:
import decimal
def round_normal(number, digits=0):
decimal.getcontext().rounding = decimal.ROUND_HALF_UP
return round(decimal.Decimal(str(number)), digits)
or if you are going for speed
import decimal
decimal.getcontext().rounding = decimal.ROUND_HALF_UP
def round_normal(number, digits=0):
return round(decimal.Decimal(str(number)), digits)
if you need a result in float, then put float around the whole part.
It takes longer to construct the string format for quantize.
- Why not use local context?
If you prefer to change the context just locally, you could also do:
def round_normal(number, digits=0):
with decimal.localcontext() as ctx:
ctx.rounding = decimal.ROUND_HALF_UP
return round(decimal.Decimal(str(number)), digits)
but the time to execute this is again much higher.
same for the abs math.floor else math.ceil solution (a bit more runtime)
So, why is this cast from float to string needed? didn't the other solutions without input -> str -> decimal work faster?
Yes, it is faster with less conversions, however, as mentioned there is a problem with some float numbers. here an example:
> a = Decimal(1.125); b = Decimal("1.125"); a == b
True
> a = Decimal(1.255); b = Decimal("1.255"); a == b
False
> a
Decimal('1.25499999999999989341858963598497211933135986328125')
> b
Decimal('1.255')
This means that even if we convert to Decimal, if our input is a float and already changed to a close but not fully matching number, then it does not help at all!
Therefore, we can go the approach to convert to str and then to Decimal:
> c = Decimal("1.255")
> c
Decimal('1.255')
if we just go with the float 1.255 or use this in a Decimal (so it keeps its error), then the calculation goes wrong!
Here the example:
> round(1.255, 2)
1.25
> round(Decimal(1.255), 2)
Decimal('1.25')
> round(Decimal(str(1.255)), 2)
Decimal('1.26')
But keep in mind, the float -> str -> Decimal works, since the string representation of a float has not such a high precision, so the number is kind of rounded.
The solution helps to not need to change number formats in your other code, but cleanest way would be to work with Decimal everywhere! Then there would also no need for those conversions
round()
but you could usemath.ceil()
if you always want to round up – Aeolic1.3
to be rounded down to1
, so I can not useceil()
. – Interlanguage5*10**-k
depends on the digit preceding it. By rounding up for uneven digits and down for even digits, you get a positive error half the time and an even error half the time (in theory). When you perform many additions, those errors can cancel each-other – Kanzu