如何在R中正确纠正模数误差?

4
核心R引擎在表达模数运算输出方面存在严重缺陷: ceiling((1.99 %% 1) * 100) 返回结果为99(正确)。 ceiling((2.99 %% 1) * 100) 返回结果为100(不正确)。 该行为将在任何整数值N + 2.99(例如3.99等)中显露出来。如果这与浮点表示有关,则系统未表达差异的全部细节。这尤其令人不安,因为: (1.99 %% 1)(2.99 %% 1)均“看起来”返回0.99。 ((1.99 %% 1) * 100)((2.99 %% 1) * 100)均“看起来”返回99。
但是,如果您进行任何舍入或类似的数学运算,2.99的“不可见”剩余值会以意外的方式翻转事情。
虽然对于我当前的应用程序解决此问题很容易: floor((2.99 - floor(2.99)) * 100) 返回结果为99(正确)。 sprintf("%.22f", floor((2.99 - floor(2.99)) * 100)) 返回结果为99.0000000000000000000000(正确)。 ...但我想知道还有多少其他情况,模数返回错误值而没有底层细节显示浮点差异。是否有一种方式可以公开模数似乎附加的底层剩余值?否则它是不可见的。
编辑:根据andrew.punnett的慷慨示例,print(1.99, digits = 22)返回1.99(无浮点扩展),而print(1.99 %% 1, digits = 22)返回0.98999999999999999。根据Aaron的敏锐眼力,这似乎是版本和/或系统相关的。
谢谢!

1
你好,欢迎来到StackOverflow!我担心你可能会有一个糟糕的第一次体验,因此建议你对问题进行一些编辑,以避免一些问题,并帮助你获得更好的答案。1)有些人会认为你对浮点数错误一无所知,并表现得很傲慢;因此,我建议你编辑一下,包括这可能是一个潜在原因,并想知道如何更好地处理它。2)有些人会对指出“错误/缺陷”感到不满,这也可能指向浮点问题;我建议你重新措辞,将其改为提到“行为”。最好的祝愿! - Aaron left Stack Overflow
我感谢您的反馈,我已经进行了一些编辑。如果这个问题与值的浮点表示有关,则引擎不会用完整的数字集表达它--它只显示0.99,就是这样。换句话说,(2.99 %% 1)和(2.99 - floor(2.99))应该返回相同的结果。它们似乎返回相同的结果--引擎没有显示任何差异。但实际返回的数字是不同的,而R不会扩展浮点数以显示这种差异。 - TuringTester1912
1
似乎还存在一些特定于系统的问题;对于print(1.99, digits = 22),我得到了1.989999999999999991118 - Aaron left Stack Overflow
1
可能感兴趣的内容:将非整数十进制数转换为二进制 - Aaron left Stack Overflow
2
谢谢Aaron!我真的很惊讶(哈哈),在不同版本中1.99的扩展有所不同,这真的很奇怪。但是非常感谢提供二进制表示链接!我可能会在不久的将来使用它。 - TuringTester1912
1
为什么这些数字不相等? - kangaroo_cliff
1个回答

6

这并不是 R 的一个 bug,而是 浮点运算标准 IEEE 754 的属性。

问题出现的原因是因为 1.99 或者 2.99 都不能被精确地表示成一个浮点数。在双精度(64位)浮点数中,最接近 2.99 的十进制数是 2.99000000000000021316282072803(可以在此进行转换

因此,表达式的计算结果为:

ceiling((2.99 %% 1) * 100) = ceiling(99.000000000000021316282072803)
                           = 100

相比之下,1.99的最接近表示是1.989999999999999991118215803,这恰好得出了您期望的答案:

ceiling((1.99 %% 1) * 100) = ceiling(98.9999999999999991118215803)
                           = 99

在IEEE 754浮点算术中,两个结果都是正确的,但正如您所看到的,只有一个符合实数算术规则的结果。

这个问题被加剧了,在R中默认行为是截断您print()的每个浮点数。如果您想要看到更多数字,则必须提供digits参数:

print(1.99, digits = 22)

然而,即使如此,在所有平台上都无法获得正确的数字位数,因此更可靠的方法是准确查看浮点数:

cat(sprintf("%.22f\n", 1.99))

2
我认为@andrew.punnett所说的是,R确实返回了那个值;只是它没有显示出来。 - Aaron left Stack Overflow
1
我也觉得那真的很烦人。事实上,如果你尝试使用命令 print(1.99, digits = 22),你会发现 R 坚持认为这个数字是真正的 1.99。我不确定为什么。 - andypea
3
PS. 很好的回答,清晰明了但不傲慢。:) 谢谢。 - Aaron left Stack Overflow
2
似乎还有一些特定于系统的事情正在发生;对于print(1.99,digits = 22),我得到了 1.989999999999999991118 - Aaron left Stack Overflow
1
从print.default帮助页面中可以看到:“请注意,对于大量数字,目前对于digits >= 16,有效数字的计算将取决于平台内部(C库)sprintf()功能的实现。”。事实证明,在我的系统(Windows 10)上,print()永远不会显示小数点后超过17位的数字。 - andypea
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接