Truncate to three decimals in Python
Question:
How do I get 1324343032.324?
As you can see below, the following do not work:
>>1324343032.324325235 * 1000 / 1000
1324343032.3243253
>>int(1324343032.324325235 * 1000) / 1000.0
1324343032.3239999
>>round(int(1324343032.324325235 * 1000) / 1000.0,3)
1324343032.3239999
>>str(1324343032.3239999)
'1324343032.32'
Answers:
You can use an additional float()
around it if you want to preserve it as a float
.
val = '%.3f'%(1324343032.324325235)
How about this:
In [1]: '%.3f' % round(1324343032.324325235 * 1000 / 1000,3)
Out[1]: '1324343032.324'
Possible duplicate of round() in Python doesn't seem to be rounding properly
[EDIT]
Given the additional comments I believe you’ll want to do:
In : Decimal('%.3f' % (1324343032.324325235 * 1000 / 1000))
Out: Decimal('1324343032.324')
The floating point accuracy isn’t going to be what you want:
In : 3.324
Out: 3.3239999999999998
(all examples are with Python 2.6.5)
Almo’s link explains why this happens. To solve the problem, use the decimal library.
Use the decimal module. But if you must use floats and still somehow coerce them into a given number of decimal points converting to string an back provides a (rather clumsy, I’m afraid) method of doing it.
>>> q = 1324343032.324325235 * 1000 / 1000
>>> a = "%.3f" % q
>>> a
'1324343032.324'
>>> b = float(a)
>>> b
1324343032.324
So:
float("%3.f" % q)
You can use the following function to truncate a number to a set number of decimals:
import math
def truncate(number, digits) -> float:
# Improve accuracy with floating point operations, to avoid truncate(16.4, 2) = 16.39 or truncate(-1.13, 2) = -1.12
nbDecimals = len(str(number).split('.')[1])
if nbDecimals <= digits:
return number
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
Usage:
>>> truncate(1324343032.324325235, 3)
1324343032.324
I’ve found another solution (it must be more efficient than “string witchcraft” workarounds):
>>> import decimal
# By default rounding setting in python is decimal.ROUND_HALF_EVEN
>>> decimal.getcontext().rounding = decimal.ROUND_DOWN
>>> c = decimal.Decimal(34.1499123)
# By default it should return 34.15 due to '99' after '34.14'
>>> round(c,2)
Decimal('34.14')
>>> float(round(c,2))
34.14
>>> print(round(c,2))
34.14
>>> float(1324343032.324325235) * float(1000) / float(1000)
1324343032.3243253
>>> round(float(1324343032.324325235) * float(1000) / float(1000), 3)
1324343032.324
You can also use:
import math
nValeur = format(float(input('Quelle valeur ? ')), '.3f')
In Python 3.6 it would work.
After looking for a way to solve this problem, without loading any Python 3 module or extra mathematical operations, I solved the problem using only str.format() e .float(). I think this way is faster than using other mathematical operations, like in the most commom solution. I needed a fast solution because I work with a very very large dataset and so for its working very well here.
def truncate_number(f_number, n_decimals):
strFormNum = "{0:." + str(n_decimals+5) + "f}"
trunc_num = float(strFormNum.format(f_number)[:-5])
return(trunc_num)
# Testing the 'trunc_num()' function
test_num = 1150/252
[(idx, truncate_number(test_num, idx)) for idx in range(0, 20)]
It returns the following output:
[(0, 4.0),
(1, 4.5),
(2, 4.56),
(3, 4.563),
(4, 4.5634),
(5, 4.56349),
(6, 4.563492),
(7, 4.563492),
(8, 4.56349206),
(9, 4.563492063),
(10, 4.5634920634),
(11, 4.56349206349),
(12, 4.563492063492),
(13, 4.563492063492),
(14, 4.56349206349206),
(15, 4.563492063492063),
(16, 4.563492063492063),
(17, 4.563492063492063),
(18, 4.563492063492063),
(19, 4.563492063492063)]
I believe using the format
function is a bad idea. Please see the below. It rounds the value. I use Python 3.6.
>>> '%.3f'%(1.9999999)
'2.000'
Use a regular expression instead:
>>> re.match(r'd+.d{3}', str(1.999999)).group(0)
'1.999'
‘%.3f’%(1324343032.324325235)
It’s OK just in this particular case.
Simply change the number a little bit:
1324343032.324725235
And then:
'%.3f'%(1324343032.324725235)
gives you 1324343032.325
Try this instead:
def trun_n_d(n,d):
s=repr(n).split('.')
if (len(s)==1):
return int(s[0])
return float(s[0]+'.'+s[1][:d])
Another option for trun_n_d:
def trun_n_d(n,d):
dp = repr(n).find('.') #dot position
if dp == -1:
return int(n)
return float(repr(n)[:dp+d+1])
Yet another option ( a oneliner one) for trun_n_d [this, assumes ‘n‘ is a str and ‘d‘ is an int]:
def trun_n_d(n,d):
return ( n if not n.find('.')+1 else n[:n.find('.')+d+1] )
trun_n_d gives you the desired output in both, Python 2.7 and Python 3.6
trun_n_d(1324343032.324325235,3) returns 1324343032.324
Likewise, trun_n_d(1324343032.324725235,3) returns 1324343032.324
Note 1 In Python 3.6 (and, probably, in Python 3.x) something like this, works just fine:
def trun_n_d(n,d):
return int(n*10**d)/10**d
But, this way, the rounding ghost is always lurking around.
Note 2 In situations like this, due to python‘s number internals, like rounding and lack of precision, working with n as a str is way much better than using its int counterpart; you can always cast your number to a float at the end.
Maybe this way:
def myTrunc(theNumber, theDigits):
myDigits = 10 ** theDigits
return (int(theNumber * myDigits) / myDigits)
Okay, this is just another approach to solve this working on the number as a string and performing a simple slice of it. This gives you a truncated output of the number instead of a rounded one.
num = str(1324343032.324325235)
i = num.index(".")
truncated = num[:i + 4]
print(truncated)
Output:
'1324343032.324'
Of course then you can parse:
float(truncated)
a = 1.0123456789
dec = 3 # keep this many decimals
p = 10 # raise 10 to this power
a * 10 ** p // 10 ** (p - dec) / 10 ** dec
>>> 1.012
I think the best and proper way is to use decimal
module.
import decimal
a = 1324343032.324325235
decimal_val = decimal.Decimal(str(a)).quantize(
decimal.Decimal('.001'),
rounding=decimal.ROUND_DOWN
)
float_val = float(decimal_val)
print(decimal_val)
>>>1324343032.324
print(float_val)
>>>1324343032.324
You can use different values for rounding=decimal.ROUND_DOWN
, available options are ROUND_CEILING
, ROUND_DOWN
, ROUND_FLOOR
, ROUND_HALF_DOWN
, ROUND_HALF_EVEN
, ROUND_HALF_UP
, ROUND_UP
, and ROUND_05UP
. You can find explanation of each option here in docs.
Maybe python changed since this question, all of the below seem to work well
Python2.7
int(1324343032.324325235 * 1000) / 1000.0
float(int(1324343032.324325235 * 1000)) / 1000
round(int(1324343032.324325235 * 1000) / 1000.0,3)
# result for all of the above is 1324343032.324
I suggest next solution:
def my_floor(num, precision):
return f'{num:.{precision+1}f}'[:-1]
my_floor(1.026456,2) # 1.02
I am trying to generate a random number between 5 to 7 and want to limit it to 3 decimal places.
import random
num = float('%.3f' % random.uniform(5, 7))
print (num)
Function
def truncate(number: float, digits: int) -> float:
pow10 = 10 ** digits
return number * pow10 // 1 / pow10
Test code
f1 = 1.2666666
f2 = truncate(f1, 3)
print(f1, f2)
Output
1.2666666 1.266
Explain
It shifts f1
numbers digits
times to the left, then cuts all decimals and finally shifts back the numbers digits
times to the right.
Example in a sequence:
1.2666666 # number
1266.6666 # number * pow10
1266.0 # number * pow10 // 1
1.266 # number * pow10 // 1 / pow10
I develop a good solution, I know there is much If
statements, but It works! (Its only for <1 numbers)
def truncate(number, digits) -> float:
startCounting = False
if number < 1:
number_str = str('{:.20f}'.format(number))
resp = ''
count_digits = 0
for i in range(0, len(number_str)):
if number_str[i] != '0' and number_str[i] != '.' and number_str[i] != ',':
startCounting = True
if startCounting:
count_digits = count_digits + 1
resp = resp + number_str[i]
if count_digits == digits:
break
return resp
else:
return number
Based on @solveMe asnwer (https://stackoverflow.com/a/39165933/641263) which I think is one of the most correct ways by utilising decimal context, I created following method which does the job exactly as needed:
import decimal
def truncate_decimal(dec: Decimal, digits: int) -> decimal.Decimal:
round_down_ctx = decimal.getcontext()
round_down_ctx.rounding = decimal.ROUND_DOWN
new_dec = round_down_ctx.create_decimal(dec)
return round(new_dec, digits)
Another way in comparison with the accepted answer is to use a more modern formatted string literal (f-string):
>>> f'{1324343032.324325235:.3f}'
>>> '1324343032.324'
How do I get 1324343032.324?
As you can see below, the following do not work:
>>1324343032.324325235 * 1000 / 1000
1324343032.3243253
>>int(1324343032.324325235 * 1000) / 1000.0
1324343032.3239999
>>round(int(1324343032.324325235 * 1000) / 1000.0,3)
1324343032.3239999
>>str(1324343032.3239999)
'1324343032.32'
You can use an additional float()
around it if you want to preserve it as a float
.
val = '%.3f'%(1324343032.324325235)
How about this:
In [1]: '%.3f' % round(1324343032.324325235 * 1000 / 1000,3)
Out[1]: '1324343032.324'
Possible duplicate of round() in Python doesn't seem to be rounding properly
[EDIT]
Given the additional comments I believe you’ll want to do:
In : Decimal('%.3f' % (1324343032.324325235 * 1000 / 1000))
Out: Decimal('1324343032.324')
The floating point accuracy isn’t going to be what you want:
In : 3.324
Out: 3.3239999999999998
(all examples are with Python 2.6.5)
Almo’s link explains why this happens. To solve the problem, use the decimal library.
Use the decimal module. But if you must use floats and still somehow coerce them into a given number of decimal points converting to string an back provides a (rather clumsy, I’m afraid) method of doing it.
>>> q = 1324343032.324325235 * 1000 / 1000
>>> a = "%.3f" % q
>>> a
'1324343032.324'
>>> b = float(a)
>>> b
1324343032.324
So:
float("%3.f" % q)
You can use the following function to truncate a number to a set number of decimals:
import math
def truncate(number, digits) -> float:
# Improve accuracy with floating point operations, to avoid truncate(16.4, 2) = 16.39 or truncate(-1.13, 2) = -1.12
nbDecimals = len(str(number).split('.')[1])
if nbDecimals <= digits:
return number
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
Usage:
>>> truncate(1324343032.324325235, 3)
1324343032.324
I’ve found another solution (it must be more efficient than “string witchcraft” workarounds):
>>> import decimal
# By default rounding setting in python is decimal.ROUND_HALF_EVEN
>>> decimal.getcontext().rounding = decimal.ROUND_DOWN
>>> c = decimal.Decimal(34.1499123)
# By default it should return 34.15 due to '99' after '34.14'
>>> round(c,2)
Decimal('34.14')
>>> float(round(c,2))
34.14
>>> print(round(c,2))
34.14
>>> float(1324343032.324325235) * float(1000) / float(1000)
1324343032.3243253
>>> round(float(1324343032.324325235) * float(1000) / float(1000), 3)
1324343032.324
You can also use:
import math
nValeur = format(float(input('Quelle valeur ? ')), '.3f')
In Python 3.6 it would work.
After looking for a way to solve this problem, without loading any Python 3 module or extra mathematical operations, I solved the problem using only str.format() e .float(). I think this way is faster than using other mathematical operations, like in the most commom solution. I needed a fast solution because I work with a very very large dataset and so for its working very well here.
def truncate_number(f_number, n_decimals):
strFormNum = "{0:." + str(n_decimals+5) + "f}"
trunc_num = float(strFormNum.format(f_number)[:-5])
return(trunc_num)
# Testing the 'trunc_num()' function
test_num = 1150/252
[(idx, truncate_number(test_num, idx)) for idx in range(0, 20)]
It returns the following output:
[(0, 4.0),
(1, 4.5),
(2, 4.56),
(3, 4.563),
(4, 4.5634),
(5, 4.56349),
(6, 4.563492),
(7, 4.563492),
(8, 4.56349206),
(9, 4.563492063),
(10, 4.5634920634),
(11, 4.56349206349),
(12, 4.563492063492),
(13, 4.563492063492),
(14, 4.56349206349206),
(15, 4.563492063492063),
(16, 4.563492063492063),
(17, 4.563492063492063),
(18, 4.563492063492063),
(19, 4.563492063492063)]
I believe using the format
function is a bad idea. Please see the below. It rounds the value. I use Python 3.6.
>>> '%.3f'%(1.9999999)
'2.000'
Use a regular expression instead:
>>> re.match(r'd+.d{3}', str(1.999999)).group(0)
'1.999'
‘%.3f’%(1324343032.324325235)
It’s OK just in this particular case.
Simply change the number a little bit:
1324343032.324725235
And then:
'%.3f'%(1324343032.324725235)
gives you 1324343032.325
Try this instead:
def trun_n_d(n,d):
s=repr(n).split('.')
if (len(s)==1):
return int(s[0])
return float(s[0]+'.'+s[1][:d])
Another option for trun_n_d:
def trun_n_d(n,d):
dp = repr(n).find('.') #dot position
if dp == -1:
return int(n)
return float(repr(n)[:dp+d+1])
Yet another option ( a oneliner one) for trun_n_d [this, assumes ‘n‘ is a str and ‘d‘ is an int]:
def trun_n_d(n,d):
return ( n if not n.find('.')+1 else n[:n.find('.')+d+1] )
trun_n_d gives you the desired output in both, Python 2.7 and Python 3.6
trun_n_d(1324343032.324325235,3) returns 1324343032.324
Likewise, trun_n_d(1324343032.324725235,3) returns 1324343032.324
Note 1 In Python 3.6 (and, probably, in Python 3.x) something like this, works just fine:
def trun_n_d(n,d):
return int(n*10**d)/10**d
But, this way, the rounding ghost is always lurking around.
Note 2 In situations like this, due to python‘s number internals, like rounding and lack of precision, working with n as a str is way much better than using its int counterpart; you can always cast your number to a float at the end.
Maybe this way:
def myTrunc(theNumber, theDigits):
myDigits = 10 ** theDigits
return (int(theNumber * myDigits) / myDigits)
Okay, this is just another approach to solve this working on the number as a string and performing a simple slice of it. This gives you a truncated output of the number instead of a rounded one.
num = str(1324343032.324325235)
i = num.index(".")
truncated = num[:i + 4]
print(truncated)
Output:
'1324343032.324'
Of course then you can parse:
float(truncated)
a = 1.0123456789
dec = 3 # keep this many decimals
p = 10 # raise 10 to this power
a * 10 ** p // 10 ** (p - dec) / 10 ** dec
>>> 1.012
I think the best and proper way is to use decimal
module.
import decimal
a = 1324343032.324325235
decimal_val = decimal.Decimal(str(a)).quantize(
decimal.Decimal('.001'),
rounding=decimal.ROUND_DOWN
)
float_val = float(decimal_val)
print(decimal_val)
>>>1324343032.324
print(float_val)
>>>1324343032.324
You can use different values for rounding=decimal.ROUND_DOWN
, available options are ROUND_CEILING
, ROUND_DOWN
, ROUND_FLOOR
, ROUND_HALF_DOWN
, ROUND_HALF_EVEN
, ROUND_HALF_UP
, ROUND_UP
, and ROUND_05UP
. You can find explanation of each option here in docs.
Maybe python changed since this question, all of the below seem to work well
Python2.7
int(1324343032.324325235 * 1000) / 1000.0
float(int(1324343032.324325235 * 1000)) / 1000
round(int(1324343032.324325235 * 1000) / 1000.0,3)
# result for all of the above is 1324343032.324
I suggest next solution:
def my_floor(num, precision):
return f'{num:.{precision+1}f}'[:-1]
my_floor(1.026456,2) # 1.02
I am trying to generate a random number between 5 to 7 and want to limit it to 3 decimal places.
import random
num = float('%.3f' % random.uniform(5, 7))
print (num)
Function
def truncate(number: float, digits: int) -> float:
pow10 = 10 ** digits
return number * pow10 // 1 / pow10
Test code
f1 = 1.2666666
f2 = truncate(f1, 3)
print(f1, f2)
Output
1.2666666 1.266
Explain
It shifts f1
numbers digits
times to the left, then cuts all decimals and finally shifts back the numbers digits
times to the right.
Example in a sequence:
1.2666666 # number
1266.6666 # number * pow10
1266.0 # number * pow10 // 1
1.266 # number * pow10 // 1 / pow10
I develop a good solution, I know there is much If
statements, but It works! (Its only for <1 numbers)
def truncate(number, digits) -> float:
startCounting = False
if number < 1:
number_str = str('{:.20f}'.format(number))
resp = ''
count_digits = 0
for i in range(0, len(number_str)):
if number_str[i] != '0' and number_str[i] != '.' and number_str[i] != ',':
startCounting = True
if startCounting:
count_digits = count_digits + 1
resp = resp + number_str[i]
if count_digits == digits:
break
return resp
else:
return number
Based on @solveMe asnwer (https://stackoverflow.com/a/39165933/641263) which I think is one of the most correct ways by utilising decimal context, I created following method which does the job exactly as needed:
import decimal
def truncate_decimal(dec: Decimal, digits: int) -> decimal.Decimal:
round_down_ctx = decimal.getcontext()
round_down_ctx.rounding = decimal.ROUND_DOWN
new_dec = round_down_ctx.create_decimal(dec)
return round(new_dec, digits)
Another way in comparison with the accepted answer is to use a more modern formatted string literal (f-string):
>>> f'{1324343032.324325235:.3f}'
>>> '1324343032.324'