Одна и та же функция с одинаковыми входами возвращает разные значения
Допустим, у меня есть такая функция:
testFunction <- function(testInputs){
print( sum(testInputs)+1 == 2 )
return( sum(testInputs) == 1 )
}
Когда я проверяю это в командной строке со следующим вводом: c(0,65, 0,3, 0,05), он печатает и возвращает TRUE, как и ожидалось.
Однако, когда я использую c(1-0.3-0.05, 0.3, 0.05), я получаю TRUE и возвращается FALSE. Что не имеет смысла, поскольку означает, что сумма (testInputs)+1 равна 2, а сумма (testInputs) не равна 1.
Вот что я думаю: каким-то образом напечатанное значение не точно равно 1, но, вероятно, 0.9999999..., а его округленное значение на дисплее. Но это только предположение. Как это работает точно?
1 ответ
Это проблема с плавающей запятой, но для меня интересно то, как она демонстрирует, что возвращаемое значение sum()
выдает эту ошибку, но с +
ты не понимаешь
Смотрите ссылки о математике с плавающей точкой в комментариях. Вот как с этим бороться:
sum(1-0.3-0.5, 0.3, 0.05) == 1
# [1] FALSE
dplyr::near(sum(1-0.3-0.05, 0.3, 0.05), 1)
# [1] TRUE
Для меня захватывающая вещь:
(1 - 0.3 - 0.05 + 0.3 + 0.05) == 1
# [1] TRUE
Поскольку вы не можете предсказать, как будут вести себя различные реализации арифметики с плавающей запятой, вы должны исправить это. Здесь вместо использования ==
использовать dplyr::near()
, Эта проблема (математика с плавающей запятой неточна, а также непредсказуема) встречается в разных языках. Различные реализации в языке приведут к различным ошибкам с плавающей запятой.
Как я уже говорил в этом ответе на другой вопрос с плавающей запятой, dplyr::near()
, лайк all.equal()
, имеет аргумент толерантности, здесь tol
, Это установлено в .Machine$double.eps^0.5
, по умолчанию. .Machine$double.eps
это наименьшее число, которое ваша машина может добавить к 1
и быть в состоянии отличить его от 1
, Это не точно, но это на порядок. Взятие квадратного корня делает его немного больше, и позволяет вам точно идентифицировать те значения, которые отключены на величину, которая может сделать неудачный тест на равенство, скорее всего, ошибкой с плавающей запятой.
ПРИМЕЧАНИЕ: да, near()
находится в dplyr, который я почти всегда загружал, поэтому я забыл, что его нет в базе... вы можете использовать all.equal()
, но посмотрите на исходный код near()
, Это именно то, что вам нужно, и ничего, что вы не делаете:
near
# function (x, y, tol = .Machine$double.eps^0.5)
# {
# abs(x - y) < tol
# }
# <environment: namespace:dplyr>