round() doesn't seem to be rounding properly - python

The documentation for the round() function states that you pass it a number, and the positions past the decimal to round. Thus it should do this:
n = 5.59
round(n, 1) # 5.6
But, in actuality, good old floating point weirdness creeps in and you get:
5.5999999999999996
For the purposes of UI, I need to display 5.6. I poked around the Internet and found some documentation that this is dependent on my implementation of Python. Unfortunately, this occurs on both my Windows dev machine and each Linux server I've tried. See here also.
Short of creating my own round library, is there any way around this?

I can't help the way it's stored, but at least formatting works correctly:
'%.1f' % round(n, 1) # Gives you '5.6'

Formatting works correctly even without having to round:
"%.1f" % n

If you use the Decimal module you can approximate without the use of the 'round' function. Here is what I've been using for rounding especially when writing monetary applications:
from decimal import Decimal, ROUND_UP
Decimal(str(16.2)).quantize(Decimal('.01'), rounding=ROUND_UP)
This will return a Decimal Number which is 16.20.

round(5.59, 1) is working fine. The problem is that 5.6 cannot be represented exactly in binary floating point.
>>> 5.6
5.5999999999999996
>>>
As Vinko says, you can use string formatting to do rounding for display.
Python has a module for decimal arithmetic if you need that.

You get '5.6' if you do str(round(n, 1)) instead of just round(n, 1).

You can switch the data type to an integer:
>>> n = 5.59
>>> int(n * 10) / 10.0
5.5
>>> int(n * 10 + 0.5)
56
And then display the number by inserting the locale's decimal separator.
However, Jimmy's answer is better.

Take a look at the Decimal module
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.
and
Decimal numbers can be represented
exactly. In contrast, numbers like 1.1
and 2.2 do not have an 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.
Decimal provides the kind of operations that make it easy to write apps that require floating point operations and also need to present those results in a human readable format, e.g., accounting.

Floating point math is vulnerable to slight, but annoying, precision inaccuracies. If you can work with integer or fixed point, you will be guaranteed precision.

It's a big problem indeed. Try out this code:
print "%.2f" % (round((2*4.4+3*5.6+3*4.4)/8,2),)
It displays 4.85. Then you do:
print "Media = %.1f" % (round((2*4.4+3*5.6+3*4.4)/8,1),)
and it shows 4.8. Do you calculations by hand the exact answer is 4.85, but if you try:
print "Media = %.20f" % (round((2*4.4+3*5.6+3*4.4)/8,20),)
you can see the truth: the float point is stored as the nearest finite sum of fractions whose denominators are powers of two.

printf the sucker.
print '%.1f' % 5.59 # returns 5.6

I would avoid relying on round() at all in this case. Consider
print(round(61.295, 2))
print(round(1.295, 2))
will output
61.3
1.29
which is not a desired output if you need solid rounding to the nearest integer. To bypass this behavior go with math.ceil() (or math.floor() if you want to round down):
from math import ceil
decimal_count = 2
print(ceil(61.295 * 10 ** decimal_count) / 10 ** decimal_count)
print(ceil(1.295 * 10 ** decimal_count) / 10 ** decimal_count)
outputs
61.3
1.3
Hope that helps.

You can use the string format operator %, similar to sprintf.
mystring = "%.2f" % 5.5999

I am doing:
int(round( x , 0))
In this case, we first round properly at the unit level, then we convert to integer to avoid printing a float.
so
>>> int(round(5.59,0))
6
I think this answer works better than formating the string, and it also makes more sens to me to use the round function.

Works Perfect
format(5.59, '.1f') # to display
float(format(5.59, '.1f')) #to round

Another potential option is:
def hard_round(number, decimal_places=0):
"""
Function:
- Rounds a float value to a specified number of decimal places
- Fixes issues with floating point binary approximation rounding in python
Requires:
- `number`:
- Type: int|float
- What: The number to round
Optional:
- `decimal_places`:
- Type: int
- What: The number of decimal places to round to
- Default: 0
Example:
```
hard_round(5.6,1)
```
"""
return int(number*(10**decimal_places)+0.5)/(10**decimal_places)

Code:
x1 = 5.63
x2 = 5.65
print(float('%.2f' % round(x1,1))) # gives you '5.6'
print(float('%.2f' % round(x2,1))) # gives you '5.7'
Output:
5.6
5.7

The problem is only when last digit is 5. Eg. 0.045 is internally stored as 0.044999999999999... You could simply increment last digit to 6 and round off. This will give you the desired results.
import re
def custom_round(num, precision=0):
# Get the type of given number
type_num = type(num)
# If the given type is not a valid number type, raise TypeError
if type_num not in [int, float, Decimal]:
raise TypeError("type {} doesn't define __round__ method".format(type_num.__name__))
# If passed number is int, there is no rounding off.
if type_num == int:
return num
# Convert number to string.
str_num = str(num).lower()
# We will remove negative context from the number and add it back in the end
negative_number = False
if num < 0:
negative_number = True
str_num = str_num[1:]
# If number is in format 1e-12 or 2e+13, we have to convert it to
# to a string in standard decimal notation.
if 'e-' in str_num:
# For 1.23e-7, e_power = 7
e_power = int(re.findall('e-[0-9]+', str_num)[0][2:])
# For 1.23e-7, number = 123
number = ''.join(str_num.split('e-')[0].split('.'))
zeros = ''
# Number of zeros = e_power - 1 = 6
for i in range(e_power - 1):
zeros = zeros + '0'
# Scientific notation 1.23e-7 in regular decimal = 0.000000123
str_num = '0.' + zeros + number
if 'e+' in str_num:
# For 1.23e+7, e_power = 7
e_power = int(re.findall('e\+[0-9]+', str_num)[0][2:])
# For 1.23e+7, number_characteristic = 1
# characteristic is number left of decimal point.
number_characteristic = str_num.split('e+')[0].split('.')[0]
# For 1.23e+7, number_mantissa = 23
# mantissa is number right of decimal point.
number_mantissa = str_num.split('e+')[0].split('.')[1]
# For 1.23e+7, number = 123
number = number_characteristic + number_mantissa
zeros = ''
# Eg: for this condition = 1.23e+7
if e_power >= len(number_mantissa):
# Number of zeros = e_power - mantissa length = 5
for i in range(e_power - len(number_mantissa)):
zeros = zeros + '0'
# Scientific notation 1.23e+7 in regular decimal = 12300000.0
str_num = number + zeros + '.0'
# Eg: for this condition = 1.23e+1
if e_power < len(number_mantissa):
# In this case, we only need to shift the decimal e_power digits to the right
# So we just copy the digits from mantissa to characteristic and then remove
# them from mantissa.
for i in range(e_power):
number_characteristic = number_characteristic + number_mantissa[i]
number_mantissa = number_mantissa[i:]
# Scientific notation 1.23e+1 in regular decimal = 12.3
str_num = number_characteristic + '.' + number_mantissa
# characteristic is number left of decimal point.
characteristic_part = str_num.split('.')[0]
# mantissa is number right of decimal point.
mantissa_part = str_num.split('.')[1]
# If number is supposed to be rounded to whole number,
# check first decimal digit. If more than 5, return
# characteristic + 1 else return characteristic
if precision == 0:
if mantissa_part and int(mantissa_part[0]) >= 5:
return type_num(int(characteristic_part) + 1)
return type_num(characteristic_part)
# Get the precision of the given number.
num_precision = len(mantissa_part)
# Rounding off is done only if number precision is
# greater than requested precision
if num_precision <= precision:
return num
# Replace the last '5' with 6 so that rounding off returns desired results
if str_num[-1] == '5':
str_num = re.sub('5$', '6', str_num)
result = round(type_num(str_num), precision)
# If the number was negative, add negative context back
if negative_number:
result = result * -1
return result

Here's where I see round failing. What if you wanted to round these 2 numbers to one decimal place?
23.45
23.55
My education was that from rounding these you should get:
23.4
23.6
the "rule" being that you should round up if the preceding number was odd, not round up if the preceding number were even.
The round function in python simply truncates the 5.

Here is an easy way to round a float number to any number of decimal places, and it still works in 2021!
float_number = 12.234325335563
rounded = round(float_number, 3) # 3 is the number of decimal places to be returned.You can pass any number in place of 3 depending on how many decimal places you want to return.
print(rounded)
And this will print;
12.234

What about:
round(n,1)+epsilon

Related

why does my program work with numbers greater than one

I'm doing my own thing just for fun, and I decided to try to make a program find the fraction of a reoccurring decimal. I have tried this program quite a bit and got it to always work until we reach numbers that are greater than 1
whole = number // 1
number -= whole
repeating = ''
final_repeating = ''
copy_of_number = str(number)
stop = False
number = str(number)[2:]
for digit in number:
repeating += digit
number = number[1:]
for checker in range(len(repeating)):
try:
if repeating[checker] == number[0]:
final_repeating = repeating[checker:]
times_it_appears = number.count(final_repeating)
length = len(number)
length_of_final = len(final_repeating)
if times_it_appears == length // length_of_final and (length % length_of_final == 0 or number[(length % length_of_final * -1):] == final_repeating[:(length % length_of_final)]):
stop = True
break
else:
final_repeating = ''
except IndexError:
print('this isn\'t a reoccuring decimal')
quit()
if stop == True:
break
If number = 1.166666 or any other amount of 6's that is >= 2, then it final_repeating should equal '6' and the code should move on
The actual output is that it isn't a reoccurring decimal, and if I check number. after the program finishes, there are a lot of 0's and some random single number at the end a user doesn't type in
When you subtract whole from number, Python approximates the result. You can round the number when subtracting whole from number to avoid this.
Replace the first two lines of your code with this:
copy_of_number=str(number)
whole = int(number // 1)
copy_of_whole=str(whole)
number -= whole
number = round(number, len(copy_of_number)-len(copy_of_whole)-1)
I have made a program that converts "any" given rational number into a fraction using a function that receives a string representing a rational number such as "2.5(37)", "-7.8" or "4" and returns a string with the correspondent fraction.
Eg.: from_string_to_fraction("-2.5(37)") returns "-1256/495". It needs the functions gcd() and number_of_decimal_places_of().
This only works with rational numbers because rational numbers are all the numbers and the only numbers that can be written as the divison of two integers, that's the definition of ratinal numbers. Note that recurring decimals (dízimas infinitas periódicas) are rational numbers, and that 0,(9)=1.
"""
Author: #t3m2.
Date of creation: 09/07/2019, , dd/mm/yyyy, (july).
Version: 14/12/2019, dd/mm/yyyy, (december).
Language: Python.
This program defines three functions which can be used to
converts rational numbers into fractions:
from_string_to_fraction(),
gcd() and number_of_decimal_places_of().
I have built the function from_string_to_fraction()
that receives a string representing a rational number
in decimal notation (including repeating decimals),
such as "2.5(37)", "-7.8" or "4"
and returns a string with the correspondent fraction
in form "n/m" where n and m are integers.
Eg.: from_string_to_fraction("-2.5(37)") returns "-1256/495".
If the number is a repeating decimal,
its peridod should appear between round brackets.
It needs the functions gcd() and number_of_decimal_places_of()!
Read the functions' docstrings for more information.
"""
__author__ = "t3m2"
__date__ = "09/07/2019, , dd/mm/yyyy, (july)"
__version__ = "14/12/2019, dd/mm/yyyy, (december)"
def gcd(a=1, b=1):
"""Returns the greatest common divisor
between two positive integers.
(Recursive solution)
Make sure a and b are positive int's
because this function assumes that."""
if b == 0:
return a
else:
return gcd(b, a%b)
def number_of_decimal_places_of(x=0):
"""Returns the number of decimal places of a float or int.
Make sure x is a float or int
because this function assumes that."""
if x == int(x):
return 0
return len(str(x)[str(x).index('.')+1:])
def from_string_to_fraction(x='0'):
"""Receives a string representing a rational number in decimal notation,
(including repeating decimals) such as "-2.5(37)", "-7.8" or "4", and
returns a string with the correspondent fraction in form "n/m",
where both n and m are integers.
Eg.: from_string_to_fraction("-2.5(37)") returns "-1256/495".
It needs the functions gcd() and number_of_decimal_places_of()!
This only works with rational numbers because rational numbers
are all the numbers and the only numbers that can be written as
the divison of two integers, that's the definition of rational numbers.
Note that recurring decimals are rational numbers, and that 0,(9)=1.
Make sure that: (input restrictions)
- the argument is a valid string representing
a rational number in decimal notation;
- the decimal separator (if there) is a '.' or a ',';
- if the input is a recurring decimal, the period comes between round brackets.
(12.431111111111111... is represented as "12.43(1)".)
Because this function assumes that!"""
# The input string can have a ',' or a '.' separating the int and the decimal part:
x = x.replace(',', '.', 1)
sign = 1
if x[0] == '-':
# iff x represent a negative number, this turns x into
# a string representing positive number,
# because if it easier to work with positives numbers.
# And, in the end, we turn the result into negative again
# by making something like: "final_result*=sign".
sign = -1
x = x[1:]
### Getting the finit part (f) and the period (p): ###
# I will explain this with an example:
# If x == "2.5(37)"; then I set f, the finit part, to 2.5 and p, the period, to 37.
# If the number is non-recurring, f = x, since it has no period.
# Eg: if x == "-3.4"; then f = -3.4 and p = 0.
# Note that x, our argument, is still a 'string'.
try: # This will be executed iff x is a non-recurring decimal:
f = eval(x) # eval(x) "turns" the string x into a float or int.
p = 0
except TypeError: # This will be executed iff x is a recurring decimal:
f = float(x[:x.index('(')]) # The finit part of the dizim is all the way until '('.
p = int(x[x.index('(')+1:x.index(')')]) # The period of the dizim is
# the part of the number between '(' and ')').
### Getting the numerator and denominator: ###
# With f and p defined, I have to discover the numerator and the denominator:
# Here is a method that can be used in order to discover the fraction:
# If y=2,5(37): (mathematical notation)
# 1000y - 10y = 2537,(37) - 25,(37) <=>
# 1000y - 10y = 2537 - 25 <=>
# (1000-10) * y = 2537 - 25 <=>
# <=> y = (2537-25) / (1000-10) <=>
# <=> y = 2512 / 990 =>
# => y = numerator / denominator => # Then we need to simplify the fraction,
# => y = 1256 / 495 # and this will be the final result.
# Note that both numerator and denominator are integers.
# I implemented this with f and p:
numerator = f*10**(number_of_decimal_places_of(f)+len(str(p)))+p \
- f*10**number_of_decimal_places_of(f)
denominator = 10**(number_of_decimal_places_of(f)+len(str(p))) \
- 10**number_of_decimal_places_of(f)
### Simplifying the fraction: ###
# Here I am slimplifying the fraction, if it's possible:
factor = gcd(numerator, denominator)
# "sign*" is used to get the correct sign of the final answer,
# ie, the same sign of x.
numerator = sign*(numerator/factor)
denominator = denominator/factor
return "%d/%d" % (numerator, denominator)
### TESTING ###
print("This program turns \"any\" rational number in decimal notation \
into a fraction, for example: -2.5(37) = -2.537373737373737... = -1256/495\n\n")
while 1:
try:
x = input("Enter a rational number in decimal notation (exit: 'b'): ")
if x == 'b':
break
print("%s = %s" % (x, from_string_to_fraction(x)))
except:
print("Error: probably, invalid input.")
print()
If it doesn't help, I am sorry, but it will probably help others in the future.
This answer took me lots of time, I hope that someone in the future will use this to convert rational numbers in tithe notation to fractions.
The program seems to be a little bit big but this is due to the comments.
Repository of this program in GitHub.
Working on Repl.it.

Exact Value after Floating point not rounding up [duplicate]

I want to remove digits from a float to have a fixed number of digits after the dot, like:
1.923328437452 → 1.923
I need to output as a string to another function, not print.
Also I want to ignore the lost digits, not round them.
round(1.923328437452, 3)
See Python's documentation on the standard types. You'll need to scroll down a bit to get to the round function. Essentially the second number says how many decimal places to round it to.
First, the function, for those who just want some copy-and-paste code:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '{}'.format(f)
if 'e' in s or 'E' in s:
return '{0:.{1}f}'.format(f, n)
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
This is valid in Python 2.7 and 3.1+. For older versions, it's not possible to get the same "intelligent rounding" effect (at least, not without a lot of complicated code), but rounding to 12 decimal places before truncation will work much of the time:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '%.12f' % f
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
Explanation
The core of the underlying method is to convert the value to a string at full precision and then just chop off everything beyond the desired number of characters. The latter step is easy; it can be done either with string manipulation
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
or the decimal module
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
The first step, converting to a string, is quite difficult because there are some pairs of floating point literals (i.e. what you write in the source code) which both produce the same binary representation and yet should be truncated differently. For example, consider 0.3 and 0.29999999999999998. If you write 0.3 in a Python program, the compiler encodes it using the IEEE floating-point format into the sequence of bits (assuming a 64-bit float)
0011111111010011001100110011001100110011001100110011001100110011
This is the closest value to 0.3 that can accurately be represented as an IEEE float. But if you write 0.29999999999999998 in a Python program, the compiler translates it into exactly the same value. In one case, you meant it to be truncated (to one digit) as 0.3, whereas in the other case you meant it to be truncated as 0.2, but Python can only give one answer. This is a fundamental limitation of Python, or indeed any programming language without lazy evaluation. The truncation function only has access to the binary value stored in the computer's memory, not the string you actually typed into the source code.1
If you decode the sequence of bits back into a decimal number, again using the IEEE 64-bit floating-point format, you get
0.2999999999999999888977697537484345957637...
so a naive implementation would come up with 0.2 even though that's probably not what you want. For more on floating-point representation error, see the Python tutorial.
It's very rare to be working with a floating-point value that is so close to a round number and yet is intentionally not equal to that round number. So when truncating, it probably makes sense to choose the "nicest" decimal representation out of all that could correspond to the value in memory. Python 2.7 and up (but not 3.0) includes a sophisticated algorithm to do just that, which we can access through the default string formatting operation.
'{}'.format(f)
The only caveat is that this acts like a g format specification, in the sense that it uses exponential notation (1.23e+4) if the number is large or small enough. So the method has to catch this case and handle it differently. There are a few cases where using an f format specification instead causes a problem, such as trying to truncate 3e-10 to 28 digits of precision (it produces 0.0000000002999999999999999980), and I'm not yet sure how best to handle those.
If you actually are working with floats that are very close to round numbers but intentionally not equal to them (like 0.29999999999999998 or 99.959999999999994), this will produce some false positives, i.e. it'll round numbers that you didn't want rounded. In that case the solution is to specify a fixed precision.
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
The number of digits of precision to use here doesn't really matter, it only needs to be large enough to ensure that any rounding performed in the string conversion doesn't "bump up" the value to its nice decimal representation. I think sys.float_info.dig + n + 2 may be enough in all cases, but if not that 2 might have to be increased, and it doesn't hurt to do so.
In earlier versions of Python (up to 2.6, or 3.0), the floating point number formatting was a lot more crude, and would regularly produce things like
>>> 1.1
1.1000000000000001
If this is your situation, if you do want to use "nice" decimal representations for truncation, all you can do (as far as I know) is pick some number of digits, less than the full precision representable by a float, and round the number to that many digits before truncating it. A typical choice is 12,
'%.12f' % f
but you can adjust this to suit the numbers you're using.
1Well... I lied. Technically, you can instruct Python to re-parse its own source code and extract the part corresponding to the first argument you pass to the truncation function. If that argument is a floating-point literal, you can just cut it off a certain number of places after the decimal point and return that. However this strategy doesn't work if the argument is a variable, which makes it fairly useless. The following is presented for entertainment value only:
def trunc_introspect(f, n):
'''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
current_frame = None
caller_frame = None
s = inspect.stack()
try:
current_frame = s[0]
caller_frame = s[1]
gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
for token_type, token_string, _, _, _ in gen:
if token_type == tokenize.NAME and token_string == current_frame[3]:
next(gen) # left parenthesis
token_type, token_string, _, _, _ = next(gen) # float literal
if token_type == tokenize.NUMBER:
try:
cut_point = token_string.index('.') + n + 1
except ValueError: # no decimal in string
return token_string + '.' + '0' * n
else:
if len(token_string) < cut_point:
token_string += '0' * (cut_point - len(token_string))
return token_string[:cut_point]
else:
raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
break
finally:
del s, current_frame, caller_frame
Generalizing this to handle the case where you pass in a variable seems like a lost cause, since you'd have to trace backwards through the program's execution until you find the floating-point literal which gave the variable its value. If there even is one. Most variables will be initialized from user input or mathematical expressions, in which case the binary representation is all there is.
The result of round is a float, so watch out (example is from Python 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
You will be better off when using a formatted string:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
n = 1.923328437452
str(n)[:4]
At my Python 2.7 prompt:
>>> int(1.923328437452 * 1000)/1000.0
1.923
The truely pythonic way of doing it is
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
or shorter:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
Update
Usually the problem is not in truncating floats itself, but in the improper usage of float numbers before rounding.
For example: int(0.7*3*100)/100 == 2.09.
If you are forced to use floats (say, you're accelerating your code with numba), it's better to use cents as "internal representation" of prices: (70*3 == 210) and multiply/divide the inputs/outputs.
Simple python script -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
def trunc(num, digits):
sp = str(num).split('.')
return '.'.join([sp[0], sp[1][:digits]])
This should work. It should give you the truncation you are looking for.
So many of the answers given for this question are just completely wrong. They either round up floats (rather than truncate) or do not work for all cases.
This is the top Google result when I search for 'Python truncate float', a concept which is really straightforward, and which deserves better answers. I agree with Hatchkins that using the decimal module is the pythonic way of doing this, so I give here a function which I think answers the question correctly, and which works as expected for all cases.
As a side-note, fractional values, in general, cannot be represented exactly by binary floating point variables (see here for a discussion of this), which is why my function returns a string.
from decimal import Decimal, localcontext, ROUND_DOWN
def truncate(number, places):
if not isinstance(places, int):
raise ValueError("Decimal places must be an integer.")
if places < 1:
raise ValueError("Decimal places must be at least 1.")
# If you want to truncate to 0 decimal places, just do int(number).
with localcontext() as context:
context.rounding = ROUND_DOWN
exponent = Decimal(str(10 ** - places))
return Decimal(str(number)).quantize(exponent).to_eng_string()
>>> from math import floor
>>> floor((1.23658945) * 10**4) / 10**4
1.2365
# divide and multiply by 10**number of desired digits
If you fancy some mathemagic, this works for +ve numbers:
>>> v = 1.923328437452
>>> v - v % 1e-3
1.923
I did something like this:
from math import trunc
def truncate(number, decimals=0):
if decimals < 0:
raise ValueError('truncate received an invalid value of decimals ({})'.format(decimals))
elif decimals == 0:
return trunc(number)
else:
factor = float(10**decimals)
return trunc(number*factor)/factor
You can do:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
testing:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
Just wanted to mention that the old "make round() with floor()" trick of
round(f) = floor(f+0.5)
can be turned around to make floor() from round()
floor(f) = round(f-0.5)
Although both these rules break around negative numbers, so using it is less than ideal:
def trunc(f, n):
if f > 0:
return "%.*f" % (n, (f - 0.5*10**-n))
elif f == 0:
return "%.*f" % (n, f)
elif f < 0:
return "%.*f" % (n, (f + 0.5*10**-n))
def precision(value, precision):
"""
param: value: takes a float
param: precision: int, number of decimal places
returns a float
"""
x = 10.0**precision
num = int(value * x)/ x
return num
precision(1.923328437452, 3)
1.923
Short and easy variant
def truncate_float(value, digits_after_point=2):
pow_10 = 10 ** digits_after_point
return (float(int(value * pow_10))) / pow_10
>>> truncate_float(1.14333, 2)
>>> 1.14
>>> truncate_float(1.14777, 2)
>>> 1.14
>>> truncate_float(1.14777, 4)
>>> 1.1477
When using a pandas df this worked for me
import math
def truncate(number, digits) -> float:
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
df['trunc'] = df['float_val'].apply(lambda x: truncate(x,1))
df['trunc']=df['trunc'].map('{:.1f}'.format)
int(16.5);
this will give an integer value of 16, i.e. trunc, won't be able to specify decimals, but guess you can do that by
import math;
def trunc(invalue, digits):
return int(invalue*math.pow(10,digits))/math.pow(10,digits);
Here is an easy way:
def truncate(num, res=3):
return (floor(num*pow(10, res)+0.5))/pow(10, res)
for num = 1.923328437452, this outputs 1.923
def trunc(f,n):
return ('%.16f' % f)[:(n-16)]
A general and simple function to use:
def truncate_float(number, length):
"""Truncate float numbers, up to the number specified
in length that must be an integer"""
number = number * pow(10, length)
number = int(number)
number = float(number)
number /= pow(10, length)
return number
There is an easy workaround in python 3. Where to cut I defined with an help variable decPlace to make it easy to adapt.
f = 1.12345
decPlace= 4
f_cut = int(f * 10**decPlace) /10**decPlace
Output:
f = 1.1234
Hope it helps.
Most answers are way too complicated in my opinion, how about this?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Simply scanning for the index of '.' and truncate as desired (no rounding).
Convert string to float as final step.
Or in your case if you get a float as input and want a string as output:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
I think a better version would be just to find the index of decimal point . and then to take the string slice accordingly:
def truncate(number, n_digits:int=1)->float:
'''
:param number: real number ℝ
:param n_digits: Maximum number of digits after the decimal point after truncation
:return: truncated floating point number with at least one digit after decimal point
'''
decimalIndex = str(number).find('.')
if decimalIndex == -1:
return float(number)
else:
return float(str(number)[:decimalIndex+n_digits+1])
int(1.923328437452 * 1000) / 1000
>>> 1.923
int(1.9239 * 1000) / 1000
>>> 1.923
By multiplying the number by 1000 (10 ^ 3 for 3 digits) we shift the decimal point 3 places to the right and get 1923.3284374520001. When we convert that to an int the fractional part 3284374520001 will be discarded. Then we undo the shifting of the decimal point again by dividing by 1000 which returns 1.923.
use numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Something simple enough to fit in a list-comprehension, with no libraries or other external dependencies. For Python >=3.6, it's very simple to write with f-strings.
The idea is to let the string-conversion do the rounding to one more place than you need and then chop off the last digit.
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1] for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.800', '0.888', '1.125', '1.250', '1.500']
Of course, there is rounding happening here (namely for the fourth digit), but rounding at some point is unvoidable. In case the transition between truncation and rounding is relevant, here's a slightly better example:
>>> nacc = 6 # desired accuracy (maximum 15!)
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nacc}f}'[:-(nacc-nout)] for x in [2.9999, 2.99999, 2.999999, 2.9999999]]
>>> ['2.999', '2.999', '2.999', '3.000']
Bonus: removing zeros on the right
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1].rstrip('0') for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.8', '0.888', '1.125', '1.25', '1.5']
The core idea given here seems to me to be the best approach for this problem.
Unfortunately, it has received less votes while the later answer that has more votes is not complete (as observed in the comments). Hopefully, the implementation below provides a short and complete solution for truncation.
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return l[0] + '.' + l[1][:digits]
which should take care of all corner cases found here and here.
Am also a python newbie and after making use of some bits and pieces here, I offer my two cents
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str(int(time.time())) will take the time epoch as int and convert it to string and join with...
str(datetime.now().microsecond)[:3] which returns the microseconds only, convert to string and truncate to first 3 chars
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n

How to format a float point number in python?

I have a number 15.579. I want to format it to write 0.15790000E+02.
I can display it as 1.57900000E+01 but I want a 0 before the decimal point.
How can I do it in Python?
This should work. This checks for negative numbers and for any weird result. I also used Python's default precision, and made that as well as the decimal point character changeable. You can easily change the default precision if you want 8 digits, as in your example, but Python's default is 6.
def alt_sci_notation(x, prec=6, decpt='.'):
"""Return a string of a floating point number formatted in
alternate scientific notation. The significand (mantissa) is to be
between 0.1 and 1, not including 1--i.e. the decimal point is
before the first digit. The number of digits after the decimal
point, which is also the number of significant digits, is 6 by
default and must be a positive integer. The decimal point character
can also be changed.
"""
# Get regular scientific notation with the new exponent
s = '{0:.{p}E}'.format(10 * x, p=prec-1)
# Handle negative values
prefix = ''
if s[0] == '-':
prefix = s[0]
s = s[1:]
# Return the string after moving the decimal point
if prec > 1: # if a decimal point exists in thesignificand
return prefix + '0' + decpt + s[0] + s[2:]
else: # no decimal point, just one digit in the significand
return prefix + '0' + decpt + s
Here are sample results in iPython.
alt_sci_notation(15.579, 8)
Out[2]: '0.15579000E+02'
alt_sci_notation(-15.579, 8)
Out[3]: '-0.15579000E+02'
alt_sci_notation(0)
Out[4]: '0.000000E+00'
alt_sci_notation(100000000)
Out[5]: '0.100000E+09'
alt_sci_notation(.00000001)
Out[6]: '0.100000E-07'

How to get the exact number of positions after a decimal for the round() function

Lets say that I have
float_number = 3.05321355"
I want to limit my result to just 5 characters (after I convert it to a string). So the result would be 5 characters counted from the left (including the decimal point), and that would be the position after the decimal that I want to add to the round() function.
5 character version of float_number:
float_number_5_char = 3.053
it is 3 decimal positions after the decimal point so that is where I want to round up to.
round(float_number, 3)
Is there an algorithm or way I can find out the number of decimal positions of any floating point for 5 characters max?
Another example:
float_1 = 32.53235
float_1_5_char = 32.53
So this is 2 decimal positions after the decimal point.
float_2 = 0.53235
float_2_5_char = 0.532
So this is 3 decimal positions after the point.
I tried to come up with something like this:
round_place = len(str(value)) - len(str(value)[:5])
But that doesn't work for all floating numbers.
Edit: The function's preconditions are that the value is from 0-999.99
For positive numbers, you can get the number of digits to the left of the decimal with a simple formula:
left = int(math.log10(x))+1 if x >= 10 else 1
Now you can round:
r = round(x, n - 1 - left)
Floating point numbers never have an exact number of digits. If you need output precision just use string formating for proper character count:
five_chars = lambda num: ("%.3f" if num<1 else "%.5g") % num
float1 = five_chars(32.53235)
float2 = five_chars(0.53235)
gives:
32.53
0.532

Convert float to rounded decimal equivalent

When you convert a float to Decimal, the Decimal will contain as accurate a representation of the binary number that it can. It's nice to be accurate, but it isn't always what you want. Since many decimal numbers can't be represented exactly in binary, the resulting Decimal will be a little off - sometimes a little high, sometimes a little low.
>>> from decimal import Decimal
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print Decimal(f)
0.1000000000000000055511151231257827021181583404541015625
0.299999999999999988897769753748434595763683319091796875
10000000000000000905969664
9999999999999999583119736832
1.000000000000099920072216264088638126850128173828125
Ideally we'd like the Decimal to be rounded to the most likely decimal equivalent.
I tried converting to str since a Decimal created from a string will be exact. Unfortunately str rounds a little too much.
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print Decimal(str(f))
0.1
0.3
1E+25
1E+28
1.0
Is there a way of getting a nicely rounded Decimal from a float?
It turns out that repr does a better job of converting a float to a string than str does. It's the quick-and-easy way to do the conversion.
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print Decimal(repr(f))
0.1
0.3
1E+25
1E+28
1.0000000000001
Before I discovered that, I came up with a brute-force way of doing the rounding. It has the advantage of recognizing that large numbers are accurate to 15 digits - the repr method above only recognizes one significant digit for the 1e25 and 1e28 examples.
from decimal import Decimal,DecimalTuple
def _increment(digits, exponent):
new_digits = [0] + list(digits)
new_digits[-1] += 1
for i in range(len(new_digits)-1, 0, -1):
if new_digits[i] > 9:
new_digits[i] -= 10
new_digits[i-1] += 1
if new_digits[0]:
return tuple(new_digits[:-1]), exponent + 1
return tuple(new_digits[1:]), exponent
def nearest_decimal(f):
sign, digits, exponent = Decimal(f).as_tuple()
if len(digits) > 15:
round_up = digits[15] >= 5
exponent += len(digits) - 15
digits = digits[:15]
if round_up:
digits, exponent = _increment(digits, exponent)
while digits and digits[-1] == 0 and exponent < 0:
digits = digits[:-1]
exponent += 1
return Decimal(DecimalTuple(sign, digits, exponent))
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print nearest_decimal(f)
0.1
0.3
1.00000000000000E+25
1.00000000000000E+28
1.0000000000001
Edit: I discovered one more reason to use the brute-force rounding. repr tries to return a string that uniquely identifies the underlying float bit representation, but it doesn't necessarily ensure the accuracy of the last digit. By using one less digit, my rounding function will more often be the number you would expect.
>>> print Decimal(repr(2.0/3.0))
0.6666666666666666
>>> print dec.nearest_decimal(2.0/3.0)
0.666666666666667
The decimal created with repr is actually more accurate, but it implies a level of precision that doesn't exist. The nearest_decimal function delivers a better match between precision and accuracy.
I have implemented this in Pharo Smalltalk, in a Float method named asMinimalDecimalFraction.
It is exactly the same problem as printing the shortest decimal fraction that would be re-interpreted as the same float/double, assuming correct rounding (to nearest).
See my answer at Count number of digits after `.` in floating point numbers? for more references

Categories