在 R 中对非常小的值使用舍入函数返回零

Ash*_*win 3 floating-point r rounding floor

有时我必须处理非常低的 p 值并以表格格式呈现它们。R 返回的值可以具有很长的有效数字(即小数点后的数字)。现在,由于 p 值无论如何都很低,我倾向于在将它们写入 .xls 或 .tsv 文件之前缩短它们。(只是为了使表格看起来漂亮!!)

我在用R version 3.0.0 (2013-04-03)

一些背景和例子:

9.881313e-208在 R 中将9.88e-208在我的表中

我可以使用roundR 中的函数来做到这一点。

round(9.881313e-208, 210)
[1] 9.88e-208
Run Code Online (Sandbox Code Playgroud)

然而,在每种情况下 的幂值都e可能不同,并且由于有很多这样的情况,我使用以下公式:-

x = 9.881313e-208
round(x,abs(floor(log10(x)-2)))   ## I came to this following trial and error
[1] 9.88e-208
Run Code Online (Sandbox Code Playgroud)

我已经根据经验测试了这个公式,它在不同的情况下有效,例如:-

a <- c(1.345678e-150,8.543678e-250,5.555555e-303, 0.01123, 4.523456e-290)
round(a,abs(floor(log10(a)-2)))
[1] 1.35e-150 8.54e-250 5.56e-303  1.12e-02 4.52e-290
Run Code Online (Sandbox Code Playgroud)

现在,当 的幂e超过数字 306 时,问题就开始了(即使是 307 也可以,但在 308 之后开始变得奇怪)

## Example 1:
b <- c(1.345678e-306,1.345678e-307,1.345678e-308, 1.345678e-309, 1.345678e-310)
round(b,abs(floor(log10(b)-2)))
[1] 1.35e-306 1.30e-307 1.00e-308  0.00e+00  0.00e+00

## Example 2:
b <- c(3.345678e-306,3.345678e-307,3.345678e-308, 3.345678e-309, 3.345678e-310)
round(b,abs(floor(log10(b)-2)))

[1] 3.35e-306 3.30e-307 3.00e-308  0.00e+00  0.00e+00

## Example 3:
b <- c(7.345678e-306,7.345678e-307,7.345678e-308, 7.345678e-309, 7.345678e-310)
round(b,abs(floor(log10(b)-2)))

[1] 7.35e-306 7.30e-307 7.00e-308 1.00e-308  0.00e+00
Run Code Online (Sandbox Code Playgroud)

另外,我直接检查了这些:

round(7.356789e-306,308)
[1] 7.36e-306

round(7.356789e-307,309)
[1] 7.4e-307

round(7.356789e-308,310)
[1] 7e-308

round(7.356789e-309,311)
[1] 1e-308

round(7.356789e-310,312)
[1] 0

round(7.356789e-311,313)
[1] 0
Run Code Online (Sandbox Code Playgroud)

我是否在这里遗漏了一些微不足道的东西,或者该round功能是否达到了超出的分辨率限制e-308。我知道这些值非常低并且几乎等于零,但我仍然希望有精确的值。我在使用Python解决这个问题的SO中看到了一些答案,(请参阅如何正确地舍入一个非常小的负数?)但是有什么关于如何在R中克服这个问题的建议吗?

非常感谢帮助

干杯

阿什温

Pat*_*han 5

这个答案基于 R 的浮点数由 IEEE 754 64 位二进制数表示的假设。这与报告的结果一致,并且本质上很有可能。

对绝对大小低于 2e-308 的数字进行大量算术是非常有问题的。低于该点,精度会大幅下降。最小的可表示数字,大约 4.9E-324,在其表示中具有一位有效位。它的一对相邻数字是0和大约1.0E-323。任何舍入误差都会将其减少到零或加倍。它不会遇到仅影响十进制表示中的低有效位的细微舍入误差。同样,round也不能稍稍改变它。它可以原样返回、加倍、返回 0 或进行更大的更改。

请参阅非正规数有关所发生情况的更多说明,

解决方案是,如果可能的话,避免对如此小的数字进行算术。正如评论中已经指出的,一种好的方法是使用对数。如果您需要处理非常大和非常小的数字,这是唯一的方法。

如果这是不可能的,并且您的数字都很小,请考虑按适度大的 2 的幂进行缩放。范围内的二的幂是可以精确表示的,乘以它们只会改变指数,没有舍入误差。在舍入之前,您可以将要存储在文本文件中的所有数字按常数缩放。