29

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?

Ingo
  • 1,732
  • 10
  • 26
  • 34
  • 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 Feb 23 '12 at 15:46
  • @Wooble [This](http://stackoverflow.com/a/6163157/566644) very nice answer delves into the question you're raising. It's worth a look. – Lauritz V. Thaulow Feb 23 '12 at 16:02
  • But there is no more precise data type than float in Python, is there? – Ingo Feb 24 '12 at 10:55

6 Answers6

50

Let:

>>> num = 0.0012345

For 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.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
  • 6
    This should be marked as the correct answer. See [Wikipedia](https://en.wikipedia.org/wiki/Significant_figures) – wizclown Mar 15 '19 at 10:32
  • 5
    This displays numbers in scientific format, e.g. 12.0 => "1.2e+01", which may not be what people expect. – quantoid Jun 03 '19 at 00: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. – Mateen Ulhaq May 04 '20 at 03:14
  • In any case, I think most people want to print numbers s.t. `abs(float(f'{num:.{d}}')) < 10**d` – Mateen Ulhaq May 04 '20 at 03:15
19

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.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • 8
    `print('{0:.15f}'.format(val))` is also another way to do it in the new format. – TyrantWave Feb 23 '12 at 16:07
  • 20
    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 Jul 01 '16 at 09:18
  • Yeah, this is decimal places not significant figures. – quantoid Jun 03 '19 at 00:02
12

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
GENETAY
  • 3
  • 2
Bijou Smith
  • 149
  • 1
  • 6
10

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.

quantoid
  • 530
  • 5
  • 16
  • 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. – BallpointBen Apr 04 '20 at 06:38
4

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.

Matt Alcock
  • 12,399
  • 14
  • 45
  • 61
  • 1
    I downvoted because this gives 15 digits after the decimal point, not 15 [significant digits](https://en.wikipedia.org/wiki/Significant_figures). I deem [this answer](https://stackoverflow.com/a/52899472/2980080) to be correct. – wizclown Mar 15 '19 at 10:36
1

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 ;)

  • Hmm, this kept spitting out numbers in exponent notation. Perhaps because I converted them to back to strings later?, i.e. str(float(...)) – DrMisha Apr 28 '15 at 16:35