34

For a scientific application I need to output very precise numbers, so I have to print 15 significant figures. There are already questions on this topic here, but they all concern with truncating the digits, not printing more.

I realized that the print function converts the input float to a 10 character string. Also, I became aware of the decimal module, but that does not suit my needs.

So the question is, how can I easily print a variable amount of signifcant figures of my floats, where I need to display more than 10?

2
  • Keep in mind that a float might not actually be capable of representing the values you're using to the precision you require, which means that your calculations could very well be horribly wrong.
    – Wooble
    Commented Feb 23, 2012 at 15:46
  • @Wooble This very nice answer delves into the question you're raising. It's worth a look. Commented Feb 23, 2012 at 16:02

7 Answers 7

62

Let:

>>> num = 0.0012345

For up to 3 significant figures:

>>> f'{num:.3}'
'0.00123'

For 3 decimal places:

>>> f'{num:.3f}'
'0.001'

See the "presentation types for floating point and decimal" table at the bottom of this section for any additional requirements provided by e, E, f, F, g, G, n, %, None.

5
  • 6
    This should be marked as the correct answer. See Wikipedia
    – wizclown
    Commented Mar 15, 2019 at 10:32
  • 6
    This displays numbers in scientific format, e.g. 12.0 => "1.2e+01", which may not be what people expect.
    – quantoid
    Commented Jun 3, 2019 at 0:06
  • 2
    @quantoid According to my REPL, f'{12.0:.3}' == '12.0'. But it seems you're correct when abs(float(f'{num:.3}')) >= 10**2. It's not totally unreasonable behavior either... for instance, how would you handle 1234.56? Printing either 1230 | 1230. is misleading because not a float | 0 is actually 4. Printing 1.23e4 seems the only reasonable solution here. Commented May 4, 2020 at 3:14
  • In any case, I think most people want to print numbers s.t. abs(float(f'{num:.{d}}')) < 10**d Commented May 4, 2020 at 3:15
  • This answer is wrong and should be deleted. f"{4.2:.3}" produced 4.2, which is not three significant figures.
    – Myridium
    Commented Jan 12 at 1:08
18

You could use the string formatting operator %:

In [3]: val = 1./3

In [4]: print('%.15f' % val)
0.333333333333333

or str.format():

In [8]: print(str.format('{0:.15f}', val))
Out[8]: '0.333333333333333'

In new code, the latter is the preferred style, although the former is still widely used.

For more info, see the documentation.

3
  • 8
    print('{0:.15f}'.format(val)) is also another way to do it in the new format.
    – TyrantWave
    Commented Feb 23, 2012 at 16:07
  • 22
    Note that this does not print 15 significant digits, but just 15 digits after the decimal point. For very large or very small numbers, this does not make much sense. IMHO, using "%.15e" might be more reasonable.
    – tobias_k
    Commented Jul 1, 2016 at 9:18
  • Yeah, this is decimal places not significant figures.
    – quantoid
    Commented Jun 3, 2019 at 0:02
11

To display N significant figures (not decimal places) you use the "g" format:

>>> x = 1.23
>>> print("%.2g" % x)
1.2
>>> x = 12.3
>>> print("%.2g" % x)
12

See format spec for details on precision:

The precision is a decimal number indicating how many digits should be displayed after the decimal point for a floating point value formatted with 'f' and 'F', or before and after the decimal point for a floating point value formatted with 'g' or 'G'. For non-number types the field indicates the maximum field size - in other words, how many characters will be used from the field content. The precision is not allowed for integer values.

2
  • 1
    The docs on g: The precise rules are as follows: suppose that the result formatted with presentation type 'e' and precision p-1 would have exponent exp. Then if -4 <= exp < p, the number is formatted with presentation type 'f' and precision p-1-exp. Otherwise, the number is formatted with presentation type 'e' and precision p-1. In both cases insignificant trailing zeros are removed from the significand, and the decimal point is also removed if there are no remaining digits following it, unless the '#' option is used. Commented Apr 4, 2020 at 6:38
  • This answer is wrong and should be deleted. f"{4.2:.3g}" produced 4.2, which is not three significant figures.
    – Myridium
    Commented Jan 12 at 1:09
11

Thought the original question wanted to format n significant figures, not n decimal points. So a custom function might be required until some more native built-in types are on offer? So you'll want something like:

def float_nsf(q,n):
    """
    Truncate a float to n significant figures.  May produce overflow in 
    very last decimal place when q < 1.  This can be removed by an extra 
    formatted print. 
    Arguments:
      q : a float
      n : desired number of significant figures
    Returns:
    Float with only n s.f. and trailing zeros, but with a possible small overflow.
    """
    sgn=np.sign(q)
    q=abs(q)
    n=int(np.log10(q/10.)) # Here you overwrite input n!
    if q<1. :
        val=q/(10**(n-1))
        return sgn*int(val)*10.**(n-1)
    else:
        val=q/(10**n)
        return sgn*int(val)*10.**n
1
  • This answer is wrong and should be deleted. float_nsf(4.2,3) produced 4.0, which is neither the correct answer nor three significant figures.
    – Myridium
    Commented Jan 12 at 1:13
3

Use these two common print idioms for formatting. Its a matter of personal taste on which is better.

value = 10/3            #gives a float of 3.33333.....

print '%.15f' % value
print str.format('{0:.15f}', value)

Personally I think the first is more compact and the 2nd is more explicit. Format has more features when working with multiple vals.

1
1

All existing answers are wrong.

Here is one that works for all cases that I've tried:

import numpy as np
def float_to_nsf(f: float, n: int) -> str:
  """Convert a float into a string with a chosen number of significant figures. 
     The decimal point is always included, even if trailing."""
  s = np.format_float_positional(f, fractional=False, min_digits=n, precision=n)
  if s[0:2] == "0.":
    s = "0." + f"{s[2:]:0<{n}}"
  return s
0

You could use this function I wrote, it seems to be working fine and it's quite simple!:

def nsf(num, n=1):
    """n-Significant Figures"""
    numstr = ("{0:.%ie}" % (n-1)).format(num)
    return float(numstr)
  1. It first converts the number to a string using exponent notation
  2. Then returns it as float.

Some tests:

>>> a = 2./3
>>> b = 1./3
>>> c = 3141592
>>> print(nsf(a))
0.7
>>> print(nsf(a, 3))
0.667
>>> print(nsf(-a, 3))
-0.667
>>> print(nsf(b, 4))
0.3333
>>> print(nsf(-b, 2))
-0.33
>>> print(nsf(c, 5))
3141600.0
>>> print(nsf(-c, 6))
-3141590.0

I hope this helps you ;)

5
  • Hmm, this kept spitting out numbers in exponent notation. Perhaps because I converted them to back to strings later?, i.e. str(float(...))
    – DrMisha
    Commented Apr 28, 2015 at 16:35
  • This answer is wrong and should be deleted. nsf(4.2,3) produced 4.2, which is not three significant figures.
    – Myridium
    Commented Jan 12 at 1:10
  • @Myridium, please note that 4.2 only has 2 significant figures, the trailing zeroes are irrelevant. You may find the definition of significant figures here: - en.wikipedia.org/wiki/Significant_figures Commented Jan 14 at 10:36
  • In any case, I believe the solution provided by @MateenUlhaq is much better, i.e.: '{:.3}'.format(4.2) will output '4.2' Commented Jan 14 at 10:45
  • @ionelberdin that is incorrect. I.e. from the article you linked: "Zeros to the right of the last non-zero digit (trailing zeros) in a number with the decimal point are significant". The number 4.2 to three significant figures is 4.20.
    – Myridium
    Commented Jan 14 at 11:36

Not the answer you're looking for? Browse other questions tagged or ask your own question.