为什么要在JSON中使用字符串来表示十进制数

时间:2016-02-29 21:03:08

标签: json serialization paypal

某些API(如paypal API)使用JSON中的字符串类型来表示十进制数。所以"7.47"代替7.47

为什么/何时使用json数值类型是个好主意? AFAIK数值类型允许无限精度和科学记数法。

4 个答案:

答案 0 :(得分:38)

将JSON中的数值作为字符串传输的主要原因是为了消除传输中的任何精度损失或模糊性。

JSON规范确实没有为数值指定精度。这并不意味着JSON数字具有无限精度。这意味着未指定数字精度,这意味着JSON实现可以自由选择任何方便其实现或目标的数字精度。如果您的应用程序具有特定的精度要求,那么这种可变性可能会很痛苦。

精度损失通常在数值的JSON编码中不明显(1.7很好且简洁),但在接收端的JSON解析和中间表示中表现出来。 JSON解析函数可以合理地将1.7解析为IEEE双精度浮点数。但是,有限长度/有限精度表示总是会遇到无法精确表示的数字。无理数(如pi和e)永远不能在有限系统中准确表示。 1.7具有十进制(基数10)表示法的有限表示,但在二进制(基数2)中,它是无理数 - 无限数字系列。

因此,将1.7解析为内存中的浮点数,然后打印出数字可能会返回1.69 - 而不是1.7。

JSON 1.7值的消费者可以使用更复杂的技术来解析和保留内存中的值,例如使用定点数据类型或具有任意精度的“string int”数据类型,但这不会完全消除某些数字的转换精度损失的幽灵。实际情况是,很少有JSON解析器为这种极端措施而烦恼,因为大多数情况下的好处很少,而且内存和CPU成本很高。

因此,如果您想要向消费者发送精确的数值并且您不希望将值自动转换为典型的内部数字表示,那么您最好的选择是将数值作为字符串发送出去并告诉如果需要对数字操作执行数字操作,那么消费者应该如何处理该字符串。

例如:在一些JSON生成器(JRuby,一个)中,BigInteger值自动输出为JSON作为字符串,主要是因为BigInteger的范围和精度比IEEE双精度浮点数大得多。将BigInteger值减小为两倍以便作为JSON数字输出通常会丢失有效数字。

此外,JSON规范(http://www.json.org/)明确指出NaN和无穷大(INF)对JSON数值无效。如果需要表达这些边缘元素,则不能使用JSON编号。您必须使用字符串或对象结构。

最后,还有另一个方面可以导致选择将数字数据作为字符串发送:控制显示格式。前导零和尾随零对数值无关紧要。如果您发送JSON数值2.10或004,转换为内部数字形式后,它们将显示为2.1和4.

如果您要发送的数据将直接显示给用户,您可能希望您的资金数字在屏幕上很好地排列,十进制对齐。一种方法是让客户端负责格式化数据以便显示。另一种方法是让服务器格式化数据以供显示。客户端可能更容易在屏幕上显示内容,但如果客户端还需要对值进行计算,这会使得从字符串中提取数值变得困难。

答案 1 :(得分:10)

我会有点相反,说 7.47在JSON中是绝对安全的,即使是财务金额也是如此,并且"7.47"并不更安全。 / p>


首先,让我解决这个线程的一些误解:

因此,将1.7解析为内存中的浮点数,然后输出该数字可能会返回1.69-而不是1.7。

那是不正确的,尤其是在该答案中提到的IEEE 754双精度格式的情况下。 1.7转换为精确的双精度1.69999999999999999999555910790149149383830547547763763875,当该值被“打印”以显示时,总是为1.7,从不为1.69、1.699999999999或1.70000000001。完全是1.7。

了解更多here

7.47转换为float时实际上可能是7.4699999923423423423

7.47已经是一个浮点数,其精确值是7.46999999999999975131004248396493494510650634765625。不会将其“转换”为任何其他浮点数。

一个简单的系统会简单地将多余的数字截断,结果为7.46,而现在您在某个地方已经损失了一分钱

IEEE舍入而不是截断。而且它不会转换为7.47以外的任何其他数字。

JSON编号实际上是浮点数吗?据我了解,这是一个与语言无关的数字,如果愿意,您可以将JSON数字直接解析为Java BigDecimal或任何语言的其他任意精度格式。

recommended会将JSON数字解释为双精度(IEEE 754双精度格式)。我还没有看到不会这样做的解析器。

不,BigDecimal(7.47)不是正确的方法,它实际上会创建一个BigDecimal,它表示7.47的确切 double ,即7.46999999999999975131004248396493494510650634765625。要获得预期的行为,请BigDecimal("7.47") should be used


总体而言,我认为{"price": 7.47}没有任何根本性的问题。实际上,它将在所有平台上都转换为double,并且IEEE 754的语义保证将始终准确地将其“打印”为7.47。

当然,在使用该值进行进一步的计算时,可能会发生浮点舍入错误,请参见例如0.1 + 0.2 == 0.30000000000000004,但我看不到JSON中的字符串如何使它变得更好。如果“ 7.47”以字符串形式到达并且应作为某些计算的一部分,则无论如何都需要将其转换为某种数字数据类型,可能是float:)。

值得注意的是,字符串也有缺点,例如,不能将它们传递给Intl.NumberFormat,它们不是“纯”数据类型,例如,点是格式决定。

我并不强烈反对字符串,它们在我看来也不错,但我也没有发现{"price": 7.47}上有什么问题。

答案 2 :(得分:3)

汇总版

引用@dthorpe的回答,因为我认为这是最重要的一点:

  

此外,JSON规范(http://www.json.org/)明确指出NaN和无穷大(INF)对JSON数值无效。如果需要表达这些边缘元素,则不能使用JSON编号。您必须使用字符串或对象结构。

答案 3 :(得分:0)

我这样做的原因是SoftwareAG解析器试图从其接收的值中“猜测” java类型。

所以当它收到时

"jackpot":{
 "growth":200,
 "percentage":66.67
}

第一个值(增长)将变为java.lang.Long,第二个值(百分比)将变为java.lang.Double

现在,当此累积奖金数组中的第二个对象具有此对象

"jackpot":{
 "growth":50.50,
 "percentage":65
}

我有问题。

当我将这些值作为字符串交换时,我拥有完全的控制权,可以将值转换/转换为所需的任何值。