R语言中floor函数为何不总是返回乘法运算的预期结果?及正确计算方法咨询
为啥R里floor(0.57*100)会得到56?
先看看你遇到的这些奇怪现象:
直接对整数用floor()完全正常:
floor(c(50, 55, 56, 57, 58)) #> [1] 50 55 56 57 58
但换成小数乘100的结果,就出问题了:
floor(c(0.5*100, 0.55*100, 0.56*100, 0.57*100, 0.58*100)) #> [1] 50 55 56 56 57
不管怎么调整括号、先赋值给变量,结果都一样:
floor(c(0.5, 0.55, 0.56, 0.57, 0.58)*100) #> [1] 50 55 56 56 57 floor(c((0.5*100), (0.55*100), (0.56*100), (0.57*100), (0.58*100))) #> [1] 50 55 56 56 57 x <- 0.57*100 floor(x) #> [1] 56
甚至在0到100的范围内,还有不少数字有类似的问题:
x <- diff(floor(seq(0, 1, 0.01)*100)) names(x) <- seq(0, 0.99, 0.01)*100 x #> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #> 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 #> 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #> 1 1 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 #> 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #> 1 1 1 1 1 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 #> 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #> 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
问题根源:二进制浮点数的精度误差
其实这不是R的bug,而是计算机存储浮点数的固有特性在搞鬼。
我们日常用的是十进制,但计算机底层用的是二进制。很多十进制小数(比如0.57)没办法转换成精确的二进制小数——就像我们没法用十进制精确表示1/3(0.3333...无限循环)一样。所以0.57在计算机里只能被存储成一个近似值,当你计算0.57*100时,实际得到的并不是精确的57,而是一个非常接近57但稍微小一点的数:你可以直接在R里输入0.57*100,会看到输出是56.99999999999999。
floor()函数的作用是取不大于输入值的最大整数,面对56.99999999999999,自然就返回56了。
怎么正确得到预期的floor结果?
如果你必须先完成乘法运算再传入floor(),有几种方法可以修正这个精度误差:
先四舍五入到足够多的小数位
用round()把乘法结果修正到接近真实值,再用floor():floor(round(0.57*100, 10)) #> [1] 57这里取10位小数足够修正浮点数的微小误差了。
使用十进制小数类型
可以用decimal包来处理十进制的精确计算,避免二进制浮点数的误差:library(decimal) floor(decimal("0.57") * 100) #> [1] 57用有效数字修正
用signif()保留足够的有效数字,修正精度问题:floor(signif(0.57*100, 15)) #> [1] 57
内容的提问来源于stack exchange,提问作者Mikko




