I am using Pydantic-based Response Model with a Decimal in FastAPI-based app. I want to return a response with precision up to 20 decimal values. But after serialization, it rounds off the decimal of 15 digits to 14 digits.
For example:
Original Decimal : 328.448267489936666
Decimal in Response : 328.44826748993665
I have tried setting getcontext.prec = 15, but it has no effect on it.
Minimal reproducible Example :
from decimal import getcontent, Decimal
from fastapi import FastAPI
from typing import Dict
app = FastAPI()
@app.get("/")
def read_root()->Dict[str, Decimal]:
getcontext().prec = 15
return {"a": Decimal(328.448267489936666)}
Dependencies : fastapi==0.92.0
uvicorn==0.20.0
pydantic==1.10.5
Python 3.7
Some precision loss happens with Decimal(328.448267489936666) since you're converting a float to a Decimal – convert a string to a Decimal with Decimal("328.448267489936666") so you have a precise Decimal to begin with, and then:
import decimal
from fastapi import FastAPI
from typing import Dict
app = FastAPI()
@app.get("/")
def read_root() -> Dict[str, decimal.Decimal]:
decimal.getcontext().prec = 15
return {"a": decimal.Decimal("328.448267489936666")}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=11111)
~ $ curl http://localhost:11111
{"a":"328.448267489936666"}
Note how the Decimal is a string in the JSON; that's because many JSON decoders just use floats for number content (at least by default), and that can't be trusted to be precise either.
The serialization behavior actually seems to depend on the version of Pydantic in use; Pydantic 2.x encodes decimals as strings, while Pydantic 1 doesn't.
You can patch the encoder dict in FastAPI to fix this though; do something like this early enough in your app (or in a startup hook):
from fastapi import encoders
encoders.ENCODERS_BY_TYPE[decimal.Decimal] = str
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With