Python string.format() percentage without rounding
Asked Answered
D

5

28

In the example below I would like to format to 1 decimal place but python seems to like rounding up the number, is there a way to make it not round the number up?

>>> '{:.1%}'.format(0.9995)
'100.0%'
>>> '{:.2%}'.format(0.9995)
'99.95%'
Dubiety answered 12/7, 2013 at 8:30 Comment(0)
Z
19

If you want to round down always (instead of rounding to the nearest precision), then do so, explicitly, with the math.floor() function:

from math import floor

def floored_percentage(val, digits):
    val *= 10 ** (digits + 2)
    return '{1:.{0}f}%'.format(digits, floor(val) / 10 ** digits)

print floored_percentage(0.995, 1)

Demo:

>>> from math import floor
>>> def floored_percentage(val, digits):
...     val *= 10 ** (digits + 2)
...     return '{1:.{0}f}%'.format(digits, floor(val) / 10 ** digits)
... 
>>> floored_percentage(0.995, 1)
'99.5%'
>>> floored_percentage(0.995, 2)
'99.50%'
>>> floored_percentage(0.99987, 2)
'99.98%'
Zuleika answered 12/7, 2013 at 8:44 Comment(0)
M
2

With Python 3.6+, you can use formatted string literals, also known as f-strings. These are more efficient than str.format. In addition, you can use more efficient floor division instead of math.floor. In my opinion, the syntax is also more readable.

Both methods are included below for comparison.

from math import floor
from random import random

def floored_percentage(val, digits):
    val *= 10 ** (digits + 2)
    return '{1:.{0}f}%'.format(digits, floor(val) / 10 ** digits)

def floored_percentage_jpp(val, digits):
    val *= 10 ** (digits + 2)
    return f'{val // digits / 10 ** digits:.{digits}f}%'

values = [random() for _ in range(10000)]

%timeit [floored_percentage(x, 1) for x in values]      # 35.7 ms per loop
%timeit [floored_percentage_jpp(x, 1) for x in values]  # 28.1 ms per loop
Mceachern answered 23/8, 2018 at 8:43 Comment(0)
W
1

Something like this:

def my_format(num, x):
     return str(num*100)[:4 + (x-1)] + '%'

>>> my_format(.9995, 1)
'99.9%'
>>> my_format(.9995, 2)
'99.95%'
>>> my_format(.9999, 1)
'99.9%'
>>> my_format(0.99987, 2)
'99.98%'
Weighting answered 12/7, 2013 at 8:37 Comment(0)
T
0

There are a couple of ways, maybe the easiest is

x = str(10. * 0.9995).split('.')
my_string = '%s.%s%%' % (x[0], x[1][:2])

this will ensure that you always have the decimal point in the correct place (for edge cases like 1.0000 or 0.001)

Tombouctou answered 12/7, 2013 at 8:37 Comment(0)
L
0

Expanding on accepted answer while using more modern python 3.6+ f-string formatting

def floored_percentage(val, digits):
    val *= 10 ** (digits + 2)
    return f'{floor(val) / 10 ** digits}%)'

Without any digits (like I needed), it's even simpler:

def floored_percentage(val):
    return f"{floor(val*100)}%"
Libbie answered 28/7, 2020 at 9:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.