模数“%%”给出了“3.1 %% 0.1”的意外结果。这是预期的吗?

问题描述 投票:0回答:1

我正在使用基本模数(

%%
)运算符,并且只是努力理解以下行为。我的理解是
%%
返回除 2 个数字后的余数。所以例如:

5 %% 2
# [1] 1

6 %% 2
# [1] 0

因为 5 除以 2 余数为 1,6 除以 2 余数为 0。它也适用于小数,例如:

3.2 %% 0.1
# [1] 0

我还意识到在某些情况下可能会出现浮点问题,例如:

12345.2 %% 0.1
# [1] 4.263256e-14

我认为这是因为 12345.2 的浮点实际上是 12345.2000000000000042632... 没有问题。

我感到困惑的地方在这里:

3.1 %% 0.1
# [1] 0.1

上面为什么有0.1余数?我假设结果为 0(或者如果存在浮点问题则非常接近于零)。我是不是错过了什么?

我预计任何具有 1 个或更少小数点精度的值都会给出 0,但有很多结果似乎与此不一致(请注意,我对输出进行四舍五入只是为了避免像上面那样的“e-14”结果):

data.frame(num = seq.int(3, 5, 0.1)) |>
  dplyr::mutate(result = round(num %% 0.1, 4))
#    num result
# 1  3.0    0.1
# 2  3.1    0.1
# 3  3.2    0.0
# 4  3.3    0.1
# 5  3.4    0.1
# 6  3.5    0.1
# 7  3.6    0.1
# 8  3.7    0.1
# 9  3.8    0.1
# 10 3.9    0.1
# 11 4.0    0.1
# 12 4.1    0.1
# 13 4.2    0.1
# 14 4.3    0.1
# 15 4.4    0.0
# 16 4.5    0.1
# 17 4.6    0.1
# 18 4.7    0.1
# 19 4.8    0.1
# 20 4.9    0.0
# 21 5.0    0.1

如果我将所有值(数字和模运算符)乘以 10,我会得到我期望的行为:

data.frame(num = seq.int(30, 50, 1)) |>
  dplyr::mutate(result = round(num %% 1, 4))
#    num result
# 1   30      0
# 2   31      0
# 3   32      0
# 4   33      0
# 5   34      0
# 6   35      0
# 7   36      0
# 8   37      0
# 9   38      0
# 10  39      0
# 11  40      0
# 12  41      0
# 13  42      0
# 14  43      0
# 15  44      0
# 16  45      0
# 17  46      0
# 18  47      0
# 19  48      0
# 20  49      0
# 21  50      0

值得注意的是,我所看到的在 Python 中也是一致的。这显示了相同的行为:

import math
[math.fmod(x/10.0, 0.1) for x in range(30, 50, 1)]

我有什么遗漏的吗?

python r modulo
1个回答
0
投票

感谢评论中的人们,我现在可以看到这是如何由浮点(我将缩短为 FP)精度引起的,所以我想添加一个答案,以防它对其他人有用。我最初认为这不可能是 FP 问题,因为返回的结果是 0.1 而不是预期的 0,这比我习惯看到的 FP 错误有更大的差异(除非错误已成倍增加)不知何故,这里的情况并非如此)。

为了解释为什么会发生这种情况,以

0.9 %% 0.1
为例,这是一个输出不是预期的 0 的示例。

0.9 %% 0.1
# [1] 0.1

如果我们仔细观察这两个数字,我们可以看到 FP 精度的作用:

print(0.9, digits = 22)
# [1] 0.9000000000000000222045

print(0.1, digits = 22)
# [1] 0.1000000000000000055511

注意数字排列得很好,所以我们可以看到 0.9 有一个额外的

222045e-22
,而 0.1 有一个额外的
55511e-22
,这要归功于 FP。

我们预计 0.1 会干净地乘以 9 倍,得到 0.9(因此

%%
函数的预期结果为 0)。如果 9 乘以 0.1 的 FP 表示比 0.9 的 FP 稍微,那么余数将非常接近 0(可能在
1e-15
范围内),并且 R 将返回 0。但是 9 乘以 0.1比 0.9 稍微,我们可以通过乘以
e-22
部分来看到:

55511 * 9
# 499599

请注意,这比 0.9 获得的 222045 FP“额外位”要大。因此,当 R“尝试”将 0.1 乘以 9 次时,它并不能完全适合 0.9。因此,它必须假设在超过 0.9 之前可以乘以 0.1 的最多次数是 8(特别是 0.9 %/% 0.1 给出 8),这留下了

teeny-tiny
比 0.1 少一点。 与任何 FP 问题一样,我讨厌说“哦,那好吧”。不幸的是(至少在我看来)我们必须将其视为计算机执行数学计算方式的限制。只要我们使用浮点运算,我们就需要接受这样的问题将会出现的事实。在我看到 FP 问题之前,我认为我能够在看到它们时发现它们。这件事让我很感动,我相信这不会是最后一次发生。

© www.soinside.com 2019 - 2024. All rights reserved.