round up/down float to 2 decimals - python

I would like to design a function f(x : float, up : bool) with these input/output:
# 2 decimals part rounded up (up = True)
f(142.452, True) = 142.46
f(142.449, True) = 142.45
# 2 decimals part rounded down (up = False)
f(142.452, False) = 142.45
f(142.449, False) = 142.44
Now, I know about Python's round built-in function but it will always round 142.449 up, which is not what I want.
Is there a way to do this in a nicer pythonic way than to do a bunch of float comparisons with epsilons (prone to errors)?

Have you considered a mathematical approach using floor and ceil?
If you always want to round to 2 digits, then you could premultiply the number to be rounded by 100, then perform the rounding to the nearest integer and then divide again by 100.
from math import floor, ceil
def rounder(num, up=True):
digits = 2
mul = 10**digits
if up:
return ceil(num * mul)/mul
else:
return floor(num*mul)/mul

You can also perform some mathematical logic if you do not want to use any explicit function as:
def f(num, up):
num = num * 100
if up and num != int(num): # if up and "float' value != 'int' value
num += 1
return int(num) / (100.0)
Here, the idea is if up is True and int value of number is not equal to float value then increase the number by 1. Else it will be same as the original number

math.ceil() rounds up, and math.floor() rounds down. So, the following is an example of how to use it:
import math
def f(x, b):
if b:
return (math.ceil(100*x) / 100)
else:
return (math.floor(100*x) / 100)
This function should do exactly what you want.

Related

Round numbers of different lengths to nearest 0 or 5

How to round numbers of different length size to the nearest zero or five?
Example:
1291 -> 1290
0.069 -> 0.07
1.08 -> 1.1
14 -> 15
6 -> 5
Tried to use round() and math.ceil() / math.floor() but since the numbers are different each time in length I can’t adapt it dynamically, numbers are returning from a function not an array.
Here you are, thanks for the other solutions:
import math
import decimal
def round_half_up(n):
if (str(n).find(".") > 0):
decimalsource = len(str(n).split(".")[1])
base = 10**decimalsource
number = n*base
rounded = 5 * round(number/5)
result = rounded / base
if (result == int(result)):
return int(result)
else:
return result
else:
return 5 * round(n/5)
print(round_half_up(1291))
print(round_half_up(0.069))
print(round_half_up(1.08))
print(round_half_up(14))
print(round_half_up(6))
print(round_half_up(12.121213))
print(round_half_up(12.3))
print(round_half_up(18.))
print(round_half_up(18))
I wrote a code and explained. It seems working. I didn't take into account negative numbers.
import numpy as np
convDict = {
"0":"0",
"1":"0",
"2":"0",
"3":"5",
"4":"5",
"5":"5",
"6":"5",
"7":"5",
"8":"0",
"9":"0"
}
def conv(f):
str_f = str(f)
# if input is like, 12. or 13.0,so actually int but float data type
# We will get rid of the .0 part
if str_f.endswith(".0"):
str_f = str(int(f))
# We need last character, and other body part
last_f = str_f[-1]
body_f = str_f[:-1]
# if last char is 8 or 9 we should increment body last value
if last_f in "89":
# Number of decimals
numsOfDec = body_f[::-1].find('.')
# numsOfDec = -1 means body is integer, we will add 1
if numsOfDec == -1:
body_f = str(int(body_f) + 1)
else:
# We will add 10 ** -numsOfDec , but it can lead some numerical differences like 0.69999, so i rounded
body_f = str(np.round(float(body_f) + 10 ** (-numsOfDec),numsOfDec))
# Finally we round last char
last_f = convDict[last_f]
return float(body_f + last_f)
And some examples,
print(conv(1291))
print(conv(0.069))
print(conv(1.08))
print(conv(14))
print(conv(6))
print(conv(12.121213))
print(conv(12.3))
print(conv(18.))
print(conv(18))
The kinda pythonic way to round dynamically to nearest five would be to use round() function and input number (divided by 5), the index in which you want the rounding to ocurr [-2: hundreds, -1: tens, 0: whole, 1:1/10ths, 2:1/100s] and multiply result by 5. You can calculate this index by finding how many decimal places there are using decimal module.
i = decimal.Decimal('0.069')
i.as_tuple().exponent
-3
Note that this function takes the number as a string and outputs the number
of decimal places in negative.
After getting this number, make it positive, and there you have the calculated index to put into the round function in the beginning.
round(0.069/5, 3) * 5
You need to also check before all this calculation if the number is a full number (meaning no decimal places--17, 290, 34.0) (in that case you shouldn't use above code at all), which you can easily do by using modulus, so the overall function would look like this:
if number % 1 == 0:
return round(int(number)/5)*5
else:
index = decimal.Decimal(str(number)).as_tuple().exponent
return round(number/5, -index)*5
Hope this helped !

Python round scalar up to nearest non-integer value [duplicate]

Suppose I have 8.8333333333333339, and I want to convert it to 8.84. How can I accomplish this in Python?
round(8.8333333333333339, 2) gives 8.83 and not 8.84. I am new to Python or programming in general.
I don't want to print it as a string, and the result will be further used. For more information on the problem, please check Tim Wilson's Python Programming Tips: Loan and payment calculator.
8.833333333339 (or 8.833333333333334, the result of 106.00/12) properly rounded to two decimal places is 8.83. Mathematically it sounds like what you want is a ceiling function. The one in Python's math module is named ceil:
import math
v = 8.8333333333333339
print(math.ceil(v*100)/100) # -> 8.84
Respectively, the floor and ceiling functions generally map a real number to the largest previous or smallest following integer which has zero decimal places — so to use them for 2 decimal places the number is first multiplied by 102 (or 100) to shift the decimal point and is then divided by it afterwards to compensate.
If you don't want to use the math module for some reason, you can use this (minimally tested) implementation I just wrote:
def ceiling(x):
n = int(x)
return n if n-1 < x <= n else n+1
How all this relates to the linked Loan and payment calculator problem:
From the sample output it appears that they rounded up the monthly payment, which is what many call the effect of the ceiling function. This means that each month a little more than 1⁄12 of the total amount is being paid. That made the final payment a little smaller than usual — leaving a remaining unpaid balance of only 8.76.
It would have been equally valid to use normal rounding producing a monthly payment of 8.83 and a slightly higher final payment of 8.87. However, in the real world people generally don't like to have their payments go up, so rounding up each payment is the common practice — it also returns the money to the lender more quickly.
This is normal (and has nothing to do with Python) because 8.83 cannot be represented exactly as a binary float, just as 1/3 cannot be represented exactly in decimal (0.333333... ad infinitum).
If you want to ensure absolute precision, you need the decimal module:
>>> import decimal
>>> a = decimal.Decimal("8.833333333339")
>>> print(round(a,2))
8.83
You want to use the decimal module but you also need to specify the rounding mode. Here's an example:
>>> import decimal
>>> decimal.Decimal('8.333333').quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_UP)
Decimal('8.34')
>>> decimal.Decimal('8.333333').quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
Decimal('8.33')
>>>
A much simpler way is to simply use the round() function. Here is an example.
total_price = float()
price_1 = 2.99
price_2 = 0.99
total_price = price_1 + price_2
If you were to print out total_price right now you would get
3.9800000000000004
But if you enclose it in a round() function like so
print(round(total_price,2))
The output equals
3.98
The round() function works by accepting two parameters. The first is the number you want to round. The second is the number of decimal places to round to.
The easiest way to do this is by using the below function, which is built in:
format()
For example:
format(1.242563,".2f")
The output would be:
1.24
Similarly:
format(9.165654,".1f")
would give:
9.2
If you round 8.8333333333339 to 2 decimals, the correct answer is 8.83, not 8.84. The reason you got 8.83000000001 is because 8.83 is a number that cannot be correctly reprecented in binary, and it gives you the closest one. If you want to print it without all the zeros, do as VGE says:
print "%.2f" % 8.833333333339 #(Replace number with the variable?)
If you want to round, 8.84 is the incorrect answer. 8.833333333333 rounded is 8.83 not 8.84. If you want to always round up, then you can use math.ceil. Do both in a combination with string formatting, because rounding a float number itself doesn't make sense.
"%.2f" % (math.ceil(x * 100) / 100)
Just for the record. You could do it this way:
def roundno(no):
return int(no//1 + ((no%1)/0.5)//1)
There, no need for includes/imports
Here is my solution for the round up/down problem
< .5 round down
> = .5 round up
import math
def _should_round_down(val: float):
if val < 0:
return ((val * -1) % 1) < 0.5
return (val % 1) < 0.5
def _round(val: float, ndigits=0):
if ndigits > 0:
val *= 10 ** (ndigits - 1)
is_positive = val > 0
tmp_val = val
if not is_positive:
tmp_val *= -1
rounded_value = math.floor(tmp_val) if _should_round_down(val) else math.ceil(tmp_val)
if not is_positive:
rounded_value *= -1
if ndigits > 0:
rounded_value /= 10 ** (ndigits - 1)
return rounded_value
# test
# nr = 12.2548
# for digit in range(0, 4):
# print("{} decimals : {} -> {}".format(digit, nr, _round(nr, digit)))
# output
# 0 decimals : 12.2548 -> 12
# 1 decimals : 12.2548 -> 12.0
# 2 decimals : 12.2548 -> 12.3
# 3 decimals : 12.2548 -> 12.25
I have this code:
tax = (tax / 100) * price
and then this code:
tax = round((tax / 100) * price, 2)
round worked for me
Use the decimal module: http://docs.python.org/library/decimal.html
ََََََ
Here is a simple function to do this for you:
def precision(num,x):
return "{0:.xf}".format(round(num))
Here, num is the decimal number. x is the decimal up to where you want to round a floating number.
The advantage over other implementation is that it can fill zeros at the right end of the decimal to make a deciaml number up to x decimal places.
Example 1:
precision(10.2, 9)
will return
10.200000000 (up to 9 decimal points)
Example 2:
precision(10.2231, 2)
will return
10.22 (up to two decimal points)

Power digit sum

I am trying to solve this excercise:
https://projecteuler.net/problem=16
The code is pretty self-explanatory: I calculate 2^n in power(n), and in sum(n), I cut off the last digit of the number. I do this as long as pow > 0. I receive the right solution for 2^15, but for one reason or another, the same code doesn't work for 2^1000. I receive 1889, which is apparently wrong.
def power(n):
power = 2
for x in range(1, n):
power = 2*power
return power
def sum(n):
pow = power(n)
sum = 0
while pow > 0:
modulo = pow%10
sum = sum + modulo
pow = int((pow - modulo)/10)
return sum
def main():
print(int(sum(1000)))
if __name__ == '__main__':
main()
A simple change in your code will give you the correct answer,
def power(n):
power = 2
for x in range(1, n):
power = 2*power
return power
def sum(n):
pow = power(n)
sum = 0
while pow > 0:
modulo = pow%10
sum = sum + modulo
pow = pow//10 # modified line
return sum
def main():
print(int(sum(1000)))
if __name__ == '__main__':
main()
The reason why your example doesn't work is because you are casting the result of a float operation to int. Floats are never precise and when they are very large, they loose precision. Hence if you convert them back to integer, you get a much lower value.
A better function using divmod() is,
def sum(n):
pow = power(n)
sum = 0
while pow > 0:
pow,modulo = divmod(pow,10)
sum = sum + modulo
return sum
Your original solution would have worked in Python 2 because Python 2 and Python 3 handle division differently.
For example print(1/2) gives 0 in Python2, and 0.5 in Python3. In Python3, we use // for floor division (which is what you want here).
Your code doesn't work for any number >= 57
The problem here is very easy to solve.
In python 3 and higher, / is a division that returns a float, while // is an integer division that always returns an integer. Since you are using float division, you are encountering the issues with floating point arithmetic.
More about the issues and limitations.
To solve your problem, change the line
pow = int(pow - modulo)/10
into
pow = int(pow - modulo)//10
or even better, you can just say pow//=10
Isn't python beatiful?
def Power_digit_sum(n):
number = list(str(2**n)) # pow number and convert number in string and list
result= [int(i) for i in number]# convert number in int and
return sum(result) # sum list
print(Power_digit_sum(15)) # result 26
print(Power_digit_sum(1000)) # result 1366

Random rounding to integer in Python

I am looking for a way to round a floating point number up or down to the next integer based on a probability derived from the numbers after the decimal point. For example the floating number 6.1 can be rounded to 6 and to 7. The probability for beeing rounded to 7 is 0.1 and the probability to be rounded to 6 is 1-0.1. So if I run this rounding experiment infinite times, the average of all integer results should be 6.1 again. I don't know if there is a name for such a procedure and if there is already and implemented function in Python.
Of course it'd be very nice if it is possible to round also to e.g. 2 decimal places the same way.
Does that make sense? Any ideas?
Here is a nice one-liner for this. By using the floor function, it will only be rounded up if the random number between 0 and 1 is enough to bring it up to the next highest integer. This method also works with positive and negative numbers equally well.
def probabilistic_round(x):
return int(math.floor(x + random.random()))
Consider the case of a negative input x = -2.25. 75% of the time the random number will be greater than or equal to 0.25 in which case the floor function will result in -2 being the answer. The other 25% of time the number will get rounded down to -3.
To round to different decimal places it can be modified as follows:
def probabilistic_round(x, decimal_places=0):
factor = 10.0**decimal_places
return int(math.floor(x*factor + random.random()))/factor
The probability you're looking for is x-int(x).
To sample with this probability, do random.random() < x-int(x)
import random
import math
import numpy as np
def prob_round(x):
sign = np.sign(x)
x = abs(x)
is_up = random.random() < x-int(x)
round_func = math.ceil if is_up else math.floor
return sign * round_func(x)
x = 6.1
sum( prob_round(x) for i in range(100) ) / 100.
=> 6.12
EDIT: adding an optional prec argument:
def prob_round(x, prec = 0):
fixup = np.sign(x) * 10**prec
x *= fixup
is_up = random.random() < x-int(x)
round_func = math.ceil if is_up else math.floor
return round_func(x) / fixup
x = 8.33333333
[ prob_round(x, prec = 2) for i in range(10) ]
=> [8.3399999999999999,
8.3300000000000001,
8.3399999999999999,
8.3300000000000001,
8.3300000000000001,
8.3300000000000001,
8.3300000000000001,
8.3300000000000001,
8.3399999999999999,
8.3399999999999999]
The most succinct way to do this for non-negative x is:
int(x + random.random())
If for example x == 6.1, then there's a 10% chance that random.random() will be large enough to make x + random.random() >= 7.
Note that if x == 6, then this expression is guaranteed to return 6, because random.random() is always in the range [0, 1).
Update: This method only works for non-negative inputs. For a solution that works for negative numbers, see Chris Locke's answer.
For rounding positive values to integers, you can do this very concisely:
x = int(x) + (random.random() < x - int(x))
This works because Python's bool type is a subclass of int. The value True is equal to 1 and False is equal to 0.
I also came up with a solution based on the binomial function of random and the code already provided by shx2:
def prob_round(x, prec = 0):
fixup = np.sign(x) * 10**prec
x *= fixup
round_func = int(x) + np.random.binomial(1,x-int(x))
return round_func/fixup
Here's an easy way:
x = round(random.random()*100)
The *100 bit means 1 to 100.
If *200, it means 1 to 200.

Checking if float is equivalent to an integer value in python

In Python 3, I am checking whether a given value is triangular, that is, it can be represented as n * (n + 1) / 2 for some positive integer n.
Can I just write:
import math
def is_triangular1(x):
num = (1 / 2) * (math.sqrt(8 * x + 1) - 1)
return int(num) == num
Or do I need to do check within a tolerance instead?
epsilon = 0.000000000001
def is_triangular2(x):
num = (1 / 2) * (math.sqrt(8 * x + 1) - 1)
return abs(int(num) - num) < epsilon
I checked that both of the functions return same results for x up to 1,000,000. But I am not sure if generally speaking int(x) == x will always correctly determine whether a number is integer, because of the cases when for example 5 is represented as 4.99999999999997 etc.
As far as I know, the second way is the correct one if I do it in C, but I am not sure about Python 3.
There is is_integer function in python float type:
>>> float(1.0).is_integer()
True
>>> float(1.001).is_integer()
False
>>>
Both your implementations have problems. It actually can happen that you end up with something like 4.999999999999997, so using int() is not an option.
I'd go for a completely different approach: First assume that your number is triangular, and compute what n would be in that case. In that first step, you can round generously, since it's only necessary to get the result right if the number actually is triangular. Next, compute n * (n + 1) / 2 for this n, and compare the result to x. Now, you are comparing two integers, so there are no inaccuracies left.
The computation of n can be simplified by expanding
(1/2) * (math.sqrt(8*x+1)-1) = math.sqrt(2 * x + 0.25) - 0.5
and utilizing that
round(y - 0.5) = int(y)
for positive y.
def is_triangular(x):
n = int(math.sqrt(2 * x))
return x == n * (n + 1) / 2
You'll want to do the latter. In Programming in Python 3 the following example is given as the most accurate way to compare
def equal_float(a, b):
#return abs(a - b) <= sys.float_info.epsilon
return abs(a - b) <= chosen_value #see edit below for more info
Also, since epsilon is the "smallest difference the machine can distinguish between two floating-point numbers", you'll want to use <= in your function.
Edit: After reading the comments below I have looked back at the book and it specifically says "Here is a simple function for comparing floats for equality to the limit of the machines accuracy". I believe this was just an example for comparing floats to extreme precision but the fact that error is introduced with many float calculations this should rarely if ever be used. I characterized it as the "most accurate" way to compare in my answer, which in some sense is true, but rarely what is intended when comparing floats or integers to floats. Choosing a value (ex: 0.00000000001) based on the "problem domain" of the function instead of using sys.float_info.epsilon is the correct approach.
Thanks to S.Lott and Sven Marnach for their corrections, and I apologize if I led anyone down the wrong path.
Python does have a Decimal class (in the decimal module), which you could use to avoid the imprecision of floats.
floats can exactly represent all integers in their range - floating-point equality is only tricky if you care about the bit after the point. So, as long as all of the calculations in your formula return whole numbers for the cases you're interested in, int(num) == num is perfectly safe.
So, we need to prove that for any triangular number, every piece of maths you do can be done with integer arithmetic (and anything coming out as a non-integer must imply that x is not triangular):
To start with, we can assume that x must be an integer - this is required in the definition of 'triangular number'.
This being the case, 8*x + 1 will also be an integer, since the integers are closed under + and * .
math.sqrt() returns float; but if x is triangular, then the square root will be a whole number - ie, again exactly represented.
So, for all x that should return true in your functions, int(num) == num will be true, and so your istriangular1 will always work. The only sticking point, as mentioned in the comments to the question, is that Python 2 by default does integer division in the same way as C - int/int => int, truncating if the result can't be represented exactly as an int. So, 1/2 == 0. This is fixed in Python 3, or by having the line
from __future__ import division
near the top of your code.
I think the module decimal is what you need
You can round your number to e.g. 14 decimal places or less:
>>> round(4.999999999999997, 14)
5.0
PS: double precision is about 15 decimal places
It is hard to argue with standards.
In C99 and POSIX, the standard for rounding a float to an int is defined by nearbyint() The important concept is the direction of rounding and the locale specific rounding convention.
Assuming the convention is common rounding, this is the same as the C99 convention in Python:
#!/usr/bin/python
import math
infinity = math.ldexp(1.0, 1023) * 2
def nearbyint(x):
"""returns the nearest int as the C99 standard would"""
# handle NaN
if x!=x:
return x
if x >= infinity:
return infinity
if x <= -infinity:
return -infinity
if x==0.0:
return x
return math.floor(x + 0.5)
If you want more control over rounding, consider using the Decimal module and choose the rounding convention you wish to employ. You may want to use Banker's Rounding for example.
Once you have decided on the convention, round to an int and compare to the other int.
Consider using NumPy, they take care of everything under the hood.
import numpy as np
result_bool = np.isclose(float1, float2)
Python has unlimited integer precision, but only 53 bits of float precision. When you square a number, you double the number of bits it requires. This means that the ULP of the original number is (approximately) twice the ULP of the square root.
You start running into issues with numbers around 50 bits or so, because the difference between the fractional representation of an irrational root and the nearest integer can be smaller than the ULP. Even in this case, checking if you are within tolerance will do more harm than good (by increasing the number of false positives).
For example:
>>> x = (1 << 26) - 1
>>> (math.sqrt(x**2)).is_integer()
True
>>> (math.sqrt(x**2 + 1)).is_integer()
False
>>> (math.sqrt(x**2 - 1)).is_integer()
False
>>> y = (1 << 27) - 1
>>> (math.sqrt(y**2)).is_integer()
True
>>> (math.sqrt(y**2 + 1)).is_integer()
True
>>> (math.sqrt(y**2 - 1)).is_integer()
True
>>> (math.sqrt(y**2 + 2)).is_integer()
False
>>> (math.sqrt(y**2 - 2)).is_integer()
True
>>> (math.sqrt(y**2 - 3)).is_integer()
False
You can therefore rework the formulation of your problem slightly. If an integer x is a triangular number, there exists an integer n such that x = n * (n + 1) // 2. The resulting quadratic is n**2 + n - 2 * x = 0. All you need to know is if the discriminant 1 + 8 * x is a perfect square. You can compute the integer square root of an integer using math.isqrt starting with python 3.8. Prior to that, you could use one of the algorithms from Wikipedia, implemented on SO here.
You can therefore stay entirely in python's infinite-precision integer domain with the following one-liner:
def is_triangular(x):
return math.isqrt(k := 8 * x + 1)**2 == k
Now you can do something like this:
>>> x = 58686775177009424410876674976531835606028390913650409380075
>>> math.isqrt(k := 8 * x + 1)**2 == k
True
>>> math.isqrt(k := 8 * (x + 1) + 1)**2 == k
False
>>> math.sqrt(k := 8 * x + 1)**2 == k
False
The first result is correct: x in this example is a triangular number computed with n = 342598234604352345342958762349.
Python still uses the same floating point representation and operations C does, so the second one is the correct way.
Under the hood, Python's float type is a C double.
The most robust way would be to get the nearest integer to num, then test if that integers satisfies the property you're after:
import math
def is_triangular1(x):
num = (1/2) * (math.sqrt(8*x+1)-1 )
inum = int(round(num))
return inum*(inum+1) == 2*x # This line uses only integer arithmetic

Categories

Resources