Last active
July 23, 2021 19:59
-
-
Save odony/5269a695545902e7e23e761e20a9ec8c to your computer and use it in GitHub Desktop.
Python 3 JSON serialization of floats with chosen precision
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import decimal | |
import odoo | |
from odoo.tools import float_utils | |
from decimal import Decimal | |
# Version 0: BROKEN because float_round does not return the float with the shortest | |
# representation, so it will serialize as 211.70000000000002. | |
d = {'foo': float_utils.float_round(211.7, 1)} | |
print(json.dumps(d)) | |
# {"foo": 211.70000000000002} | |
# Version 1: rely the Python 2.7/3.1 "spec" that says that native float __repr__(f) will | |
# always use the shortest representation that rounds back to f. By rounding first with | |
# float_repr we choose the desired precision, and hope for the best. | |
d = {'foo': float(float_utils.float_repr(211.7, 1))} | |
print(json.dumps(d)) | |
# {"foo": 211.7} | |
# Version 2: hold the float value in a Decimal instance to hook it up with a | |
# custom encoder, and then use a fake float type to serialize it as a string. | |
class number_str1(float): | |
def __init__(self, o): | |
self.o = o | |
def __repr__(self): | |
return str(self.o) | |
def __float__(self): | |
return self | |
class MyEncoder(json.JSONEncoder): | |
def default(self, o): | |
if isinstance(o, decimal.Decimal): | |
return number_str1(o) | |
return super().default(o) | |
d = {'foo': Decimal(float_utils.float_repr(211.7, 1))} | |
print(json.dumps(d, cls=MyEncoder)) | |
# {"foo": 211.7} | |
# Version 3: skip the Decimal wrapper and directly use a fake float type that | |
# holds an exact string representation. | |
class float_string(float): | |
def __init__(self, fstr): | |
self.o = fstr | |
def __repr__(self): | |
return self.o | |
def __float__(self): | |
return self | |
d = {'foo': float_string(float_utils.float_repr(211.7, 1))} | |
print(json.dumps(d)) | |
# {"foo": 211.7} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
My decision for float format without changing objects which we pass to the function.
works for Python3
https://gist.github.com/Sukonnik-Illia/ed9b2bec1821cad437d1b8adb17406a3