thanks for reading this , I've been struggled for a while I'm using Aeson/TH to auto generate the fromJSON/toJSON
of a ADT. I'm using Yesod
to accept data from HTTP call .
import Data.Aeson hiding (json)
import Language.Haskell.TH
import Data.Aeson.TH
import Data.Aeson.Types
data Money = USD Float
$(deriveJSON defaultOptions ''Money)
.... -- getting request from HTTP
_money <- requireCheckJsonBody :: Handler Money
The issue is that , if I pass a large nubmer in Money
like USD 1157265240.03
, but the _money
will have float value of 1,157,265,300.0
which rounds 240
to 300
.
this looks like caused by a conversion from String alike Scientific notation
to a Float which will trancate the tail part of number.
Any idea how to bypass such conversion which cause loss of precision ? Thanks
CodePudding user response:
The lack of precision has nothing to do with yesod or aeson, but instead with your choice of Float
as a datatype. Type 1157265240.03 :: Float
at the GHCi prompt, and you'll get 1.1572653e9
back. IEEE single-precision floats can't represent amounts more accurately than that once the magnitude gets that high. (Specifically, the only number between 1,157,265,152.00 and 1,157,265,408.00 that a single-precision float can hold is 1,157,265,280.00.) You need a different datatype. Aeson internally stores JSON numbers using the Scientific
datatype, which is arbitrary precision. Another reasonable choice would be Centi
from Data.Fixed
.