为什么这些数字不相等? [英] Why are these numbers not equal?

查看:164
本文介绍了为什么这些数字不相等?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码显然是错误的。问题是什么?

  i < -  0.1 
i < - i + 0.05
i
## [1] 0.15
if(i == 0.15)cat(我等于0.15)else cat(我不等于0.15)
## i不等于0.15


解决方案

常规(语言不可知)原因



由于不是所有的数字都可以用 IEEE浮点运算 (几乎所有计算机用来表示十进制数字并与它们进行数学运算的标准),你不会总是得到你所期望的。这是特别真实的,因为一些简单的有限小数(例如0.1和0.05)的值在计算机中没有精确地表示,所以对它们的算术结果可能不会给出与直接表示已知答案。

这是计算机算术的一个众所周知的限制,在几个地方讨论:



比较标量



R 中的标准解决方案是不使用< a href =https://www.rdocumentation.org/packages/base/versions/3.3.2/topics/Comparison =noreferrer> == ,而是 all.equal 功能。或者说,因为 all.equal 给出了许多关于差异的详细信息,如果有的话, isTRUE(all.equal(...))

  if(isTRUE(all.equal(i,0.15)))cat( 0.15)else cat(我不等于0.15)

产生

  i等于0.15 

一些使用 all.equal 而不是 == 的例子(最后一个例子应该表明这样做会正确显示差异)。

  0.1 + 0.05 == 0.15 
#[1] FALSE
isTRUE .equal(0.1 + 0.05,0.15))
#[1] TRUE
1-0.1-0.1-0.1 == 0.7
#[1] FALSE
isTRUE(全部。 (1-0.1-0.1-0.1,0.7))
#[1] TRUE
0.3 / 0.1 == 3
#[1] FALSE
isTRUE(all.equal (0.3 / 0.1,3))
#[1] TRUE
0.1 + 0.1 == 0.15
#[1] FALSE
isTRUE(all.equal(0.1 + 0.1, 0.15))
#[1] FALSE

a href =https: //sackoverflow.com/a/2228139/892313\">对一个类似的问题:

你遇到的问题是浮点数不能表示十进制在大多数情况下,这意味着你会经常发现精确的匹配失败。



当你说:R $时,你会说:

  1.1-0.2 
#[1] 0.9
0.9
#[1] 0.9



 <$> 
$ b 您可以找出十进制的真实含义。 c $ c> sprintf(%。54f,1.1-0.2)
#[1]0.900000000000000133226762955018784850835800170898437500
sprintf(%。54f,0.9)
#[1] 0.900000000000000022204460492503130808472633361816406250

你可以看到这些数字是不同的,但是表示有点笨拙。如果我们用二进制(十六进制,等价的)来看它们,我们可以得到一个更清晰的图像:

pre $ sp code sprintf( a,0.9)
#[1]0x1.ccccccccccccdp-1
sprintf(%a,1.1-0.2)
#[1]0x1.ccccccccccccep-1
sprintf(%a,1.1-0.2-0.9)
#[1]0x1p-53

你可以看到它们的区别在于 2 ^ -53 ,这很重要,因为这个数字是两个数字之间最小的可表示的差值,接近1,因为这是。

我们可以找出任何给定的计算机这个最小的可表示的数字是通过查看R的 machine field:

?.Machine 
#....
#double.eps最小正浮点数x
#例如1 + x!= 1 。如果
#base为2或四舍五入为0,则等于base ^ ulp.digits;否则,它是
#(base ^ ulp.digits)/ 2。通常是2.220446e-16。
#....
.Machine $ double.eps
#[1] 2.220446e-16
sprintf(%a,。Machine $ double.eps)
#[1]0x1p-52

你可以用这个事实来创建一个'几乎等于函数,它检查差异是否接近浮点最小可表示的数字。实际上这已经存在: all.equal

 ?all。等于
#....
#all.equal(x,y)是比较R对象x和y测试近似等于的实用程序。
#....
#all.equal(target,current,
#tolerance = .Machine $ double.eps ^ 0.5,
#scale = NULL,check.attributes = TRUE,...)
#....

所以all.equal函数实际上是检查数字之间的差异是两个尾数之间的最小差异的平方根。



这个算法有点滑稽,接近非常小的数字称为denormals,但是你不需要担心。



比较向量



以上讨论假设两个单一的值。在R中,没有标量,只有向量和隐式向量化是语言的一个力量。为了比较矢量元素的价值,先前的原则是成立的,但是实现略有不同。 == 是向量化的(元素明智的比较),而 all.equal 将整个向量作为一个实体进行比较。

使用前面的例子

  a< -c 0.1,0.05,0.1,0.1,0.1,0.3 / 0.1,0.1 + 0.1)bb bb c(0.15,0.7,3,0.15)
code> pre
$ b $ == 不会给出预期的结果, all.equal 不执行元素


  a == b 
#[1] FALSE FALSE FALSE FALSE
all.equal(a,b)
#[1]平均相对差异:0.01234568
isTRUE(all.equal(a,b))
# 1] FALSE

相反,必须使用循环遍历这两个向量的版本



pre $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ [1] TRUE TRUE TRUE FALSE

如果需要这样的功能版本, / p>

  elementwise.all.equa l<  -  Vectorize(function(x,y){isTRUE(all.equal(x,y))})

可以被称为只是

  elementwise.all.equal(a,b)
# [1] TRUE TRUE TRUE FALSE

或者,不是将 all.equal 在更多的函数调用中,您可以复制 all.equal.numeric 的相关内部元素并使用隐式向量化:

  tolerance = .Machine $ double.eps ^ 0.5 
#这是all.equal,
#中使用的默认公差你可以选择一个不同的容差来满足你的需求

abs(a - b)<容忍
#[1] TRUE TRUE TRUE FALSE


The following code is obviously wrong. What's the problem?

i <- 0.1
i <- i + 0.05
i
## [1] 0.15
if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
## i does not equal 0.15

解决方案

General (language agnostic) reason

Since not all numbers can be represented exactly in IEEE floating point arithmetic (the standard that almost all computers use to represent decimal numbers and do math with them), you will not always get what you expected. This is especially true because some values which are simple, finite decimals (such as 0.1 and 0.05) are not represented exactly in the computer and so the results of arithmetic on them may not give a result that is identical to a direct representation of the "known" answer.

This is a well known limitation of computer arithmetic and is discussed in several places:

Comparing scalars

The standard solution to this in R is not to use ==, but rather the all.equal function. Or rather, since all.equal gives lots of detail about the differences if there are any, isTRUE(all.equal(...)).

if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")

yields

i equals 0.15

Some more examples of using all.equal instead of == (the last example is supposed to show that this will correctly show differences).

0.1+0.05==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.05, 0.15))
#[1] TRUE
1-0.1-0.1-0.1==0.7
#[1] FALSE
isTRUE(all.equal(1-0.1-0.1-0.1, 0.7))
#[1] TRUE
0.3/0.1 == 3
#[1] FALSE
isTRUE(all.equal(0.3/0.1, 3))
#[1] TRUE
0.1+0.1==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.1, 0.15))
#[1] FALSE

Some more detail, directly copied from an answer to a similar question:

The problem you have encountered is that floating point cannot represent decimal fractions exactly in most cases, which means you will frequently find that exact matches fail.

while R lies slightly when you say:

1.1-0.2
#[1] 0.9
0.9
#[1] 0.9

You can find out what it really thinks in decimal:

sprintf("%.54f",1.1-0.2)
#[1] "0.900000000000000133226762955018784850835800170898437500"
sprintf("%.54f",0.9)
#[1] "0.900000000000000022204460492503130808472633361816406250"

You can see these numbers are different, but the representation is a bit unwieldy. If we look at them in binary (well, hex, which is equivalent) we get a clearer picture:

sprintf("%a",0.9)
#[1] "0x1.ccccccccccccdp-1"
sprintf("%a",1.1-0.2)
#[1] "0x1.ccccccccccccep-1"
sprintf("%a",1.1-0.2-0.9)
#[1] "0x1p-53"

You can see that they differ by 2^-53, which is important because this number is the smallest representable difference between two numbers whose value is close to 1, as this is.

We can find out for any given computer what this smallest representable number is by looking in R's machine field:

 ?.Machine
 #....
 #double.eps     the smallest positive floating-point number x 
 #such that 1 + x != 1. It equals base^ulp.digits if either 
 #base is 2 or rounding is 0; otherwise, it is 
 #(base^ulp.digits) / 2. Normally 2.220446e-16.
 #....
 .Machine$double.eps
 #[1] 2.220446e-16
 sprintf("%a",.Machine$double.eps)
 #[1] "0x1p-52"

You can use this fact to create a 'nearly equals' function which checks that the difference is close to the smallest representable number in floating point. In fact this already exists: all.equal.

?all.equal
#....
#all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality’.
#....
#all.equal(target, current,
#      tolerance = .Machine$double.eps ^ 0.5,
#      scale = NULL, check.attributes = TRUE, ...)
#....

So the all.equal function is actually checking that the difference between the numbers is the square root of the smallest difference between two mantissas.

This algorithm goes a bit funny near extremely small numbers called denormals, but you don't need to worry about that.

Comparing vectors

The above discussion assumed a comparison of two single values. In R, there are no scalars, just vectors and implicit vectorization is a strength of the language. For comparing the value of vectors element-wise, the previous principles hold, but the implementation is slightly different. == is vectorized (does an element-wise comparison) while all.equal compares the whole vectors as a single entity.

Using the previous examples

a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1)
b <- c(0.15,     0.7,           3,       0.15)

== does not give the "expected" result and all.equal does not perform element-wise

a==b
#[1] FALSE FALSE FALSE FALSE
all.equal(a,b)
#[1] "Mean relative difference: 0.01234568"
isTRUE(all.equal(a,b))
#[1] FALSE

Rather, a version which loops over the two vectors must be used

mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)
#[1]  TRUE  TRUE  TRUE FALSE

If a functional version of this is desired, it can be written

elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})

which can be called as just

elementwise.all.equal(a, b)
#[1]  TRUE  TRUE  TRUE FALSE

Alternatively, instead of wrapping all.equal in even more function calls, you can just replicate the relevant internals of all.equal.numeric and use implicit vectorization:

tolerance = .Machine$double.eps^0.5
# this is the default tolerance used in all.equal,
# but you can pick a different tolerance to match your needs

abs(a - b) < tolerance
#[1]  TRUE  TRUE  TRUE FALSE

这篇关于为什么这些数字不相等?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆