Неверное значение средней функции - PullRequest
1 голос
/ 15 октября 2019

У меня есть массив из 80 элементов с такими же записями: 176.019779658138 53

Если я использую функцию среднего значения, я получу значение 176.019779658138 42

Почему это?


Вот минимальный рабочий пример:

using Statistics
arr = fill(176.01977965813853, 80)

julia> mean(arr)
176.01977965813842

Я ожидал, что это вернет 176.01977965813853.

Ответы [ 2 ]

4 голосов
/ 15 октября 2019

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

julia> using KahanSummation
[ Info: Precompiling KahanSummation [8e2b3108-d4c1-50be-a7a2-16352aec75c3]

julia> sum_kbn(fill(176.01977965813853, 80))/80
176.01977965813853

Ссылка: Википедия

3 голосов
/ 15 октября 2019

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

using Statistics
arr = fill(176.01977965813853, 80)

julia> mean(arr)
176.01977965813842

Причина этого заключается в том, что Юлия выполняет всю арифметику с плавающей запятой с 64-битной точностью по умолчанию (т. Е. Тип Float64). Float64 s не может представлять любое действительное число. Существует конечный шаг между каждым числом с плавающей запятой, и ошибки округления возникают, когда вы выполняете арифметику с ними. Эти ошибки округления обычно хороши, но если вы не будете осторожны, они могут быть катастрофическими. Например:

julia> 1e100 + 1.0 - 1e100
0.0

Это говорит о том, что если я сделаю 10^100 + 1 - 10^100, я получу ноль! Если вы хотите получить верхнюю границу ошибок, вызванных арифметикой с плавающей запятой, мы можем использовать IntervalArithmetic.jl:

using IntervalArithmetic

julia> 1e100 + interval(1.0) - 1e100
[0, 1.94267e+84]

Это говорит о том, что операция 1e100 + 1.0 - 1e100 по крайней мере равна 0.0 имаксимум 1.94*10^84, поэтому границы ошибок огромны!

Мы можем сделать то же самое для интересующей вас операции:

arr = fill(interval(176.01977965813853), 80);

julia> mean(arr)
[176.019, 176.02]

julia> mean(arr).lo
176.019779658138

julia> mean(arr).hi
176.0197796581391

, которая говорит, что среднее значение может составлять не менее 176.019779658138 или не более176.0197796581391, но нельзя быть более уверенным из-за ошибки с плавающей запятой! Так вот, Float64 дал ответ с не более 10^-13 процентов ошибкой, которая на самом деле довольно мала.

Что если это недопустимые границы ошибок? Используйте больше точности! Вы можете использовать строковый макрос big для получения числовых литералов произвольной точности:

arr = fill(interval(big"176.01977965813853"), 80);

julia> mean(arr).lo
176.0197796581385299999999999999999999999999999999999999999999999999999999999546

julia> mean(arr).hi
176.019779658138530000000000000000000000000000000000000000000000000000000000043

Это вычисление было выполнено с использованием 256-битной точности, но вы можете получить еще большую точность, используя функцию setprecision:

setprecision(1000)
arr = fill(interval(big"176.01977965813853"), 80);

julia> mean(arr).lo
176.019779658138529999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999599

julia> mean(arr).hi
176.019779658138530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000579

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

...