diff --git a/fastapi/encoders.py b/fastapi/encoders.py index c9f882d2b..04859f85b 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -73,12 +73,20 @@ def decimal_encoder(dec_value: Decimal) -> int | float: >>> decimal_encoder(Decimal("NaN")) nan + + >>> decimal_encoder(Decimal("sNaN")) + nan """ exponent = dec_value.as_tuple().exponent if isinstance(exponent, int) and exponent >= 0: return int(dec_value) else: - return float(dec_value) + try: + return float(dec_value) + except ValueError: + # Signaling NaN (sNaN) cannot be converted to float directly. + # Treat it as a quiet NaN, consistent with Decimal("NaN") behaviour. + return float("nan") ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = { diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 8f8bd3fcb..e7efa6512 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -299,6 +299,14 @@ def test_decimal_encoder_infinity(): assert isinf(jsonable_encoder(data)["value"]) +def test_decimal_encoder_signaling_nan(): + # Decimal("sNaN") (signaling NaN) previously raised ValueError when + # decimal_encoder called float() on it. It should now return float("nan") + # consistently with Decimal("NaN") behaviour. + data = {"value": Decimal("sNaN")} + assert isnan(jsonable_encoder(data)["value"]) + + def test_encode_deque_encodes_child_models(): class Model(BaseModel): test: str