0

How to convert Decimal("123456789012345.99") to json number 123456789012345.99?

Python 3.11 win64

import json
from decimal import Decimal

class DecimalToFloat(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Decimal):
            return float(obj)
        return json.JSONEncoder.default(self, obj)

class DecimalToStr(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Decimal):
            return str(obj)
        return json.JSONEncoder.default(self, obj)

obj = {'decimal':Decimal("123456789012345.99")}

# {"decimal": 123456789012345.98} - WRONG!!!!
print(json.dumps(obj, cls=DecimalToFloat)) 

# {"decimal": "123456789012345.99"} - json string
print(json.dumps(obj, cls=DecimalToStr)) 

# ??? {"decimal": 123456789012345.99}

UPD

simplejson module is OK

# {"decimal": 123456789012345.99}
print(simplejson.dumps(obj, use_decimal=True)) 

Is there a way to do the same with the standard library without external dependencies?

dimmaq
  • 1
  • 1
  • You need to clarify what your assumptions are regarding where this JSON will end up. Does the recipeint have Python with Decimal? Does the recipient have access to an arbitrary precision library like Decimal? Since JSON does not natively support the concept of Decimal in the same way Python does, you need to articulate the differences. Or, use a Python native serialization type. – dawg Aug 01 '23 at 11:06

2 Answers2

0

The issue you're facing is due to the limitations of the float representation in Python, which uses double-precision floating-point numbers. The Decimal class, on the other hand, provides arbitrary-precision decimal arithmetic, which can handle large numbers and fixed decimal places accurately.

When you convert Decimal("123456789012345.99") to a float, the float representation cannot precisely represent the decimal number, leading to a small loss of precision. As a result, the float representation becomes 123456789012345.98.

To maintain the original decimal precision, you can use a custom JSON encoder that converts Decimal objects to strings:

import json
from decimal import Decimal

class DecimalToStr(json.JSONEncoder):
def default(self, obj):
    if isinstance(obj, Decimal):
        return str(obj)
    return super().default(obj)

obj = {'decimal': Decimal("123456789012345.99")}

# Output: {"decimal": "123456789012345.99"}
print(json.dumps(obj, cls=DecimalToStr))

By using the DecimalToStr JSON encoder, the Decimal object will be converted to its string representation, preserving the original decimal precision in the JSON output.

Keep in mind that when you use DecimalToStr, the decimal value will be represented as a string in the JSON output. If you need to use the number as a float in the JSON (e.g., for interoperability with other systems), you will encounter the inherent limitations of float representation, as shown in your initial code. In such cases, it's generally better to keep the decimal as a string to avoid precision issues.

-1
import json
from decimal import Decimal


d = Decimal("123456789012345.99")

s = str(d)

json_obj = {"number": s}

json_str = json.dumps(json_obj)

print(json_str) # outputs: {"number": "123456789012345.99"}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
  • Answer needs supporting information Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](https://stackoverflow.com/help/how-to-answer). – moken Aug 01 '23 at 12:27
  • Good try at answering but this is not what the OP is after. How would this scale to a complex dict with nested values for example? – Peter Aug 01 '23 at 13:20