Одна и та же функция с одинаковыми входами возвращает разные значения

Допустим, у меня есть такая функция:

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>
Другие вопросы по тегам