pydantic-core icon indicating copy to clipboard operation
pydantic-core copied to clipboard

Parsing Decimal from JSON number loses precision

Open kolditz-senec opened this issue 8 months ago • 1 comments

When parsing a decimal from a JSON number, pydantic-core (2.33.1) is losing precision. It seems like the JSON number is converted to a float internally before being passed to Decimal. This problem has been brought up already in the pydantic repo (https://github.com/pydantic/pydantic/issues/9180), but I think it belongs here.

Code to reproduce with pydantic-core:

from decimal import Decimal
from pydantic_core import SchemaValidator

def test_parse_decimal():
    v = SchemaValidator(
        {
            'type': 'typed-dict',
            'fields': {
                'value': {
                    'type': 'typed-dict-field',
                    'schema': {
                        'type': 'decimal',
                    },
                },
            },
        }
    )
    r1 = v.validate_json(b'{"value": 0.29999999999999998}')
    assert r1["value"] == Decimal("0.29999999999999998")

AssertionError: assert Decimal('0.3') == Decimal('0.29999999999999998')

Since the JSON standard doesn't specify limits on range and precision for numbers, parsing them into Decimals without loss of precision is a valid use case. So I assume this is a bug.

kolditz-senec avatar Apr 10 '25 06:04 kolditz-senec

I'm seeing a similar issue with UUIDs loaded from JSON strings. I've found that calling model_validate on the return from model_validate_json will properly coerce the UUID strings to UUID types. Not sure if that will help you in the Decimal case. But it seems the issue is across types.

mattwwarren avatar Jun 10 '25 17:06 mattwwarren

@kolditz-senec I understand the frustration with losing decimal precision, but this isn't actually a bug in pydantic-core. The precision loss happens at the JSON parsing layer before pydantic-core even gets the value.

pydantic-core uses jiter for JSON parsing, and jiter converts JSON numbers to floats during parsing. When you write {"value": 0.29999999999999998} as a JSON number, the precision is already lost due to floating-point representation before pydantic-core receives it. To fix this, we'd need jiter to parse numbers as strings or have special precision-preserving mode, which would be a significant architectural change.

The workaround is using JSON strings: {"value": "0.29999999999999998"}. This bypasses float conversion and preserves exact precision. That's also why pydantic serializes Decimals as strings by default.

PrettyWood avatar Aug 04 '25 06:08 PrettyWood