Format floats with standard json module Format floats with standard json module python python

Format floats with standard json module


Note: This does not work in any recent version of Python.

Unfortunately, I believe you have to do this by monkey-patching (which, to my opinion, indicates a design defect in the standard library json package). E.g., this code:

import jsonfrom json import encoderencoder.FLOAT_REPR = lambda o: format(o, '.2f')    print(json.dumps(23.67))print(json.dumps([23.67, 23.97, 23.87]))

emits:

23.67[23.67, 23.97, 23.87]

as you desire. Obviously, there should be an architected way to override FLOAT_REPR so that EVERY representation of a float is under your control if you wish it to be; but unfortunately that's not how the json package was designed:-(.


import simplejson    class PrettyFloat(float):    def __repr__(self):        return '%.15g' % self    def pretty_floats(obj):    if isinstance(obj, float):        return PrettyFloat(obj)    elif isinstance(obj, dict):        return dict((k, pretty_floats(v)) for k, v in obj.items())    elif isinstance(obj, (list, tuple)):        return list(map(pretty_floats, obj))    return obj    print(simplejson.dumps(pretty_floats([23.67, 23.97, 23.87])))

emits

[23.67, 23.97, 23.87]

No monkeypatching necessary.


If you're using Python 2.7, a simple solution is to simply round your floats explicitly to the desired precision.

>>> sys.version'2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)]'>>> json.dumps(1.0/3.0)'0.3333333333333333'>>> json.dumps(round(1.0/3.0, 2))'0.33'

This works because Python 2.7 made float rounding more consistent. Unfortunately this does not work in Python 2.6:

>>> sys.version'2.6.6 (r266:84292, Dec 27 2010, 00:02:40) \n[GCC 4.4.5]'>>> json.dumps(round(1.0/3.0, 2))'0.33000000000000002'

The solutions mentioned above are workarounds for 2.6, but none are entirely adequate. Monkey patching json.encoder.FLOAT_REPR does not work if your Python runtime uses a C version of the JSON module. The PrettyFloat class in Tom Wuttke's answer works, but only if %g encoding works globally for your application. The %.15g is a bit magic, it works because float precision is 17 significant digits and %g does not print trailing zeroes.

I spent some time trying to make a PrettyFloat that allowed customization of precision for each number. Ie, a syntax like

>>> json.dumps(PrettyFloat(1.0 / 3.0, 4))'0.3333'

It's not easy to get this right. Inheriting from float is awkward. Inheriting from Object and using a JSONEncoder subclass with its own default() method should work, except the json module seems to assume all custom types should be serialized as strings. Ie: you end up with the Javascript string "0.33" in the output, not the number 0.33. There may be a way yet to make this work, but it's harder than it looks.