Change default float print format

Question:

I’ve some lists and more complex structures containing floats. When printing them, I see the floats with a lot of decimal digits, but when printing, I don’t need all of them.
So I would like to define a custom format (e.g. 2 or 3 decimals) when floats are printed.

I need to use floats and not Decimal. Also, I’m not allowed to truncate/round floats.

Is there a way to change the default behavior?

Asked By: AkiRoss

||

Answers:

No, because that would require modifying float.__str__(), but you aren’t allowed to monkeypatch C types. Use string interpolation or formatting instead.

>>> a = 0.1
>>> a
0.10000000000000001
>>> print a
0.1
>>> print "%0.3f" % a
0.100
>>>

From the Python docs, repr(a) would give 17 digits (as seen by just typing a at the interactive prompt, but str(a) (automatically performed when you print it) rounds to 12.

Edit: Most basic hack solution…
You have to use your own class though, so…yeah.

>>> class myfloat(float):
...     def __str__(self):
...             return "%0.3f" % self.real
>>> b = myfloat(0.1)
>>> print repr(b)
0.10000000000000001
>>> print b
0.100
>>>
Answered By: Nick T

You are not allowed to monkeypatch C types, like Ignacio said.

However, if you are terribly pressed in doing so and you know some C, you could go modify the Python interpreter source code yourself, then recompile it into a custom solution. Once I modified one of the standard behaviors for lists and it was only a moderate pain.

I suggest you find a better solution, such as just printing the floats with the "%0.2f" printf notation:

for item in mylist:
    print '%0.2f' % item,

or

print " ".join('%0.2f' % item for item in mylist)
Answered By: Donald Miner

Upgrade to Python 3.1. It doesn’t use more digits than necessary.

Python 3.1.2 (r312:79147, Apr 15 2010, 15:35:48) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1
0.1
Answered By: dan04

If you are using C language, you can either use #define
or "%*.*f" to do that, e.g.

printf("%*.*f",4,2,variable);
Answered By: Swapnil

I ran into this issue today, and I came up with a different solution. If you’re worried about what it looks like when printed, you can replace the stdout file object with a custom one that, when write() is called, searches for any things that look like floats, and replaces them with your own format for them.

class ProcessedFile(object):

    def __init__(self, parent, func):
        """Wraps 'parent', which should be a file-like object,
        so that calls to our write transforms the passed-in
        string with func, and then writes it with the parent."""
        self.parent = parent
        self.func = func

    def write(self, str):
        """Applies self.func to the passed in string and calls
        the parent to write the result."""
        return self.parent.write(self.func(str))

    def writelines(self, text):
        """Just calls the write() method multiple times."""
        for s in sequence_of_strings:
            self.write(s)

    def __getattr__(self, key):
        """Default to the parent for any other methods."""
        return getattr(self.parent, key)

if __name__ == "__main__":
    import re
    import sys

    #Define a function that recognises float-like strings, converts them
    #to floats, and then replaces them with 1.2e formatted strings.
    pattern = re.compile(r"bd+.d*b")
    def reformat_float(input):
        return re.subn(pattern, lambda match: ("{:1.2e}".format(float(match.group()))), input)[0]

    #Use this function with the above class to transform sys.stdout.
    #You could write a context manager for this.
    sys.stdout = ProcessedFile(sys.stdout, reformat_float)
    print -1.23456
    # -1.23e+00
    print [1.23456] * 6
    # [1.23e+00, 1.23e+00, 1.23e+00, 1.23e+00, 1.23e+00, 1.23e+00]
    print "The speed of light is  299792458.0 m/s."
    # The speed of light is  3.00e+08 m/s.
    sys.stdout = sys.stdout.parent
    print "Back to our normal formatting: 1.23456"
    # Back to our normal formatting: 1.23456

It’s no good if you’re just putting numbers into a string, but eventually you’ll probably want to write that string to some sort of file somewhere, and you may be able to wrap that file with the above object. Obviously there’s a bit of a performance overhead.

Fair warning: I haven’t tested this in Python 3, I have no idea if it would work.

Answered By: Widjet

This doesn’t answer the more general question of floats nested in other structures, but if you just need to print floats in lists or even array-like nested lists, consider using numpy.

e.g.,

import numpy as np
np.set_printoptions(precision=3, suppress=False)
list_ = [[1.5398, 2.456, 3.0], 
         [-8.397, 2.69, -2.0]]
print(np.array(list_))

gives

[[ 1.54   2.456  3.   ]
 [-8.397  2.69  -2.   ]]
Answered By: LHeng

I have just put together a way to achieve this, at least for sympy expressions, see this.
With the two functions below, replace every print(... with print_my(....
This seems to me exceedingly less contrived, easier to use, and versatile than most other solutions posted.

I suspect my round_expr can be easily adapted for non-sympy expressions, which is the missing link for a full answer.

def round_expr(expr, num_digits):
    """Round all sp.Float numerical values in an expression to 3 decimal digits"""
    return expr.xreplace({n.evalf() : n if isinstance(n, int) else sp.Float(n, num_digits) for n in expr.atoms(sp.Number)})

def print_my(*args, **kwargs):
    end_my = kwargs['end'] if 'end' in kwargs else 'n'
    sep_my = kwargs['sep'] if 'sep' in kwargs else ' '
    for arg in args:
        if (isinstance(arg, str)):
            print(arg, end=sep_my)
        else:
            print(round_expr(arg, 3), end=sep_my)
    print(end=end_my)
    return

For those working in pandas:

pd.options.display.float_format = "{:,.0f}".format

I know this is an "old" post, but when I quickly search the web to find this solution, I often get older "work-arounds". Apologies in advance if I’ve totally missed something.

The above code formats all floats as whole numbers with "thousands" separator. Other decimal places can be obtained from changing the format specification string.

Health warning – this can complicate debugging as all output (e.g., "print", "display" and "describe") will also output figures duly rounded. This can be problematic when some floats will display differently (i.e., rounded) when they are actually a different value.

Answered By: JMKõ