Previous Entry Share Next Entry
2016-01

Floating point sucks.

Ну все знают, что у Single 6-7 значащих (в смысле "сохраняющихся после перевода из десятичной формы и обратно"), а у Double 15-17, да?

Так вот, да здравствует очень_быстрое_накопление_погрешности.

15000*8.2 = 122999.99999999999, например.
EDIT: это в Double, если кто не понял. Откройте консоль Javascript в браузере и проверьте сами.

EDIT: хорошо, раз у нас тут pedant party, поясняю. Первое число изменяется с шагом в 1000. Второе - фиксировано. До 10000 включительно всё в порядке. Максимум - 20000. Итого, делаем под результат textbox шириной в 6 символов. И уже на 11000 получаем округление вниз и погрешность не в 1Е-17, а на 17 порядков больше.

This entry was originally posted at http://wizzard.dreamwidth.org/328931.html. It has comment count unavailable comments. Please comment there using OpenID.

  • 1
belezbar December 12th, 2013
Да, засада... Так вот ходишь-ходишь в школу, а тут бац... Это что ж теперь, извращаться чем-то вроде: 15000*8 + 15000*0.2 ? )

dmih December 12th, 2013
в double тоже всё хорошо.
У меня биллинг в double (молодой был, неопытный), так с тех пор и трахаюсь с невозможностью пользоваться там оператором = в принципе, даже сравнивая с нулем.
(хотя надо сказать есть и определенные плюсы по сравнению с int/decimal).

wizzard0 December 12th, 2013
> в double тоже всё хорошо.

В javascript числа double precision. Возьми консоль браузера и попробуй.

Я обновил пост (на случай если мы неправильно друг друга поняли)

Edited at 2013-12-12 05:53 pm (UTC)

kunaifusu December 12th, 2013
Дело не в накоплении погрешности, а в том, что 1/5 не представима в двоичной системе.

juan_gandhi December 12th, 2013
Сейчас 2013-й год уже заканчивается; можно бы и пометить, что имели в виду десятичные числа.

(Deleted comment)
wizzard0 December 12th, 2013
Я не ожидал, что умножение чисел будет порождать бесконечные дроби.

juan_gandhi December 12th, 2013
А кстати, вообще было бы хорошей идеей держать числа вместе с (подразумеваемой) системой счисления - или подразумевать десятичную, и, соответственно, округления делать как положено. Скажем, 8.2 - одна цифра; 15000 - минус три цифры; произведение будет минус две цифры, остальное округляем. И т.д. Не всё так просто, конечно; можно ещё вбросить таг "это число иррациональное, его не трогать".

nponeccop December 12th, 2013
Всё уже украдено до нас

А ещё есть exact real arithmetic.

Edited at 2013-12-12 05:26 pm (UTC)

nponeccop December 12th, 2013
> да здравствует очень_быстрое_накопление_погрешности.

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

Нет никакого накопления погрешности, погрешность всё та же 1E-17.

Твой пример можно даже упростить до 1300 * 8.2 :)

3 / 10 * 3 хоть тебя не удивляет?



Edited at 2013-12-12 05:22 pm (UTC)

wizzard0 December 12th, 2013
Я в курсе, что происходит. Только оно показывалось в поле, которое обрезало (попиксельно) выведенный результат, т.к. появления периодических дробей там не предполагалось.

jakobz December 12th, 2013
Так очень точно же. Почему тебя раздражают девяточки?

nponeccop December 12th, 2013
Можно даже более хитро спросить: "почему тебя раздражает, что 0.(9) равно 1" и перейти к http://en.wikipedia.org/wiki/0.999... и плавно к http://en.wikipedia.org/wiki/Construction_of_the_real_numbers

famulan December 12th, 2013
pedant party - ыыыы шикарный термин :)

sab123 December 12th, 2013
Как бы очевидно, что "округление вниз до тысяч" - не округление. printf, например, умеет правильно округлять. А вот приведение к int - не умеет, теряет единичку. Еще, кстати, чисто в процессоре есть разные режимы округления.

soonts December 12th, 2013
Ты просто неправильно печатаешь числа.
Относительное отклонение 122999.99999999999 от правильного значения 123000 составляет всего лишь 8*10-17.
Если тебя интересуют только 6 десятичных знаков, нужно округлять не вниз, а до ближайшего.

wizzard0 December 12th, 2013
Угу.

max630 December 12th, 2013
Ну ты же понимаешь, что в данном случае и 170 значащих цифр не помогли бы? Это вот котинуум.

wizzard0 December 12th, 2013
Угу. Котинуум - это хорошее слово, я запомню.

kodt_rsdn December 12th, 2013
Ну нифига! Откуда минус-семнадцатый порядок, если операция округления - ВСЕГДА привносит погрешность в 0.5 или 1.0.
Просто иногда карты удачно ложатся, и число-с-погрешностью равно числу без погрешности... а иногда - ложатся неудачно.
Ты неудачник, тебе не повезло!

wizzard0 December 12th, 2013
)))

amarao_san December 12th, 2013
Насколько я помню институтские занятия, при работе с флоатами (не важно какой размерности) вообще нельзя думать о них, как о точных значениях. То есть approximate_float <= float(exact_int_value). Не допустимо использовать знак равенства, а сравнение на "попало или нет" должно осуществляться с учётом эпсилон, вычисляемой исходя из относительной погрешности. При сложении флоатов всегда надо рассчитывать новую погрешность - и если она больше ожидаемой величины (например, 1e+8 + 1) обрабатывать это как ошибку огрубления.

wizzard0 December 13th, 2013
Да, но, черт побери, я не ожидал что эта подстава найдет меня в таком тривиальном примере %)

(no subject) (Anonymous) Expand
(no subject) (Anonymous) Expand
max630 December 15th, 2013
строго говоря, 8.2, если оно написано именно так, означает какое-то число от 8.15 до 8.2499999, значит результат, учитывая ешё ошибку в 15000, должен быть от 122245 до 123754. так что ты ничего не потерял :)

worm_ii December 16th, 2013
Поэтому в серьёзных финансовых приложениях всё считают в десятичной системе.

  • 1
?

Log in

No account? Create an account