94

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'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SuperString
  • 21,593
  • 37
  • 91
  • 122
  • 7
    There is no such value in the set that are represented by floating-point numbers. – Karl Knechtel Dec 21 '11 at 20:55
  • 3
    In case Karl's comment is not clear enough: There is *no such number* as 1324343032.324 in binary floating point. If you switch to a higher version of Python (2.7 or 3.1 or later) the interpreter will *display* 1324343032.324 for you. But in actuality, the number you are computing with is neither 1324343032.324 nor 1324343032.3239999 regardless of Python version. The only way to get *exactly* 1324343032.324 is to use the `decimal` module or some other arbitrary-precision math library, such as `gmpy`. – John Y Dec 21 '11 at 23:14
  • The accepted answer below is correct, if you want to round (up) to a given number of decimal places. However, what the question is asking, and what I wanted to know, is how to truncate to a particular number of decimal places. For me, `'%.3f'%(1324343032.3243)` and `'%.3f'%(1324343032.3245)` give different results. (I am using Python 2.7.8). – nullstellensatz Feb 03 '15 at 13:44
  • 1
    @nullstellensatz http://stackoverflow.com/questions/783897/truncating-floats-in-python – Abhranil Das Feb 04 '15 at 22:49
  • 1
    @AbhranilDas this question is a duplicate of the one you pointed to. Since both of the questions have misleading answers, I have marked this one as a duplicate, so that all issues related to truncating can be dealt with in one place. Also, check out my comments and suggested answer for the original question. – nullstellensatz Feb 05 '15 at 03:59
  • 1
    Maybe python changed since this question, `int(1324343032.324325235 * 1000) / 1000.0` seems to work well – Chenna V Mar 04 '20 at 20:18

21 Answers21

92

You can use an additional float() around it if you want to preserve it as a float.

val = '%.3f'%(1324343032.324325235)
edoriggio
  • 331
  • 3
  • 18
Abhranil Das
  • 5,702
  • 6
  • 35
  • 42
  • I dont want to print it...I want to store it – SuperString Dec 21 '11 at 20:33
  • 6
    This is basically the correct answer, just use `val = '%.3f'%(1324343032.324325235)` instead of `print`. – David H. Clements Dec 21 '11 at 20:37
  • Edited. You can print it, save it, whatever. – Abhranil Das Dec 21 '11 at 20:41
  • Except that this is the same value that the OP already had (namely, 1324343032.3239999). – John Y Dec 21 '11 at 22:58
  • I understand. But I think the question here was a simple truncation. Since @SuperString used `str()` in one of his attempts, I assumed he was just looking for a truncated display. – Abhranil Das Dec 22 '11 at 20:35
  • Well, if you understand, then you also know that "preserve it as a float" is not correct. He already has the float. `float('%.3f'%(1324343032.324325235))` is the **exact same thing** as `round(1324343032.324325235, 3)`. Since his first comment here is "I don't want to print it...I want to store it", it is clear he hasn't gained anything at all by doing what you are suggesting. – John Y Dec 23 '11 at 05:48
  • 18
    This answer is correct, if you want to round (up) to a given number of decimal places. However, what the question is asking, and what I wanted to know, is how to *truncate* to a particular number of decimal places. For me, `'%.3f'%(1324343032.3243)` and `'%.3f'%(1324343032.3245)` give different results. (I am using Python 2.7.8). – nullstellensatz Feb 03 '15 at 13:38
  • 6
    '%.2f' % 8866.316 is round but not truncate – Oleg Jan 30 '18 at 13:18
  • 3
    you are rounding; take a look at my answer – alsjflalasjf.dev Apr 15 '18 at 19:21
  • 8
    This it *not* truncating. – Darkenor May 22 '20 at 15:31
  • @Darkenor its not truncating but we get the idea, what else would you call it ? This is a good solution for something that is not built in. The only other option I could think of is knowing how the floating point number is stored and bit masking. Everyone else says multiple the number and then divide by a power of 10 which just says value overflow all over it. – John Sohn Oct 06 '21 at 13:03
  • @AbhranilDas This looks like something I would have done in high school with pascal but its a good answer that works. Props. – John Sohn Oct 06 '21 at 13:04
87

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
Erwin Mayer
  • 18,076
  • 9
  • 88
  • 126
  • 14
    This should really be the accepted answer. The code is simple, elegant, and makes the most sense. Use the standard truncate function, however first shift the decimal place, shifting the decimal place back once the truncate has been performed. – CatalystNZ Feb 27 '18 at 23:57
  • note floating point precision errors `truncate(-1.13, 2) == -1.12`, i've change it a bit (with a bitter taste in the mouth) to `math.trunc(round(stepper * number, digits * 3)) / stepper` – eplaut May 07 '19 at 11:02
  • you can just do int(stepper * number) / stepper – Yohan E Jul 17 '20 at 15:50
  • 1
    I've tried this with the following numbers: number = 16.4 and digits = 2; the resulting value is 16.39 unfortunately. – Mo Kanj May 16 '22 at 17:47
  • @MoKanj Good point, I have updated the answer to improve the accuracy. – Erwin Mayer May 18 '22 at 22:23
31

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

About decimals module

About rounding settings

solveMe
  • 1,866
  • 1
  • 18
  • 20
  • 1
    I think this should be one of the more accepted answers... it teaches you how to utilize & modify modules to your advantage, which expresses how truly dynamic and powerful python can be – greenhouse Jun 28 '19 at 21:54
  • @solveme the only issue with this and personally I don't know why the standard math lib doesn't have round in it., is that the decimal library being very high precision reportedly comes with a performance cost. still a 100x better than the answer directly above which would throw an exception with a number too big. good job. – John Sohn Oct 06 '21 at 13:06
  • The problem here is that it's trading the undesirable string manipulation for the equally (if not more) undesirable package import, plus the added overhead of `decimal`. Which one is worse will probably depend on the specific use case. – Z4-tier Feb 11 '23 at 07:38
  • Looks like, If module import is so bad, it's better to not use python at all. – solveMe Feb 13 '23 at 08:49
13

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)

Community
  • 1
  • 1
AlG
  • 14,697
  • 4
  • 41
  • 54
7

'%.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.

alsjflalasjf.dev
  • 1,589
  • 13
  • 11
7

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
gorandp
  • 500
  • 5
  • 11
6

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'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Yiqing Lan
  • 101
  • 1
  • 3
6

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)
primelens
  • 457
  • 4
  • 8
  • 1
    It's good that you mentioned the decimal module first, because that is the only fully correct answer. One thing to be a little careful of with the rest is that `b` in your example will be displayed as 1324343032.3239999 on versions of Python before 2.7. And indeed, this is the value that OP is seeing when he tries. Of course, both values are indistinguishable, in terms of binary floating point. – John Y Dec 21 '11 at 22:56
4

Maybe this way:

def myTrunc(theNumber, theDigits):

    myDigits = 10 ** theDigits
    return (int(theNumber * myDigits) / myDigits)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
OSchubert
  • 41
  • 2
4

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.

Vladimir Prudnikov
  • 6,974
  • 4
  • 48
  • 57
4

I suggest next solution:

def my_floor(num, precision):
   return f'{num:.{precision+1}f}'[:-1]

my_floor(1.026456,2) # 1.02

Danylkaaa
  • 151
  • 1
  • 11
  • I like this because it is using the modern Python string interpolation. But I don't understand why you have made it one more length than it needs to be and then cut off the last character. This can't be to do with rounding, as your solution does what the OP asked for, truncation, not rounding (as demonstrated by your example). – mike rodent Oct 20 '21 at 13:43
  • 1
    Taking one extra digit and then removing it is necessary, because formatting a number with the string _itself rounds the number_! For example: `f'{0.55:.2f}'[:-1]` returns `'0.5'`, but `f'{0.55:.1f}'` returns `'0.6'`. This is also why this solution will fail on pathological cases like `1.9999999`. It will always return `'2.0x'`, where `0x` denotes as many zeros as the desired precision. – Tushar Rakheja Nov 13 '22 at 20:27
2

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)
revliscano
  • 2,227
  • 2
  • 12
  • 21
2

Almo's link explains why this happens. To solve the problem, use the decimal library.

Spencer Rathbun
  • 14,510
  • 6
  • 54
  • 73
1

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)]
Eduardo Alvim
  • 201
  • 2
  • 4
0

You can also use:

import math

nValeur = format(float(input('Quelle valeur ?    ')), '.3f')

In Python 3.6 it would work.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Squid0ne
  • 11
  • 2
0
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
FFT
  • 929
  • 8
  • 17
0

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
Chenna V
  • 10,185
  • 11
  • 77
  • 104
0

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)
Amit Baderia
  • 4,454
  • 3
  • 27
  • 19
0

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
0

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)
darklow
  • 2,249
  • 24
  • 22
-1
>>> float(1324343032.324325235) * float(1000) / float(1000)

1324343032.3243253

>>> round(float(1324343032.324325235) * float(1000) / float(1000), 3)

1324343032.324
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
  • 1
    While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – DimaSan Mar 18 '17 at 01:37