6

While working with float precision, I stumbled across a strange fact. Why does python prints only the integer part when formatted with "%.f". I am willing to know the mechanism behind this

 >>> a = float(2.12345)
 >>> a
 2.12345
 >>> print "%.2f" % a
 2.12
 >>> print "%.1f" % a
 2.1
 >>> print "%f" % a
 2.123450
 >>> print "%.f" % a
 2                   #why?

Thanks in advance for the explanation :)

3
  • 1
    Because "%.f" is the same as "%.0f"? Commented Apr 13, 2016 at 12:35
  • 1
    For the same reason that 1.0 is the same as 1., probably. Commented Apr 13, 2016 at 12:36
  • This appears to be undefined behaviour. The optional precision is given as a '.' (dot) followed by the precision. The format specification mini-language documentation similarly requires an argument for the precision. Since int() returns 0 it seems like a reasonable default, but undefined behaviour means exactly that. Commented Apr 13, 2016 at 12:41

3 Answers 3

4

It's been that way ever since % formatting was added back in 1993; if a . is not followed by a decimal number then precision is taken as zero.

This is undocumented, but is consistent with printf, which Python's % formatting was inspired by:

(optional) . followed by integer number or *, or neither that specifies precision of the conversion. In the case when * is used, the precision is specified by an additional argument of type int. If the value of this argument is negative, it is ignored. If neither a number nor * is used, the precision is taken as zero.

Interestingly, another undocumented feature also inspired by printf is that you can use * as precision, as above:

>>> "%6.*f" % (2, 1.234)
'  1.23'
Sign up to request clarification or add additional context in comments.

3 Comments

your answer seems logical to me. But I wonder why the string format raises value error as mentioned by srowland in his answer?
@HiteshPaul string.format is a later API (introduced in PEP 3101) so it was possible to have it conform strictly to the specification; the undocumented behavior of % couldn't be removed without breaking backward compatibility.
@HiteshPaul See my answer below :)
1

The docs for precision here don't mention a default if the precision is ommitted. I can only assume it just works this way because it does!

The docs give the default precision for a %f as 6 in the format specification mini language here. Maybe by specifying a precision with the . and then by omitting an integer value, the interpreter assumes it should be zero?

This may even behave differently on different interpreters. Interesting find anyway :).

Interestingly, using str.format throws a nice ValueError in my 2.7 interpreter:

>>> f = 234.12345676
>>> "{:.f}".format(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Format specifier missing precision

5 Comments

It seems weird though, python raises ValueError for string format and none for '%' format.
Completely agree with you :). I guess format has a more rigorous implementation on this point.
so does that mean the internal implementation differs for string format and % format?
I don't think either result is more "rigorous". As I said in a comment on the question, this is undefined behaviour. You might get a default of zero decimal points, you might get an exception, the Python interpreter might crash, or your machine might reboot. Some options seem better choices, but all are valid.
Thanks @Chris - link fixed. And I agree this is just undefined. It is probably a little more user friendly that format gives a ValueError in my interpreter whereas "%.f" doesn't.
1

The % operator has the following behavior, like you observed:

>>> "%.f" % 1.23
'1'

The parser goes through the format string, with the precision being undefined (-1) by default. When it hits the ., the precision will be set to 0. The arguments will be passed to the helper function formatfloat which uses the default precision 6 if no precision is given, and no . is used.

An interesting note is that str.format() will actually throw an exception in this case, probably for easier implementation and not letting people rely on unspecified behavior:

>>> "{:.f}".format(1.23)
Traceback (most recent call last):
  File "<ipython-input-6-677ba2e4a680>", line 1, in <module>
    "{:.f}".format(1.23)
ValueError: Format specifier missing precision

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.