Python truncation

I want to remove digits from float in order to have a fixed number of digits after the dot, for example:

1.923328437452 -> 1.923 

I need to output as a string to another function, not print.

I also want to ignore the lost numbers, not the circles around them.

+96
python floating-point
Apr 23 '09 at 22:56
source share
30 answers

Firstly, a function for those who just want to get the 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 true in Python 2.7 and 3.1+. For older versions, it is impossible to get the same “smart rounding” effect (at least not without a lot of complex code), but rounding to 12 decimal places before truncation will work most 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 basic method is to convert the value to a string with complete precision, and then simply chop off everything except the desired number of characters. The last step is simple; this can be done either using string manipulation

 i, p, d = s.partition('.') '.'.join([i, (d+'0'*n)[:n]]) 

or 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 several pairs of floating point literals (i.e. what you write in the source code) that both produce the same binary representation and still have to be truncated by in different ways. 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 a sequence of bits (assuming a 64-bit float)

 0011111111010011001100110011001100110011001100110011001100110011 

This is the closest value to 0.3, which can be accurately represented as an IEEE float. But if you write 0.29999999999999998 in a Python program, the compiler translates it to exactly the same value. In one case, you meant that it was truncated (by one digit) as 0.3 , while in the other case, you meant that it was truncated as 0.2 , but Python can only give one answer. This is a fundamental limitation of Python or even any programming language without lazy evaluation. The truncation function has access only to the binary value stored in the computer's memory, and not to the line that you actually entered in the source code. one

If you decode the sequence of bits back to decimal, again using the IEEE 64-bit floating point format, you will get

 0.2999999999999999888977697537484345957637... 

so a naive implementation would come about with 0.2 , although this is probably not what you want. For more information about floating point errors, see the Python Tutorial .

It is very rare to work with a floating point value that is so close to a round number and yet intentionally not equal to that round number. Therefore, when truncating, it probably makes sense to choose the “most enjoyable” decimal representation of everything that can match the value in memory. Python 2.7 and later (but not 3.0) includes a sophisticated algorithm that does just that , which we can access through the default string formatting operation.

 '{}'.format(f) 

The only caveat is that it 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. Therefore, the method should catch this case and handle it differently. There are several cases where using the f format specification instead causes a problem, for example, trying to trim 3e-10 to 28 precision digits (it produces 0.0000000002999999999999999980 ), and I'm still not sure how to best handle those.

If you really work with float that are very close to round numbers but intentionally not equal to them (for example, 0.29999999999999998 or 99.959999999999994), this will lead to some false positives, i.e. a number that you did not want to round up will be rounded. In this case, the solution should indicate a fixed accuracy.

 '{0:.{1}f}'.format(f, sys.float_info.dig + n + 2) 

The number of precision digits used here is not significant, it should be large enough to ensure that any rounding performed in the string conversion does not "replace" the value with its good decimal representation. I think that sys.float_info.dig + n + 2 may be sufficient in all cases, but if it is not necessary that 2 could be increased, and this will not hinder doing this.

In earlier versions of Python (prior to 2.6 or 3.0), formatting floating-point numbers was much rougher and regularly produced things like

 >>> 1.1 1.1000000000000001 

If this is your situation, if you want to use “beautiful” decimal notations for truncation, all you can do (as far as I know) is to select a certain number of digits, less than the full precision represented by the float , and round the number to this number, before you crop it. A typical choice is 12,

 '%.12f' % f 

but you can customize it according to the numbers you use.




1 Well ... I lied. Technically, you can instruct Python to reanalyze your own source code and extract the part corresponding to the first argument that you pass to the truncation function. If this argument is a floating point literal, you can simply cut it off from a certain number of places after the decimal point and return it. However, this strategy does not work if the argument is a variable, which makes it useless. For entertainment value, only the following is presented:

 def trunc_introspect(f, n): '''Truncates/pads the float f to n decimal places by looking at the caller 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 when you pass a variable seems like a lost cause, since you have to trace back through the program execution until you find the floating point literal that gave the variable a value. If there is even one. Most variables will be initialized from user input or mathematical expressions, in which case the binary representation is all there is.

+104
Apr 23 '09 at 23:10
source share
 round(1.923328437452, 3) 

See the Python documentation for standard types . You need to scroll a bit to get to the round function. Essentially, the second number indicates how many decimal places to round to.

+142
Apr 23 '09 at 22:59
source share

The result of round is a floating-point number, so be careful (example from Python 2.6):

 >>> round(1.923328437452, 3) 1.923 >>> round(1.23456, 3) 1.2350000000000001 

You will be better off using a formatted string:

 >>> "%.3f" % 1.923328437452 '1.923' >>> "%.3f" % 1.23456 '1.235' 
+31
Apr 23 '09 at 23:04
source share
 n = 1.923328437452 str(n)[:4] 
+19
Nov 14 '10 at 6:58
source share

At my Python 2.7 prompt:

>>> int(1.923328437452 * 1000)/1000.0 1.923

+12
01 Feb '17 at 21:10
source share

A simple Python script -

 n = 1.923328437452 n = float(int(n * 1000)) n /=1000 
+10
Jan 10 '16 at 15:49
source share
 def trunc(num, digits): sp = str(num).split('.') return '.'.join([sp[0], sp[1][:digits]]) 

That should work. This should give you the truncation you are looking for.

+9
Apr 24 '09 at 5:46
source share

A truly pythonic way to do it

 from decimal import * with localcontext() as ctx: ctx.rounding = ROUND_DOWN print Decimal('1.923328437452').quantize(Decimal('0.001')) 

or shorter:

 from decimal import D, ROUND_DOWN D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN) 

Refresh

Typically, the problem is not trimming the floating point numbers themselves, but the incorrect use of floating point numbers before rounding.

For example: int(0.7*3*100)/100 == 2.09 .

If you are forced to use float (for example, you speed up your code with numba ), it is better to use cents as an "internal representation" of prices: ( 70*3 == 210 ) and multiply / divide inputs / outputs.

+8
May 28 '14 at 16:43
source share

Thus, many answers to this question are completely incorrect. They either round up pop-ups (rather than crop) or do not work for all cases.

This is Google’s best result when I search for “Python truncate float,” a concept that is really simple and that deserves the best answers. I agree with Hatchkins that using the decimal module is a pythonic way of doing this, so I give here a function that I think answers the question correctly and that works as expected for all cases.

As a side note, fractional values, generally speaking, cannot be represented exactly with 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() 
+8
Feb 04 '15 at 14:26
source share

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 
+4
Sep 06 '14 at 16:42
source share

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] 
+4
Mar 25 '15 at 4:47
source share

If you are interested in math, this works for + ve numbers:

 >>> v = 1.923328437452 >>> v - v % 1e-3 1.923 
+3
Mar 08 '18 at 21:54
source share
 def trunc(f,n): return ('%.16f' % f)[:(n-16)] 
+1
Jul 22 '10 at 2:12
source share

Just wanted to mention that the old "make round () with the tag floor ()"

 round(f) = floor(f+0.5) 

can rotate to make floor () from round ()

 floor(f) = round(f-0.5) 

Although both of 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)) 
+1
Jul 22 2018-10-10T00:
source share

Int (16.5); this will give an integer value of 16, i.e. trunc, will not be able to specify decimal numbers, but suppose you can do this with

 import math; def trunc(invalue, digits): return int(invalue*math.pow(10,digits))/math.pow(10,digits); 
+1
Oct. 20 '11 at 23:52
source share

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

+1
Nov 28 '14 at 13:19
source share

use numpy.round

 import numpy as np precision = 3 floats = [1.123123123, 2.321321321321] new_float = np.round(floats, precision) 
+1
Apr 11 '15 at 6:02
source share

General and simple function:

 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 
+1
Nov 07 '16 at 17:16
source share
 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

+1
Jun 24 '18 at 16:26
source share

Most of the answers are 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 

Simple scan for index '.' and trim as desired (without rounding). Convert string to floating as last 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 
+1
Apr 21 '19 at 2:12
source share
 >>> floor((1.23658945) * 10**4) / 10**4 1.2365 

# divide and multiply by 10 ** the number of desired digits

0
Mar 30 '17 at 19:13
source share

There is a simple workaround in python 3. Where to cut, I defined using the variable decPlace, to facilitate adaptation.

 f = 1.12345 decPlace= 4 f_cut = int(f * 10**decPlace) /10**decPlace 

Exit:

 f = 1.1234 

Hope it helps.

0
Jun 07 '18 at 14:14
source share

Something simple enough to fit into an understanding of the list, without libraries or other external dependencies. For Python> = 3.6, it is very simple to write using f-lines.

The idea is to allow the string conversion to round 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, rounding occurs here (namely for the fourth digit), but rounding at some point is inevitable. In case the transition between truncation and rounding is important, here is 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'] 
0
Jul 04 '18 at 11:14
source share

The basic idea presented here seems to me to be the best approach to this problem. Unfortunately, he received fewer votes, while the later answer, which has more votes, is not complete (as noted in the comments). We hope that the implementation below provides a short and complete truncation solution.

 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 the corner cases found here and here .

0
Dec 20 '18 at 9:29
source share

Short and simple option

 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 
0
Mar 20 '19 at 13:46
source share
 f = 1.923328437452 f = f - f % 0.001 
0
Jul 16 '19 at 20:05
source share

When using DF pandas, 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) 
0
Aug 07 '19 at 19:16
source share

I'm also new to python, and after using 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 a string and join with ... str (datetime.now (). microsecond) [: 3], which returns only microseconds, is converted to string and truncates to the first three characters

-one
Aug 08 '12 at 16:30
source share
 # value value to be truncated # n number of values after decimal value = 0.999782 n = 3 float(int(value*1en))*1e-n 
-one
Aug 31 '15 at 2:24
source share

If you mean when printing, then the following should work:

 print '%.3f' % number 
-3
Apr 23 '09 at 23:01
source share



All Articles