由于方程式和浮点数的重新排序导致输出不一致

时间:2018-12-20 13:18:18

标签: r

c = .1
e = .3


(200 - 100) / (200 - 100) * (e - c) + c == .3

输出:

[1] TRUE

但是:

(e - c) * (200 - 100) / (200 - 100) + c == .3

输出:

[1] FALSE

为什么重新排序会更改输出?

这个问题与运算的代数顺序无关,因为两个左手表达式在理论上都会给出相同的结果。

我怀疑可能是编译优化导致了结果的差异。

在第一个等式中,两个c都将抵消而没有实际在(e-c)部分中进行算术运算。

在第二个方程中,必须计算(e-c)内部的算术,因此计算机必须计算.3-.1,这会导致不精确的误差。

2 个答案:

答案 0 :(得分:3)

在@AkselA的示例之后,为了简单起见,删除了+c

r1 <- a / b * (e - c)
r2 <- (e - c) * a / b
r3 <- (e - c) * (a / b)
options(digits=22)
r1
## [1] 0.1999999999999999833467
r2
## [1] 0.2000000000000000111022
r3
## [1] 0.1999999999999999833467

我们可以进一步简化为d <- e-c; a/b*d == d*a/b并获得相同的结果。

结果取决于是否将b除以(e-c)r1r3)还是之后(r2)。由于浮点算术是可交换的,但不具有关联性(请参见Wikipediathis answer中的任何链接),我们可以看到r1和{{ 1}}确实应该完全相同(对r3a/b进行求和,然后相乘),不一定与e-c相同({{1}乘以r2 ,则e-c除以a)。

  • 这些差异不是OP所建议的关于编译器优化的不是(R是一种解释语言,它不会优化算术表达式的执行)[此外,OP所建议的取消也不会实际上没有工作...]
  • 他们不是关于整数和浮点运算((e-c)*ab表明str(a)是浮点数,而不是整数;如果需要整数,请使用storage.mode(a)

答案 1 :(得分:1)

老实说,我不知道发生了什么。当然,浮点数是显而易见的,但是我看不出为什么重新排序这样的方程式会改变东西。我已经尝试过使用方程的稍微简化的版本,更改值并四处移动。

eq.1和eq.2与OP相同,由于操作员的偏好,eq.3等于eq.1(请参见?Syntax)。 */先于+-求值,除了它们从左至右求值,并且括号从内到外解析。因此,在等式1和等式3中,顺序为- / * +,在等式2中,顺序为- * / +

a <- 100
b <- 100
c <- 0.1
e <- 0.3

r1 <- a / b * (e - c) + c    # [1]

r2 <- (e - c) * a / b + c    # [2]

r3 <- (e - c) * (a / b) + c  # [3]

r1 == r2  # FALSE

r1 == r3  # TRUE

sprintf("%.20f", c(r1, r2, r3))
# "0.29999999999999998890" "0.30000000000000004441" "0.29999999999999998890"

很明显,确切的值取决于运算的顺序,尽管从纯粹的算术术语来看,这并不重要(它们是关联的)。但是只有在使用某些特定值时,顺序才有所不同。如果将ab设置为10 say,或者将c设置为0.2,则值是相同的。我的直觉是,这是由等式1中的整数模式除法和等式2中的浮点模式除法引起的,第二种引入了第一个没有舍入误差(取决于起始值)。当然,真正的带回家的信息是标记linked的作用,在比较浮点数时您需要格外小心,但是对这种精确的行为做出明确的解释仍然很不错。