277

How can I format a float so that it doesn't contain trailing zeros? In other words, I want the resulting string to be as short as possible.

For example:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
martineau
  • 119,623
  • 25
  • 170
  • 301
TarGz
  • 3,319
  • 4
  • 19
  • 11
  • 1
    That example doesn't make any sense at all. `3.14 == 3.140` -- They're the *same* floating point number. For that matter 3.140000 is the same floating-point number. The zero doesn't exist in the first place. – S.Lott Mar 14 '10 at 01:08
  • 65
    @S.Lott - I think the issue is PRINTING the float number without the trailing zeros, not the actual equivalence of two numbers. – pokstad Mar 14 '10 at 01:14
  • 1
    @pokstad: In which case, there's no "superfluous" zero. `%0.2f` and `%0.3f` are the two formats required to produce the last numbers on the left. Use `%0.2f` to produce the last two numbers on the right. – S.Lott Mar 14 '10 at 01:16
  • 11
    ```3.0 -> "3"``` is still a valid use case. ```print( '{:,g}'.format( X )``` worked for me to output ```3``` where ```X = 6 / 2``` and when ```X = 5 / 2``` I got an output of ```2.5``` as expected. – ShoeMaker Feb 27 '16 at 23:51
  • Python should be batteries included with better number formatting. In Excel, this is simple. Why not borrow the convention from Excel? – ChaimG Sep 11 '19 at 15:07
  • 1
    @S.Lott Sure there are superfluous zeros. If you would get the string "3.140" as the output of a conversion, that could be considered a superfluous zero since it doesn't make any difference when it comes to determining the value of the number. – HelloGoodbye Aug 10 '22 at 07:31
  • @drevicko: **`"%s"` gives no one what they want.** `"%s" % 3.0 == '3.0'`, which is exactly what this question is attempting to avoid. You do realize that `%s` is just syntactic sugar for `str()`, don't you? Please stop upvoting bad comments, people. I am shaking my head over here and getting a headache. – Cecil Curry Oct 15 '22 at 03:52

20 Answers20

257

You could use %g to achieve this:

'%g'%(3.140)

or, with Python ≥ 2.6:

'{0:g}'.format(3.140)

or, with Python ≥ 3.6:

f'{3.140:g}'

From the docs for format: g causes (among other things)

insignificant trailing zeros [to be] removed from the significand, and the decimal point is also removed if there are no remaining digits following it.

Andreas
  • 127
  • 8
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • 58
    Oh, almost! Sometimes it formats the float in scientific notation ("2.342E+09") - is it possible to turn it off, i.e. always show all significant digits? – TarGz Mar 14 '10 at 00:46
  • 7
    Why use `'{0:...}'.format(value)` when you could use `format(value, '...')`? That avoids having to parse out the format specifier from a template string that is otherwise empty. – Martijn Pieters Aug 11 '16 at 09:00
  • 4
    @MartijnPieters: The miniscule cost of parsing out the format specifier is swamped by other overhead AFAICT; in practice, my local benchmarks on 3.6 (with function scoping of the microbenchmark to accurately model real code) have `format(v, '2.5f')` take ~10% longer than `'{:2.5f}'.format(v)`. Even if it didn't, I tend to use the `str` method form because when I need to tweak it, add additional values to it, etc., there is less to change. Of course, as of 3.6 we have f-strings for most purposes. :-) – ShadowRanger Aug 25 '18 at 02:12
  • 16
    In Python 3.6, this can be shortened to `f"{var:g}"` where `var` is a float variable. – TheGreatCabbage Sep 24 '19 at 15:15
  • @TarGz : I had success with `'%d'%numpy.rint(my_number)`. Basically what it does is that it rounds the number to the closest integer using numpy's `rint` method (you could use `round` as well), then prints it using an int flag (%d). You could use `'%d'%my_number` directly but then it would round down the number instead of rounding to the closest. – Johannes Lemonde Nov 10 '21 at 13:47
  • Can also drop the `0` and use `'{:g}'.format(3.140)` directly – Diego Nov 22 '21 at 23:36
239

Me, I'd do ('%f' % x).rstrip('0').rstrip('.') -- guarantees fixed-point formatting rather than scientific notation, etc etc. Yeah, not as slick and elegant as %g, but, it works (and I don't know how to force %g to never use scientific notation;-).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • Thanks it works exactly like I wanted! Just a tiny bit unfortunate there is no presentation type for this kind of behaviour.... – TarGz Mar 14 '10 at 01:18
  • @TarGz, agreed, it would surely be more elegant to have some %-flags for that. For modern Python's approach see http://docs.python.org/library/string.html?highlight=string#string-formatting -- but it seems to behave, in regard to your specific problem, just like `'%g' % x` used to, it just has arguably nicer syntax. Plus, you can now subclass `string.Formatter` to do your own customizations. – Alex Martelli Mar 14 '10 at 01:28
  • 8
    The only problem with that is `'%.2f' % -0.0001` will leave you with `-0.00` and ultimately `-0`. – Kos Dec 07 '12 at 13:14
  • 4
    @alexanderlukanin13 because the default precision is 6, see https://docs.python.org/2/library/string.html: `'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.` You would have to use '%0.7f' in the above solution. – derenio Aug 31 '15 at 16:55
  • 4
    @derenio Good point :-) I can only add that raising precision above `'%0.15f'` is a bad idea, because [weird stuff](https://en.wikipedia.org/wiki/IEEE_floating_point) starts to happen. – alexanderlukanin13 Sep 01 '15 at 15:38
  • @alexanderlukanin13 right, ~15.95 decimal digits is the limit for "double precision" (64bits) floats, for "single precision" (32bits) it's ~7.22 decimal digits. – derenio Sep 01 '15 at 16:49
  • I wonder if it would be faster or slower as: ('%f' % x).rstrip('0.') – Victoria Mar 28 '17 at 07:08
  • It is not the full answer for the question e.g. ('%f' % 15.10).rstrip('0').rstrip('.') return '15.100001' which is not what he asking for but i have not better answer how to avoid this unprecision – Livius May 11 '17 at 20:29
  • @Victoria It is faster, but fails `('%.2f' % -0.001).rstrip('0.')` when it reduces to `'-'` – herrlich10 Jun 24 '17 at 02:12
  • what if it is in template – Mohammed Shareef C Mar 24 '18 at 07:32
  • 2
    In case you're in a middle of some other string: `print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))` – fault-tolerant Apr 28 '18 at 08:10
  • 1
    @Alex Martelli, get rid of the double `rstrip` command. Use simply this instead to strip both the dot (`.`) and all trailing zeros afterwards in one operation: `('%f' % x).rstrip('.0')` – Gabriel Staples Aug 28 '18 at 17:38
  • 1
    @GabrielStaples That will remove zeros that occur before the decimal place. I think this is because the `.` is a wildcard that will match a single character. – Peter Schorn Dec 30 '19 at 10:47
  • 3
    @Peter Schorn: you're right that Gabriel Staples's optimization is bad, but it's because the OP's trick requires you to remove all zeroes THEN all decimals and then NOT MORE ZEROS. Gabriel's approach just removes all zeros and periods until it hits something else. – Scott Stafford May 11 '20 at 14:47
26

After looking over answers to several similar questions, this seems to be the best solution for me:

def floatToString(inputValue):
    return ('%.15f' % inputValue).rstrip('0').rstrip('.')

My reasoning:

%g doesn't get rid of scientific notation.

>>> '%g' % 0.000035
'3.5e-05'

15 decimal places seems to avoid strange behavior and has plenty of precision for my needs.

>>> ('%.15f' % 1.35).rstrip('0').rstrip('.')
'1.35'
>>> ('%.16f' % 1.35).rstrip('0').rstrip('.')
'1.3500000000000001'

I could have used format(inputValue, '.15f'). instead of '%.15f' % inputValue, but that is a bit slower (~30%).

I could have used Decimal(inputValue).normalize(), but this has a few issues as well. For one, it is A LOT slower (~11x). I also found that although it has pretty great precision, it still suffers from precision loss when using normalize().

>>> Decimal('0.21000000000000000000000000006').normalize()
Decimal('0.2100000000000000000000000001')
>>> Decimal('0.21000000000000000000000000006')
Decimal('0.21000000000000000000000000006')

Most importantly, I would still be converting to Decimal from a float which can make you end up with something other than the number you put in there. I think Decimal works best when the arithmetic stays in Decimal and the Decimal is initialized with a string.

>>> Decimal(1.35)
Decimal('1.350000000000000088817841970012523233890533447265625')
>>> Decimal('1.35')
Decimal('1.35')

I'm sure the precision issue of Decimal.normalize() can be adjusted to what is needed using context settings, but considering the already slow speed and not needing ridiculous precision and the fact that I'd still be converting from a float and losing precision anyway, I didn't think it was worth pursuing.

I'm not concerned with the possible "-0" result since -0.0 is a valid floating point number and it would probably be a rare occurrence anyway, but since you did mention you want to keep the string result as short as possible, you could always use an extra conditional at very little extra speed cost.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result
PolyMesh
  • 2,228
  • 2
  • 20
  • 23
  • 2
    Unfortunately only works with numbers with fewer than roughly) five or more digits to the left of the decimal place. `floatToString(12345.6)` returns `'12345.600000000000364'` for example. Decreasing the 15 in `%.15f` to a lower number solves it in this example, but that value needs to be decreased more and more as the number gets larger. It could be dynamically calculated based on the log-base-10 of the number, but that quickly becomes very complicated. – JohnSpeeks Mar 29 '17 at 03:21
  • 1
    One way to solve that problem might be to limit the length of the whole number (rather than just the digits after the decimal): `result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')` – Timothy Smith Jul 06 '18 at 17:08
  • 1
    @JohnSpeeks I'm not sure this is avoidable. It is a side effect of floating numbers not being able to represent the accuracy if more digits are required on the left side. From what I can tell, the number that comes out as a string is the same number that goes in as a float, or at least the closest representation of it. `>>>12345.600000000000364 == 12345.6` `True` – PolyMesh Mar 07 '19 at 00:26
  • I wrote another [solution](https://stackoverflow.com/questions/24634664/python-round-a-float-to-2-digits/62255623#62255623). – niitsuma Jun 10 '20 at 03:27
15

What about trying the easiest and probably most effective approach? The method normalize() removes all the rightmost trailing zeros.

from decimal import Decimal

print (Decimal('0.001000').normalize())
# Result: 0.001

Works in Python 2 and Python 3.

-- Updated --

The only problem as @BobStein-VisiBone pointed out, is that numbers like 10, 100, 1000... will be displayed in exponential representation. This can be easily fixed using the following function instead:

from decimal import Decimal


def format_float(f):
    d = Decimal(str(f));
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
Ander
  • 5,093
  • 7
  • 41
  • 70
  • 4
    Except `Decimal('10.0').normalize()` becomes `'1E+1'` – Bob Stein Feb 12 '17 at 15:21
  • Good point. A workaround could be with f-string: `f'{Decimal(10.0).normalize():f}'`. This will return 10 (inspired by [this thread](https://stackoverflow.com/questions/46957416/can-decimal5e1-be-simply-converted-to-decimal50-in-python)) – Shan Dou Dec 28 '22 at 20:33
12

Here's a solution that worked for me. It's a blend of the solution by PolyMesh and use of the new .format() syntax.

for num in 3, 3., 3.0, 3.1, 3.14, 3.140:
    print('{0:.2f}'.format(num).rstrip('0').rstrip('.'))

Output:

3
3
3
3.1
3.14
3.14
Kaushal Modi
  • 1,258
  • 2
  • 20
  • 43
  • Only thing wrong with this one is that you have to set a sensible number of decimal digits. The higher you set it, the more precise numbers you can represent, but if you do this a lot, it can degrade performance. – beruic Jun 29 '17 at 12:10
  • 1
    Adding to beruic's comment, this doesn't work for floats of greater precision (e.g. `3.141`) as the `.2f` is hard-coded. – TrebledJ Jun 06 '19 at 05:47
  • 1
    result = "{:.{}f}".format(float(format(number).rstrip('0').rstrip('.')), precision), fixes that issue TrebledJ. – Kris Kizlyk Mar 05 '21 at 06:17
  • 1
    great for a one liner one use application with no extra libraries – Paul Collingwood Apr 25 '21 at 11:28
5

You can simply use format() to achieve this:

format(3.140, '.10g') where 10 is the precision you want.

clel
  • 175
  • 1
  • 3
  • 8
5

if you want something that works both on numeric or string input (thanks to @mike-placentra for bug hunting):

def num(s):
    """ 3.0 -> 3, 3.001000 -> 3.001 otherwise return s """
    s = str(s)
    try:
        int(float(s))
        if '.' not in s:
            s += '.0'
        return s.rstrip('0').rstrip('.')
    except ValueError:
        return s

>>> for n in [3, 3., 3.0, 3.1, 3.14, 3.140, 3.001000, 30 ]: print(num(n))
... 
3
3
3
3.1
3.14
3.14
3.001
30

>>> for n in [3, 3., 3.0, 3.1, 3.14, 3.140, 3.001000, 30 ]: print(num(str(n)))
... 
3
3
3
3.1
3.14
3.14
3.001
30
Yuri
  • 511
  • 3
  • 6
3
>>> str(a if a % 1 else int(a))
Shameem
  • 2,664
  • 17
  • 21
  • Don't you mean `int(a) if a % 1 else a`? – beruic Jun 29 '17 at 12:03
  • Dear Beruic, your answer results negative answer. `a if a % 1 else int(a)` is correct. Question needs output in string , So I just added `str` – Shameem Jun 29 '17 at 14:35
  • Ah, I get it now. `a % 1` is truthy because it is non-zero. I implicitly and wrongly perceived it as `a % 1 == 0`. – beruic Jun 29 '17 at 15:17
3

While formatting is likely that most Pythonic way, here is an alternate solution using the more_itertools.rstrip tool.

import more_itertools as mit


def fmt(num, pred=None):
    iterable = str(num)
    predicate = pred if pred is not None else lambda x: x in {".", "0"}
    return "".join(mit.rstrip(iterable, predicate))


assert fmt(3) == "3"
assert fmt(3.) == "3"
assert fmt(3.0) == "3"
assert fmt(3.1) == "3.1"
assert fmt(3.14) == "3.14"
assert fmt(3.140) == "3.14"
assert fmt(3.14000) == "3.14"
assert fmt("3,0", pred=lambda x: x in set(",0")) == "3"

The number is converted to a string, which is stripped of trailing characters that satisfy a predicate. The function definition fmt is not required, but it is used here to test assertions, which all pass. Note: it works on string inputs and accepts optional predicates.

See also details on this third-party library, more_itertools.

pylang
  • 40,867
  • 14
  • 129
  • 121
  • 2
    Most of the solutions here (including this one) totally forget about integers ending in 0 which is an unwanted behavior. – Paulo Freitas Sep 12 '21 at 04:30
  • When you add a mandatory third-party dependency to your app just to strip floats, you know something is profoundly wrong. – Cecil Curry Oct 15 '22 at 03:42
  • @CecilCurry perhaps, but you are mistaken. The package is not mandatory. You can reimplement the function without the package, if you wish. https://github.com/more-itertools/more-itertools/blob/master/more_itertools/more.py#L2373 – pylang Oct 15 '22 at 23:47
3

A new challenger has appeared.

def prettify_float(real: float, precision: int = 2) -> str:
    '''
    Prettify the passed floating-point number into a human-readable string,
    rounded and truncated to the passed number of decimal places.

    This converter prettifies floating-point numbers for human consumption,
    producing more readable results than the default :meth:`float.__str__`
    dunder method. Notably, this converter:

    * Strips all ignorable trailing zeroes and decimal points from this number
      (e.g., ``3`` rather than either ``3.`` or ``3.0``).
    * Rounds to the passed precision for perceptual uniformity.

    Parameters
    ----------
    real : float
        Arbitrary floating-point number to be prettified.
    precision : int, optional
        **Precision** (i.e., number of decimal places to round to). Defaults to
        a precision of 2 decimal places.

    Returns
    ----------
    str
        Human-readable string prettified from this floating-point number.

    Raises
    ----------
    ValueError
        If this precision is negative.
    '''

    # If this precision is negative, raise an exception.
    if precision < 0:
        raise ValueError(f'Negative precision {precision} unsupported.')
    # Else, this precision is non-negative.

    # String prettified from this floating-point number. In order:
    # * Coerce this number into a string rounded to this precision.
    # * Truncate all trailing zeroes from this string.
    # * Truncate any trailing decimal place if any from this string.
    result = f'{real:.{precision}f}'.rstrip('0').rstrip('.')

    # If rounding this string from a small negative number (e.g., "-0.001")
    # yielded the anomalous result of "-0", return "0" instead; else, return
    # this result as is.
    return '0' if result == '-0' else result

Don't Believe My Lies

pytest-style unit tests or it didn't happen.

def test_prettify_float() -> None:
    '''
    Test usage of the :func:`prettify_float` prettifier.
    '''

    # Defer test-specific imports.
    from pytest import raises

    # Assert this function prettifies zero as expected.
    assert prettify_float(0.0) == '0'

    # Assert this function prettifies a negative integer as expected.
    assert prettify_float(-2.0) == '-2'

    # Assert this prettifier prettifies a small negative float as expected.
    assert prettify_float(-0.001) == '0'

    # Assert this prettifier prettifies a larger negative float as expected.
    assert prettify_float(-2.718281828) == '-2.72'
    assert prettify_float(-2.718281828, precision=4) == '-2.7183'

    # Assert this function prettifies a positive integer as expected.
    assert prettify_float(3.0) == '3'

    # Assert this function prettifies a positive float as expected.
    assert prettify_float(3.14159265359) == '3.14'
    assert prettify_float(3.14159265359, precision=4) == '3.1416'

    # Assert this prettifier raises the expected exception when passed a
    # negative precision.
    with raises(ValueError):
        prettify_float(2.718281828, precision=-2)

%100 Pure Python

Ignore seductively simpler answers that promote:

  • Trivial one liners. They all fail under common edge cases like whole numbers or small negative floats.
  • Third-party packages. NumPy, QuantiPhy, and more_itertools? Surely you jest. Don't increase your maintenance burden or code debt any more than you must. That said...

Throw @beartype on prettify_float() for added runtime safety and you're golden! Your userbase will shower you with praise. Then so will I. Also, I'm pretty sure my bias is showing here.

See Also

This answer stands on the shoulders of giant mammoths – including:

  1. Alex Martelli's clever accepted answer.
  2. PolyMesh's generalization of Martelli's answer to catch the edge case of small negative floats.
  3. Kaushal Modi's generalization of PolyMesh's answer to force a precision of two decimal places.
Cecil Curry
  • 9,789
  • 5
  • 38
  • 52
  • 2
    TL;DR `f'{my_float:.2f}'.rstrip('0').rstrip('.')` And... if you use this for negative numbers, you might get `-0` when rounding up from a small negative number, so need to handle that. – M3RS Oct 19 '22 at 06:47
2

For float you could use this:

def format_float(num):
    return ('%i' if num == int(num) else '%s') % num

Test it:

>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'

For Decimal see solution here: https://stackoverflow.com/a/42668598/5917543

Community
  • 1
  • 1
Artem Skoretskiy
  • 878
  • 10
  • 9
2

Here's the answer:

import numpy

num1 = 3.1400
num2 = 3.000
numpy.format_float_positional(num1, 3, trim='-')
numpy.format_float_positional(num2, 3, trim='-')

output "3.14" and "3"

trim='-' removes both the trailing zero's, and the decimal.

N'Bayramberdiyev
  • 5,936
  • 7
  • 27
  • 47
comport9
  • 71
  • 1
  • 6
  • 1
    Using GIANT library to achieve only single feature is not wise. – Soyuzbek Orozbek Uulu Apr 13 '21 at 23:27
  • I'm already using numpy as a dependency so this is acceptable. Unlike the most upvoted solution (which I also upvoted, to a total of 201) format_float_positional never converts my decimals to standard notation and maintains precision. – firescar96 Mar 26 '22 at 19:34
1

OP would like to remove superflouous zeros and make the resulting string as short as possible.

I find the %g exponential formatting shortens the resulting string for very large and very small values. The problem comes for values that don't need exponential notation, like 128.0, which is neither very large or very small.

Here is one way to format numbers as short strings that uses %g exponential notation only when Decimal.normalize creates strings that are too long. This might not be the fastest solution (since it does use Decimal.normalize)

def floatToString (inputValue, precision = 3):
    rc = str(Decimal(inputValue).normalize())
    if 'E' in rc or len(rc) > 5:
        rc = '{0:.{1}g}'.format(inputValue, precision)        
    return rc

inputs = [128.0, 32768.0, 65536, 65536 * 2, 31.5, 1.000, 10.0]

outputs = [floatToString(i) for i in inputs]

print(outputs)

# ['128', '32768', '65536', '1.31e+05', '31.5', '1', '10']
kinok
  • 21
  • 5
1

Using the QuantiPhy package is an option. Normally QuantiPhy is used when working with numbers with units and SI scale factors, but it has a variety of nice number formatting options.

    >>> from quantiphy import Quantity

    >>> cases = '3 3. 3.0 3.1 3.14 3.140 3.14000'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:p}')
          3 -> 3
         3. -> 3
        3.0 -> 3
        3.1 -> 3.1
       3.14 -> 3.14
      3.140 -> 3.14
    3.14000 -> 3.14

And it will not use e-notation in this situation:

    >>> cases = '3.14e-9 3.14 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:,p}')
    3.14e-9 -> 0
       3.14 -> 3.14
     3.14e9 -> 3,140,000,000

An alternative you might prefer is to use SI scale factors, perhaps with units.

    >>> cases = '3e-9 3.14e-9 3 3.14 3e9 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case, 'm')
    ...    print(f'{case:>7} -> {q}')
       3e-9 -> 3 nm
    3.14e-9 -> 3.14 nm
          3 -> 3 m
       3.14 -> 3.14 m
        3e9 -> 3 Gm
     3.14e9 -> 3.14 Gm
-1

Try this and it will allow you to add a "precision" variable to set how many decimal places you want. Just remember that it will round up. Please note that this will only work if there is a decimal in the string.

 number = 4.364004650000000
 precision = 2
 result = "{:.{}f}".format(float(format(number).rstrip('0').rstrip('.')), precision)

Output

 4.364004650000000
 4.36
Kris Kizlyk
  • 151
  • 8
-2

You can use max() like this:

print(max(int(x), x))

Xantium
  • 11,201
  • 10
  • 62
  • 89
elig
  • 2,635
  • 3
  • 14
  • 24
  • 1
    you have to consider the case where `x` is negative. `if x < 0: print(min(x), x)` `else : print(max(x), x)` – ThunderPhoenix Sep 29 '18 at 11:19
  • A useful method when I want to do json stringify. float 1.0 change to int 1, so it perform just same as in javascript. – pingze Apr 18 '19 at 10:02
-2

"{:.5g}".format(x)

I use this to format floats to trail zeros.

martin sun
  • 19
  • 2
-2

Use %g with big enough width, for example '%.99g'. It will print in fixed-point notation for any reasonably big number.

EDIT: it doesn't work

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'
alexanderlukanin13
  • 4,577
  • 26
  • 29
  • 2
    `.99` is precision, not width; kinda useful but you don't get to set the actual precision this way (other than truncating it yourself). – Kos Dec 07 '12 at 13:11
-4

You can achieve that in most pythonic way like that:

python3:

"{:0.0f}".format(num)
Lan Vukušič
  • 156
  • 1
  • 10
-7

Handling %f and you should put

%.2f

, where: .2f == .00 floats.

Example:

print "Price: %.2f" % prices[product]

output:

Price: 1.50