У меня есть простой вопрос, который действительно сложен для Google (кроме канонического « Что должен знать каждый учёный-компьютерщик» об арифметической работе с плавающей точкой ).
Когда следует использовать такие функции, как log1p
или, expm1
а не log
и exp
? Когда их не следует использовать? Чем отличаются реализации этих функций с точки зрения их использования?
log1p
вы ссылаетесь (особенно на то, как это реализовано, поэтому нам не нужно догадываться).Ответы:
Мы все знаем, что подразумевает, что для , мы имеем . Это означает, что если нам нужно вычислить в плавающей точке , дляможет произойти катастрофическая отмена.exp(x)=∑n=0∞xnn!=1+x+12x2+… |x|≪1 exp(x)≈1+x exp(x)−1 |x|≪1
Это может быть легко продемонстрировано в python:
Точные значения:exp(10−8)−1exp(10−22)−1=0.000000010000000050000000166666667083333334166666668…=0.000000000000000000000100000000000000000000005000000…
В общем, «точная» реализация
exp
иexpm1
должна быть правильной, чтобы не более 1ULP (то есть одна единица последнего места). Однако, поскольку достижение этой точности приводит к «медленному» коду, иногда доступна быстрая и менее точная реализация. Например, в CUDA у нас естьexpf
иexpm1f
, гдеf
означает быстрый. Согласно руководству по программированию CUDA C, приложение. Дexpf
имеет погрешность 2ULP.Если вас не волнуют ошибки порядка нескольких ULPS, обычно разные реализации экспоненциальной функции эквивалентны, но помните, что ошибки могут быть где-то спрятаны ... (Помните ошибку Pentium FDIV ?)
Таким образом, довольно ясно, чтоexp(x)−1 x x
expm1
следует использовать для вычисления для малого . Использование его для общего не вредно, так как ожидается, что он будет точным во всем диапазоне:expm1
(В приведенном выше примере значительно ниже 1ULP, равного , поэтому все три выражения возвращают одно и то же число с плавающей запятой.)1 exp(200)
Аналогичное обсуждение справедливо для обратных функцийlog(1+x)≈x |x|≪1
log
и,log1p
поскольку для .источник
expm1(x)
вместоexp(x)-1
. Конечноexp(x) == exp(x) - 1
не держится в общем.expm1(x)
должно быть с точностью до 1ULP во всем диапазоне , постепенно теряется точность от нескольких ULP при до полного сбоя при , где - машинно-эпсилон.exp(x) - 1
Чтобы расширить разницу между
log
иlog1p
это может помочь вспомнить график, если логарифм:Если ваши данные содержат нули, то вы, вероятно, не хотите использовать,x 0 ln(x) −∞ x 0 ln(x) ln(1e)=−1 ln(1e10)=−10
log
поскольку они не определены в ноль. И когда приближается к , значение приближается к . Таким образом, если ваши значения близки к , то значение потенциально может быть большим отрицательным числом. Например, и и т. Д. Это может быть полезно, но также может искажать ваши данные в сторону больших отрицательных чисел, особенно если ваш набор данных также содержит числа, которые намного больше нуля.С другой стороны, когда приближается к , значение приближается к с положительного направления. Например, и . Таким образом, выдает только положительные значения и устраняет «опасность» больших отрицательных чисел. Обычно это обеспечивает более однородное распределение, когда набор данных содержит числа, близкие к нулю.x 0 ln(x+1) 0 ln(1+1e)∼0.31 ln(1+1e10)∼0.000045
log1p
Короче говоря, если набор данных больше , то обычно все в порядке. Но если набор данных имеет числа от до , то обычно лучше.1 0 10 1
log
log1p
источник