如何使用Aeson在解码JSON时区分负零与非负零?
Aeson解码JSON负零丢失符号位的原因与解决方法
Haskell原生支持区分浮点数的正负零:
ghci> (isNegativeZero (0 :: Float), isNegativeZero (-0 :: Float)) (False,True)
JSON语法层面也允许0和-0作为合法数值,但使用Aeson解码-0时,会丢失负零的符号位:
ghci> isNegativeZero <$> eitherDecode "-0" Right False
原因
Aeson默认的浮点数解码逻辑,是将JSON的数值字符串转换为Haskell的Float/Double类型。底层解析过程中,会把"-0"直接解析为数值0.0,没有保留IEEE 754浮点数负零的符号位信息——常规的数值转换逻辑默认将正负零视为等价,因此最终得到的浮点数是正零。
解决方法
要在解码时区分正负零,需要自定义JSON解码器,先读取JSON的原始数值字符串,判断是否为"-0",再手动构造负零:
import Data.Aeson import Data.Aeson.Types import Data.Float (negativeZero) import Numeric (formatScientific, Generic) instance FromJSON Float where parseJSON = withScientific "Float" $ \sci -> case formatScientific Generic Nothing sci of "-0" -> pure negativeZero numStr -> case reads numStr of [(f, "")] -> pure f _ -> fail $ "无法解析为Float: " ++ numStr
这个自定义实例会先把科学计数法表示的数值转回字符串形式,若字符串是"-0",就返回Haskell的负零值;其他情况按常规方式解析为浮点数,这样就能保留负零的符号位了。
内容的提问来源于stack exchange,提问作者Janus Troelsen
相关产品推荐
相关产品推荐

