Ответ 1

Ответьте, чтобы исправить вашу программу:

> all.equal(0.9,1.1-0.2)
[1] TRUE
> all.equal(0.9, 1.1-0.3)
[1] "Mean relative difference: 0.1111111"
> isTRUE(all.equal(0.9, 1.1-0.3)
[1] FALSE

и если используется в коде:

if(isTRUE(all.equal(0.9,1.1-0.2)) {
   ....
}

или в векторах:

> vec1=0.9
> vec2=c(1.1-0.2,1.3-0.4,1.0-0.2)
> mapply(function(...)isTRUE(all.equal(...)),vec1, vec2)
[1]  TRUE  TRUE FALSE

Ответ для разумных людей:

Я рекомендую вам прочитать " то, что должен знать каждый компьютерный ученый о числах с плавающей запятой". (или здесь).

Также Ричи указывает, что R faq упоминает эту проблему. Вы действительно должны прочитать весь FAQ R.

Ответ для мазохистов:

Проблема, с которой вы столкнулись, заключается в том, что плавающая точка не может представлять десятичные дроби точно в большинстве случаев, что означает, что вы часто обнаружите, что точные совпадения терпят неудачу.

в то время как R слегка лежит, когда вы говорите:

> 1.1-0.2
[1] 0.9
> 0.9
[1] 0.9

Вы можете узнать, что он на самом деле думает в десятичной форме:

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

Вы можете видеть, что эти цифры разные, но представление немного громоздко. Если мы посмотрим на них в двоичном (ну, шестнадцатеричный, что эквивалентно), мы получим более четкое изображение:

> 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
 ....
 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"

Этот факт можно использовать для создания функции "почти равных", которая проверяет, что разница близка к наименьшему представимому числу в плавающей запятой. На самом деле это уже существует (спасибо комментатору).

> ?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, ...)
....

> all.equal(0.9,1.1-0.2)
[1] TRUE

Таким образом, функция all.equal фактически проверяет, что разница между числами является квадратным корнем из наименьшей разницы между двумя мантиссами.

Этот алгоритм выглядит немного смешно вблизи чрезвычайно маленьких чисел, называемых денормалами, но вам не нужно об этом беспокоиться.

Ответ 2

Вам нужно быть осторожным в программировании, когда вы хотите проверить, не вычисленны два числа равны. R будет предполагать, что вы имеете в виду "ровно равные", и что это означает зависит от точности машины. Большинство номеров округлены до 53 двоичных цифр. Как правило, поэтому два числа с плавающей запятой не будут надежно равны, если они не были вычисленный по тому же алгоритму, и не всегда даже тогда. Вы можете видеть это путем возведения в квадрат квадратный корень из 2: эти значения одинаковы?

x <- sqrt(2)
x * x == 2
[1] FALSE

Мы можем видеть, насколько эти два значения отличаются вычитанием:

1.1 - 0.2 - 0.9
[1] 1.110223e-16