2013-08-03 4 views
0

У кого-нибудь есть объяснение этого странного округления в haskell (GHCi, версия 7.2.1). Кажется, все в порядке, если я не умножать с 100.Округление странности - что особенного о «100»?

*Main> 1.1 
1.1 

*Main> 1.1 *10 
11.0 

*Main> 1.1 *100 
110.00000000000001 

*Main> 1.1 *1000 
1100.0 

*Main> 1.1 *10000 
11000.0 

Edit: что puzzeling меня является то, что ошибка округления показывает только при умножении на 100.

Edit (2): Комментарии, которые я получил заставил меня понять, , что это полностью не связано с haskell, но общая проблема с числами с плавающей запятой. Многочисленные вопросы уже задавались (и отвечали) о странностях чисел с плавающей запятой, где проблема с неопределенностью типично вводила в заблуждение поплавки с действительными числами.

Perl, python, javascript и C всего сообщения 1.1 * 100.0 = 110.00000000000001. Вот что делает C

double  10.0 * 1.1 = 11.000000000000000000000000 
double 100.0 * 1.1 = 110.000000000000014210854715 
double   110.0 = 110.000000000000000000000000 
double 1000.0 * 1.1 = 1100.000000000000000000000000 

вопрос «почему это происходит только тогда, когда умножение 100» (даже если есть четкое представление 110.0) до сих пор без ответа, но я полагаю, что нет простого ответа, другой чем полностью перешагивание с помощью умножения с плавающей запятой (спасибо Dax Fohl за то, что 10 не является чем-то особенным в двоичном коде)

+3

Этот вопрос задан [более] (http://stackoverflow.com/questions/588004/is-javascripts-floating-point-math-broken) и [более] (http://stackoverflow.com/questions/ 7185512/why-0-10-2-0-3-5-5511151231258e-17-in-php) и [более] (http://stackoverflow.com/questions/6027937/javascript-float-subtract). –

+3

Я думаю, что мартин спрашивает, почему это происходит в 100, но не 1000 или 10000. Сначала я нахожу это странным. Но (предположительно) причина заключается в том, что умножение на мощность -10 не просто сдвигает цифры; он проходит через некоторый бинарный множитель, и вы оказываетесь в мантиссе и экспоненте, которые она дает вам. Поскольку 10 не является чем-то особенным в двоичном коде, вы можете в конечном итоге с вещами, которые кажутся странными на первый взгляд, вот так. –

+0

[Что каждый компьютерный ученый должен знать о арифметике с плавающей запятой] (http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf) – rampion

ответ

2

вопрос «почему это происходит только тогда, когда умножение 100» (даже если есть четкое представление 110.0) до сих пор без ответа, но я полагаю, что нет простого ответа, кроме полностью пошагового с плавающей точкой умножения

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

Двойной 1.1d составляет половину ULP от настоящего номера 1.1. Когда вы умножаете его на 10, 100, 1000 и еще несколько степеней в десять, вы умножаетесь на число N, которое точно представляется как двойное, с дополнительным свойством, что результат действительного умножения 1.1 * N точно представляется как двойной, тоже. Это делает 1.1 * N хорошим кандидатом для результата умножения с плавающей запятой, который мы будем писать RN (N * 1.1d). Но до сих пор умножение не автоматически округляется до 1,1 * N:

RN(N * 1.1d) = N * 1.1d + E1 with |E1| <= 0.5 * ULP(N*1.1d) 

      = N * (1.1 + E2) + E1 with |E2| <= 0.5 * ULP(1.1) 

      = N * 1.1 + (N * E2 + E1) 

И вопрос в том, как | N * E2 + E1 | сравнивается с ULP (N * 1.1d), так как мы предположили, что N * 1.1 является точно числом с плавающей запятой, если результат умножения (который также является числом с плавающей запятой) находится в пределах 1 ULP от N * 1.1 , он должен быть N * 1.1.


Короче говоря, это не так много, что особенного 100 ... Это то, что особенного в реальном 1.1d * 100, что 1) близка к степени двойки, находясь под ним, и 2) имеет ошибка с тем же знаком, что и ошибка при преобразовании реального 1.1 в double.

Каждый раз, когда настоящий N * 1.1d относительно ближе к ближайшей нижней силе в два, чем 1,1, к 1, результат умножения с плавающей запятой 1.1d на N должен быть точно N * 1.1 (я думаю). Примером этого случая является N = 1000, N * 1.1d ~ 1100, чуть выше 1024.

Когда реальный N * 1.1d относительно близок к сразу превосходящей мощности, равному двум, чем 1,1, равен 2, может число с плавающей запятой, которое представляет N * 1.1d лучше, чем N * 1.1. Но если ошибки E1 и E2 компенсируют друг друга (т. Е. Имеют противоположные знаки), этого не должно произойти.

3

Число 1.1 не может быть представлено конечной формой в двоичном формате. Это выглядит как 1.00011001100110011 ...

«Ошибки округления» просто математически неизбежны с помощью простых floating-point arithmetic. Если вы хотите точности, используйте тип десятичного числа.

http://support.microsoft.com/kb/42980

+0

Обязательная ссылка: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – jozefg

+3

Я понимаю, что поплавки не являются действительными. Моя проблема заключалась в том, что я не мог понять, почему ошибка проявляется только при умножении на 100, а не при умножении на 10 или 1000. –

+1

Может показаться простым добавлением или вычитанием 0, но в двоичной дробной части представленный числом плавающего указателя, это не так просто. В этом суть того, почему вы видите свое поведение. Кто-то еще связан с большой статьей о том, что comp sci должно знать о числах с плавающей запятой, и я очень рекомендую это. –