Python string.format() percentage without rounding

Question:

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%'
Asked By: Ashy

||

Answers:

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)

Answered By: danodonovan

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%'
Answered By: Ashwini Chaudhary

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%'
Answered By: Martijn Pieters

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
Answered By: jpp

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)}%"
Answered By: lapin