From 7b9774b9462d4db99d0c0dfbce37e08d35629a63 Mon Sep 17 00:00:00 2001 From: akr Date: Thu, 12 May 2022 07:49:46 +0000 Subject: [PATCH] [IMP] base: tools: Added json_round to round into json suitable representation X-original-commit: 415c06362c7ecea9472acf8da2ff75615eedb392 Part-of: odoo/odoo#92195 --- odoo/tools/float_utils.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/odoo/tools/float_utils.py b/odoo/tools/float_utils.py index 6528b5ade4b..9b0776d56a4 100644 --- a/odoo/tools/float_utils.py +++ b/odoo/tools/float_utils.py @@ -215,6 +215,35 @@ def float_split(value, precision_digits): return int(units), 0 return int(units), int(cents) +def json_float_round(value, precision_digits, rounding_method='HALF-UP'): + """Not suitable for float calculations! Similar to float_repr except that it + returns a float suitable for json dump + + This may be necessary to produce "exact" representations of rounded float + values during serialization, such as what is done by `json.dumps()`. + Unfortunately `json.dumps` does not allow any form of custom float representation, + nor any custom types, everything is serialized from the basic JSON types. + + :param int precision_digits: number of fractional digits to round to. + :param rounding_method: the rounding method used: 'HALF-UP', 'UP' or 'DOWN', + the first one rounding up to the closest number with the rule that + number>=0.5 is rounded up to 1, the second always rounding up and the + latest one always rounding down. + :return: a rounded float value that must not be used for calculations, but + is ready to be serialized in JSON with minimal chances of + representation errors. + """ + rounded_value = float_round(f, precision_digits=precision_digits, rounding_method=rounding_method) + rounded_repr = float_repr(rounded_value, precision_digits=precision_digits) + # As of Python 3.1, rounded_repr should be the shortest representation for our + # rounded float, so we create a new float whose repr is expected + # to be the same value, or a value that is semantically identical + # and will be used in the json serialization. + # e.g. if rounded_repr is '3.1750', the new float repr could be 3.175 + # but not 3.174999999999322452. + # Cfr. bpo-1580: https://bugs.python.org/issue1580 + return float(rounded_repr) + if __name__ == "__main__":