4

I realise that this question could be construed as similar to others, so before I start, here is a list of some possible "duplicates" before everyone starts pointing them out. None of these seem to really answer my question properly.

My question specifically pertains to the use of the string.format() method for displaying integer numbers.

Running the following code using % string formatting in the interpreter running python 2.7

    >>> print "%d" %(1.2345)  
    1

Whereas using the string.format() method results in the following

    >>> print "{:d}".format(1.2345)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: Unknown format code 'd' for object type 'float'

I was expecting the same behavior in both; for the interpreter to actually convert my floating point number to an integer prior to displaying. I realise that I could just use the int function to convert the floating point number to integer format, but I was looking for the same functionality you get with the %d formatting method. Is there any string.format() method that would do this for me?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Mike
  • 439
  • 1
  • 6
  • 13
  • What's the reason for not just doing the conversion explicitly yourself, if you don't know if your variable is an integer or not? e.g. `print "{:d}".format(int(1.2345))` – Tom Dalton Oct 03 '14 at 09:39
  • @Tom, as I said I could just do the conversion myself and all my troubles would go away. But what I really wanted to know was why two methods that at first glance looked very similar, didn't do exactly the same thing. – Mike Oct 03 '14 at 09:45

2 Answers2

12

The two implementations are quite separate, and some warts in the % implementation were ironed out. Using %d for floats may mask problems in your code, where you thought you had integers but got floating point values instead. Imagine a value of 1.999999 and only seeing 1 instead of 2 as %d truncates the value.

As such, the float.__format__() hook method called by str.format() to do the actual conversion work does not support the d format and throws an exception instead.

You can use the {:.0f} format to explicitly display (rounded) floating point values with no decimal numbers:

>>> '{:.0f}'.format(1.234)
'1'
>>> '{:.0f}'.format(1.534)
'2'

or use int() before formatting to explicitly truncate your floating point number.

As a side note, if all you are doing is formatting a number as a string (and not interpolating into a larger string), use the format() function:

>>> format(1.234, '.0f')
'1'

This communicates your intent better and is a little faster to boot.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

There is an important change between 2.7 and 3.0 regarding "automatic type conversion" (coercion). While 2.7 was somehow relatively "relax" regarding this, 3.0 forces you to be more disciplined.

Automatic conversion may be dangerous, as it may silently truncate/reduce some data ! Besides, this behavior is inconsistent and you never know what to expect; until you're faced with he problem. Python 3.0 requires that you specify what you want to, precisely, do !

However, the new string.format() adds some very powerful and useful formatting techniques. It's even very clear with the "free" format '{}'. Like this :

'{}'.format(234)
'{:10}.format(234)
'{:<10}'.format(234)

See ? I didn't need to specify 'integer', 'float' or anything else. This will work for any type of values.

for v in (234, 1.234, 'toto'):
    for fmt in ('[{}]', '[{:10}]', '[{:<10d}]', '[{:>10d}]'):
        print(fmt.format(v))

Besides, the % value is obsolete and should not be used any more. The new string.format() is easier to use and has more features than the old formatting techniques. Which, IMHO, renders the old technique less attractive.

dcexcal
  • 197
  • 7