处理货币价值,将一个数字除以 100 是否安全?

Cri*_*ìna 8 javascript ieee ieee-754

在存储库代码中,在另一个团队开发的模块中,我发现有一个价格从美分到欧元的转换,只需将数字除以 100。

该代码使用 Javascript,因此它使用IEEE 754标准。

我知道将货币值作为浮点数处理是不安全的,但在将任务发送给其他团队之前,我想知道这种情况是否安全。

到目前为止,我没有发现任何将整数除以 100 得到不准确结果的情况。让我们更进一步:100只是2*2*5*5.

我们知道将数字除以2是安全的,因为它正好等于位置的移动。

所以我们可以很容易地说,如果存在一个不能被 5 整除的数,那么被 100 整除是不准确的。

我做了很多测试,但没有找到任何这些数字,但我离论文的理论论证还很远。

那么,在 IEEE 754 标准中将数字除以 100 安全吗?

Max*_*kin 8

具有 15 位有效数字精度的浮点十进制数转换为 64 位二进制浮点数(Number在 JavaScript 中)并返回十进制,而不会损失精度。尽管二进制数可能无法准确存储十进制数,但它具有更多位的精度(表示 53 位尾数至少需要 17 位十进制有效数字),并且可以精确地四舍五入到原始十进制数。这些额外的尾数二进制数字正是为了在 CPU 算术的所有结果中保持这 15 个有效十进制数字的精确性。有关完整详细信息,请参阅往返转换所需的位数。

除以 100 时,二进制结果仍然具有 53 位精度,并且在最小精度单位(尾数的最低位)中可能存在错误,除非结果下溢到 0(请参阅每个计算机科学家应该了解的浮点知识)完整细节的算术。)。该二进制数仍会通过四舍五入转换为精度在 15 位有效小数位以内的正确精确十进制数。

换句话说,如果十进制数的有效数字不超过 15 位,则将它们除以 100 即可保持该精度。

例如,尝试123456789012345 / 1000.000123456789012345 / 100浏览器控制台中(这两个数字都有 15 位有效十进制数字的精度) - 这些除法返回 15 位有效十进制数字内的正确十进制数字:

123456789012345 / 100
1234567890123.45

0.000123456789012345 / 100
0.00000123456789012345
Run Code Online (Sandbox Code Playgroud)


Eri*_*hil 5

如果x是 15 位十进制数字的整数,则将x转换为 JavaScript Number,除以 100,并将结果转换为具有 15 位有效十进制数字的数字,正好生成x /100。下面是一个证明。

\n

笔记:

\n
    \n
  • 将除法结果转换为具有 15 位有效小数位的数字,结果恰好是x /100。除法的实际结果虽然采用格式Number,但通常不会恰好是x /100。例如,73/100 产生 0.729999999999999982236431605997495353221893310546875。
  • \n
  • 将除法结果转换为超过15 位有效十进制数字通常也不会产生x /100,因为额外的数字可能会揭示差异,如上面的 0.73 所示。(当然,使用较少的数字可能不足以表示x /100。)因此,如果希望将x /100 准确地传达给另一个进程,则必须使用正好 15 个有效十进制数字(或其他一些缓解措施)来完成。错误)。
  • \n
  • 下面的证明适用于 15 位整数x,不适用于其他 15 位有效十进制数字(例如具有 15 位十进制数字后跟一个或多个零的数字或以小数点开头后跟一些零后跟 15 的数字)有效数字)。
  • \n
\n

预赛

\n

JavaScript 是 ECMAScript 的实现,在Ecma-262和 ISO/IEC 16262 中指定。在第 6.1.6 条中,Ecma-262 指定 ECMAScript\xe2\x80 使用 IEEE-754 基本 64 位二进制浮点格式\x99sNumber类型,只不过仅使用单个 NaN。第 6.1.6 节进一步描述了所使用的算术,其本质上是具有舍入到最接近、偶数关系的 IEEE-754 算术。

\n

IEEE-754 基本 64 位二进制浮点格式使用 53 位有效数。

\n

二进制浮点数的最低精度单位 (ULP) 是归因于其有效数中最低有效位的位置的值。(因此,ULP 随指数缩放。)在 ULP 中测量,所有正常的 53 位有效数都在 [2 52 ULP, 2 53 ULP) 中。

\n

对于15位有效位的十进制数,其ULP在此将是归属于从前导有效位开始倒数第15位的位置的值。

\n

引理

\n

首先,我们确定一个众所周知的事实:如果数字在格式的正常范围内,则将 15 位有效十进制数字转换回Number15 位有效十进制数字会产生原始数字Number

\n

如果x是浮点格式正常范围内的 15 位有效十进制数字(不一定是整数) (2 \xe2\x88\x921022 \xe2\x89\xa4 | x | < 2 1024 ),则转换将x转换为以浮点格式表示的最接近的值,然后将结果转换为 15 位有效十进制数字,当这两种转换均采用舍入到最接近、偶数关系的方式执行时,将精确生成x 。为了看到这一点,让y为第一次转换的结果。如果y与x的差异小于x的 ULP 的 \xc2\xbd ,则x是最接近y的 15 位有效数字,因此必须是第二次转换的结果。

\n

在第一次转换中,由于舍入规则,结果y至多是x的 \xc2\xbd ULP。该相对精度最多为 \xc2\xbd / 2 52(即潜在的 \xc2\xbd ULP 误差除以最小有效数,以 ULP 测量)。因此,yx至多相差2 53份。在最坏的情况下, x的位数可能为 9999999999999 = 10 15 \xe2\x88\x921 ,因此x相对于 ULP 的误差将为 (10 15 \xe2\x88\x921)/2 53,约为 。x的 ULP 的 111 倍。因此,y与x的差异始终小于其 ULP 的 \xc2\xbd ,因此将y转换回 15 位有效十进制数字会产生x

\n

证明

\n

如果x是 15 位十进制数字的整数,则它可以在该Number格式中精确表示,因为该Number格式的有效数有 53 位,因此能够精确表示最大 2 53的所有整数,即大约 9.007e15,即超过 10 15 .

\n

因此,将x转换为Number恰好产生x,不会出现错误。

\n

然后,根据舍入算术结果的规则,将x除以 100 得到最接近x /100 的可表示数。将此称为y。现在请注意,x /100 是可以用 15 位有效十进制数字表示的数字。(它可以用科学记数法写为x \xe2\x80\xa210 \xe2\x88\x922或在源代码中写为x后缀的数字e-2。)请注意,将x /100 转换为Number也会产生y,因为转换产生,就像除法一样,可以用Number最接近x /100 的格式精确表示的数字。根据引理,将x /100 转换为Number15 位有效小数位数字并返回为x /100,因此将x转换为 的结果,然后除以Number100,然后转换为 15 位有效小数位数字也产生x /100。

\n