How to truncate float values?
Asked Answered
F

32

158

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.

Farci answered 23/4, 2009 at 22:56 Comment(3)
Should -1.233 be truncated to -1.23 or -1.24?Stripteaser
@Antony "I want to ignore the lost digits, not round them."Fortunna
@Fortunna So what? He might just as well not considered negative numbers by that moment.Stripteaser
F
161

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.

Foskett answered 23/4, 2009 at 23:10 Comment(11)
How can we apply this function to a dataframe?Nonet
@RohithRNair Off the top of my head, the same way you'd apply any other function that operates on individual elements (i.e. applymap()). Maybe there's a way to make the whole operation more efficient, but that would be a matter for a separate question.Foskett
applymap() is taking a lot time since my dataframes are really big. I am trying to compare two dataframes for differences but the floating point precision is skewing my output from the desired. Like you said, I will raise a separate question for the same. Thanks.Nonet
@RohithRNair Ah, well if you're trying to compare two dataframes for differences, ask about that instead. Truncating the values (which is what this question is about) is not the best way to do it.Foskett
Just a note, your code seems to chop negative numbers to negative zero, which can get confusing...Slavocracy
@Mehrdad I just tested on Python 2.7 and 3.6 and I'm not seeing that. The function works properly for all the negative numbers I tried.Foskett
@DavidZ: What do you get for truncate(-0.001, 1)?Slavocracy
@Mehrdad I get '-0.0', which I believe is the correct result, isn't it?Foskett
@DavidZ: But that's exactly what I was saying... I was saying it gets chopped to negative zero instead of plain zero, which is not what people would normally want I think. It certainly wasn't helpful in my case when I tried to use your code.Slavocracy
@Mehrdad Oh, I misunderstood, I thought you were saying that all negative numbers regardless of their magnitude get truncated to (some variant of) zero. Anyway, I can see why a person might want to omit the negative sign from the result in some cases, but because floating-point zeros are generally signed, I think a sensible default is to leave the sign there. For comparison, '{}'.format(-0.0) produces '-0.0'; I'm basically maintaining that behavior. If you want a variant of this that strips the sign when the result is zero, it shouldn't be hard to develop one.Foskett
@DavidZ: Yeah I don't have a problem in my case, just thought I'd leave you a tip for the future poor soul who realizes this "copy-and-paste" code still requires tweaking :PSlavocracy
M
164
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.

Midships answered 23/4, 2009 at 22:59 Comment(8)
I meant rounding isn't what i need. I need truncating, which is different.Farci
Ahhh, fair enough. My mistake sorry.Midships
That's a lot of upvotes for an incorrect solution! One of those strange Stackoverflow rarities. I wonder if there's a badge for it...Architrave
It is just appalling how many wrong answers (and upvotes for the wrong answers) there are for this question.Unlive
For those looking for a correct, short and contemporary (2018) answer: https://mcmap.net/q/150605/-how-to-truncate-float-valuesConvocation
A lot of people will come to this page looking for rounding ;)Ornithomancy
@Ornithomancy and also a lot I people may not particular care whether its rounded of truncated. For example when annotating the points of a graph, it's usually the differences between the points that matters more than the exact decimal values themselves.Karankaras
but str(round(1.923328437452, 3)) does work nicely, so I liked this answer. It allows you to "output a string to another function" as the user asked.Browbeat
F
161

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.

Foskett answered 23/4, 2009 at 23:10 Comment(11)
How can we apply this function to a dataframe?Nonet
@RohithRNair Off the top of my head, the same way you'd apply any other function that operates on individual elements (i.e. applymap()). Maybe there's a way to make the whole operation more efficient, but that would be a matter for a separate question.Foskett
applymap() is taking a lot time since my dataframes are really big. I am trying to compare two dataframes for differences but the floating point precision is skewing my output from the desired. Like you said, I will raise a separate question for the same. Thanks.Nonet
@RohithRNair Ah, well if you're trying to compare two dataframes for differences, ask about that instead. Truncating the values (which is what this question is about) is not the best way to do it.Foskett
Just a note, your code seems to chop negative numbers to negative zero, which can get confusing...Slavocracy
@Mehrdad I just tested on Python 2.7 and 3.6 and I'm not seeing that. The function works properly for all the negative numbers I tried.Foskett
@DavidZ: What do you get for truncate(-0.001, 1)?Slavocracy
@Mehrdad I get '-0.0', which I believe is the correct result, isn't it?Foskett
@DavidZ: But that's exactly what I was saying... I was saying it gets chopped to negative zero instead of plain zero, which is not what people would normally want I think. It certainly wasn't helpful in my case when I tried to use your code.Slavocracy
@Mehrdad Oh, I misunderstood, I thought you were saying that all negative numbers regardless of their magnitude get truncated to (some variant of) zero. Anyway, I can see why a person might want to omit the negative sign from the result in some cases, but because floating-point zeros are generally signed, I think a sensible default is to leave the sign there. For comparison, '{}'.format(-0.0) produces '-0.0'; I'm basically maintaining that behavior. If you want a variant of this that strips the sign when the result is zero, it shouldn't be hard to develop one.Foskett
@DavidZ: Yeah I don't have a problem in my case, just thought I'd leave you a tip for the future poor soul who realizes this "copy-and-paste" code still requires tweaking :PSlavocracy
O
33

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'
Olecranon answered 23/4, 2009 at 23:4 Comment(6)
On my Python, that rounds: '%.3f' % 1.23456 == '1.235'Foskett
This is way more elegant than the manual string formatting nonsense, good post!Eisenstein
round(1.23456, 3) is 1.235 and not 1.2350000000000001Penn
@Penn not necessarily. The example here is from Python 2.6 (note the date of the answer). The string formatting was improved in Python 2.7/3.1, that's probably why you get different results. Nevertheless, floating point numbers will often have unexpected string representations, see: docs.python.org/3.6/tutorial/floatingpoint.htmlOlecranon
This is not correct as the question is about truncating and not rounding.Dolf
Given the specification of the question, the formatting string is actually not bad but needs to be refined: def trunc_s(x, d): return f"{x:.{d + 1}f}"[:-1]Giralda
S
29
n = 1.923328437452
str(n)[:4]
Snappy answered 14/11, 2010 at 6:58 Comment(6)
Simple and pythonic. 4 is the size of the whole number though, not only the digits after the dot.Hokeypokey
So if the user enters for example 2, you'll have a decimal dot . at the end of the string - not really a good solution I think.Closeup
This is specific to a case to this number. How would it generalise to 11.923328437452?Marginal
Best answer! you also could add float() to return a number: float(str(n)[:4])Subgroup
Genius! been searching for half and hour for a smiple solution for numbers but seems the right one is in the strings :))Cabe
Old answer but for future readers, this obviously doesn't work with floats which have more than 1 digit before the decimal pointCylinder
M
24

At my Python 2.7 prompt:

>>> int(1.923328437452 * 1000)/1000.0
1.923
Mcnew answered 1/2, 2017 at 21:10 Comment(1)
I tried the answers above this one but they didn't work and this one did--thank you!Inexplicable
S
15

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.

Stripteaser answered 28/5, 2014 at 16:43 Comment(4)
Parson me for asking this, but... why?Loritalorn
@markroxor, not sure what exactly you're asking about. As a sidenote, usually the problem is not with the rounding itself, but with the improper usage of float numbers before rounding. Eg int(0.7*3*100)/100 == 2.09. Where did my 1 cent go?Stripteaser
that makes sense, can you edit your answer with this explanation? thanks.Loritalorn
Getting ImportError: cannot import name 'D', I believe you wanted to make a named import no ?Donahue
L
14

Simple python script -

n = 1.923328437452
n = float(int(n * 1000))
n /=1000
Loritalorn answered 10/1, 2016 at 15:49 Comment(0)
T
13
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.

Tormentil answered 24/4, 2009 at 5:46 Comment(1)
If the num provided is an integer this throws an IndexError due to the missing . in the number. Using partition you could get around this special case. a, dot, b = str(num).partition('.') return a + dot + b[:digits]Maim
U
10

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()
Unlive answered 4/2, 2015 at 14:26 Comment(1)
Thank you! I didn't think a correct and complete answer would be so hard to find for such a common need... Never seen so many up votes for incorrect answers.Cylinder
T
6
>>> from math import floor
>>> floor((1.23658945) * 10**4) / 10**4
1.2365

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

Tilden answered 30/3, 2017 at 19:13 Comment(1)
from math import floorCabe
M
5

If you fancy some mathemagic, this works for +ve numbers:

>>> v = 1.923328437452
>>> v - v % 1e-3
1.923
Misdemeanor answered 8/3, 2018 at 21:54 Comment(1)
As I understand 1e-3 will truncate to 3 digits after dot. I liked this answer but it doesn't seems to work for 4 and 5.Charger
T
4

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))
Theo answered 22/7, 2010 at 5:46 Comment(0)
C
4

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
Croupier answered 6/9, 2014 at 16:42 Comment(0)
I
4

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]
Incommunicable answered 25/3, 2015 at 4:47 Comment(1)
This only truncates with positive numbers, negative numbers will round down (away from zero).Osswald
F
2

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
Famulus answered 7/11, 2016 at 17:16 Comment(1)
Great! The cast to int truncates both positive and negative numbers.Osswald
P
2
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

Polymerization answered 24/6, 2018 at 16:26 Comment(1)
Nice but you are not rounding.Reseta
W
2

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
Wellordered answered 20/3, 2019 at 13:46 Comment(0)
E
2

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)
Endosperm answered 7/8, 2019 at 19:16 Comment(0)
A
1
def trunc(f,n):
  return ('%.16f' % f)[:(n-16)]
Aldos answered 22/7, 2010 at 2:12 Comment(0)
C
1

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);
Commandment answered 20/10, 2011 at 23:52 Comment(0)
D
1

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

Diabolic answered 28/11, 2014 at 13:19 Comment(0)
N
1

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.

Nianiabi answered 7/6, 2018 at 14:14 Comment(0)
A
1

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.

Autecology answered 20/12, 2018 at 9:29 Comment(0)
T
1

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
Train answered 21/4, 2019 at 2:12 Comment(1)
Your proposal is problematic if the number being truncating is small since you would waste a lot precision with the leading 0 to the right of the decimal point. But this problem is endemic to the problem as stated. What I'm trying to say is that significant figures is the real answer.Redfin
R
1

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])
Rapturous answered 3/6, 2021 at 10:42 Comment(0)
R
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.

Recidivate answered 7/7, 2022 at 19:37 Comment(0)
V
1

I think what you are looking for is this:

n = 1.923328437452
float(str(n)[:5])

NOTE [:5] is not inclusive. Prints float

1.923

Alternatively, the following will give you the same result and it is more efficient. If you need two decimal for example just change 1000 to 100:

int(n * 1000) / 1000.0
Vashti answered 16/3, 2023 at 3:22 Comment(0)
I
0

use numpy.round

import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Isolation answered 11/4, 2015 at 6:2 Comment(0)
C
0

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']
Convocation answered 4/7, 2018 at 11:14 Comment(0)
H
-1

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

Hysell answered 8/8, 2012 at 16:30 Comment(0)
S
-1
# value  value to be truncated
# n  number of values after decimal

value = 0.999782
n = 3
float(int(value*1en))*1e-n
Stegall answered 31/8, 2015 at 2:24 Comment(0)
D
-3

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

print '%.3f' % number
Dereliction answered 23/4, 2009 at 23:1 Comment(1)
That rounds the number off, it doesn't truncate.Foskett

© 2022 - 2024 — McMap. All rights reserved.