vak: (Default)
[personal profile] vak
Знаете ли вы, что Интеловские процессоры врут при округлении?  Попробуйте умножить 3.00000000000000044 на 1.00000000000000022 - пентиум даёт 3.00000000000000089.  А между тем правильный ответ 3.00000000000000133.  Компилятор не виноват, он честно включает аппаратное округление к ближайшему числу.

То же самое при делении: 1 / 0.99999999999999989 должно давать 1.00000000000000022, а не 1.

Вывод: покупайте процессоры MIPS. :)

Upd: выяснилось, что баг проявляется только на Intel Xeon (W3520 и W3530) и Pentium 4.  Core Duo, Core2 Duo, i5 и AMD Opteron работают правильно.

Date: 2011-09-07 02:12 (UTC)
From: [identity profile] spamsink.livejournal.com
Правильный ответ, конечно же, 3.0000000000000011000000000000000968.


% perl -e 'printf "%.30f", 3.00000000000000044 * 1.00000000000000022, chr(10);'
3.000000000000001332267629550188

Режимы процессора надо правильно ставить, вот и всё.

Date: 2011-09-07 02:30 (UTC)
From: [identity profile] lionet.livejournal.com
А что такое %.30f?, это физического смысла не имеет на интеловском дубле.

Date: 2011-09-07 02:33 (UTC)
From: [identity profile] spamsink.livejournal.com
Это во избежание эффектов округления в printf. Хватило бы и .18 или .19, конечно.

Date: 2011-09-07 02:38 (UTC)
From: [identity profile] spamsink.livejournal.com
Пентиум который? У меня Intel(R) Core(TM)2 Duo CPU - в нем, как видишь, правильно.

Date: 2011-09-07 04:28 (UTC)
From: [identity profile] spamsink.livejournal.com
Ну да, будет перл арифметикой высокой точности сам заниматься.

Date: 2011-09-07 04:33 (UTC)
From: [identity profile] spamsink.livejournal.com
Он не хуже, он другой, и в его описании рассказано, как он работает с числами: держит их в виде double.

Date: 2011-09-07 04:27 (UTC)
From: [identity profile] spamsink.livejournal.com
Объясняю популярно, для невежд. Округление по умолчанию делается tie break to even. Если промежуточные вычисления делаются в long double, то 2*ε2 оказываются за пределами разрядной сетки, 5*ε после нормализации выглядит как 2.5*ε, и округление делается вниз, к 2*ε.
Если промежуточные вычисления делаются в quad, то ничьей не оказывается, и получается честное округление к ближайшему, т.е. 3*ε.
И это не баг, а естественное ограничение конкретных реализаций FPU.
Edited Date: 2011-09-07 04:42 (UTC)

Date: 2011-09-07 05:16 (UTC)
From: [identity profile] spamsink.livejournal.com
Да, пардон, это не quad, это double-double (106 бит мантиссы). IEEE не специфицирует, с какой дополнительной точностью должно делаться умножение.

Date: 2011-09-07 05:22 (UTC)
From: [identity profile] lionet.livejournal.com
Это не дабл-дабл, а extended precision (64 бит мантиссы).

Date: 2011-09-07 05:33 (UTC)
From: [identity profile] spamsink.livejournal.com
Объясняю еще раз: 64 бит мантиссы недостаточно для понимания, что ε/2+2*ε2 не является tie. Именно поэтому "старые" пентиумы этого и не понимают. А в новых анализируются все биты результата умножения двух double мантисс.

Date: 2011-09-07 05:39 (UTC)
From: [identity profile] lionet.livejournal.com
double-мантисса — это 53 бита. Я же говорю про extended precision, которые 64-бита мантиссы. double-double нет в интелах.

Date: 2011-09-07 06:26 (UTC)
From: [identity profile] spamsink.livejournal.com
Я вижу, имеется некоторое непонимание. Результат умножения в процессоре попадает в регистр, имеющий определенную длину мантиссы, которая больше, чем у double. Ненулевые разряды этого результата соответствуют 2*ε(double), ε(double)/2, и, если разрядность мантиссы у этого регистра вдвое больше, чем у double, &2*ε(double)2. Если разрядность этого регистра такова, что &2*ε(double)2 в нем нет (или в fpucw установлен соответствующий режим), то при сохранении регистра в память в формате double происходит округление по типу tie break to even, т.е. вниз, что мы и наблюдаем на старых версиях процессора. Если же регистр как минимум вдвое длиннее, чем мантисса double (и fpucw не запрещает анализировать его полностью), то происходит округление по типу round to nearest, т.е. вверх.
From: [identity profile] spamsink.livejournal.com
void
set_fpu (unsigned int mode)
{
asm ("fldcw %0" : : "m" (*&mode));
}
int main(int ac, char **av) {
volatile double a = 3.00000000000000044;
volatile double b = 1.00000000000000022;
set_fpu(0x27F);
printf("%.17f\n", a * b);
}


Результат умножения будет немедленно, до попадания в регистр FPU округляться до double, и будет правильно на всех процессорах.

Date: 2011-09-07 23:41 (UTC)
From: [identity profile] spamsink.livejournal.com
По умолчанию 0x37F (3 - extended precision, 2 - double precision, 1 - reserved, 0 - single precision).

Я вот чего не пойму:

Xeon:
FPU mode 7f: 3.00000000000000000
FPU mode 17f: 3.00000000000000089
FPU mode 27f: 3.00000000000000133
FPU mode 37f: 3.00000000000000089

Core:
FPU mode 7f: 3.00000000000000133
FPU mode 17f: 3.00000000000000133
FPU mode 27f: 3.00000000000000133
FPU mode 37f: 3.00000000000000133

Что у них extended precision разная - понятно, но почему single precision так нагло игнорируется?
Edited Date: 2011-09-08 00:19 (UTC)

Date: 2011-09-08 00:36 (UTC)
From: [identity profile] spamsink.livejournal.com
Потому что их extended precision достаточна, чтобы eps*eps туда поместился.

Date: 2011-09-08 00:51 (UTC)
From: [identity profile] spamsink.livejournal.com
Эти два бита всю жизнь этакие полусекретные были, раз никакого API для них не предусмотрено.

Date: 2011-09-07 02:39 (UTC)
From: [identity profile] lionet.livejournal.com
[vlm@nala:~]> cc -o c c.c && ./c 3.00000000000000044 1.00000000000000022
3.00000000000000044
3.00000000000000133
[vlm@nala:~]> cat c.c
int main(int ac, char **av) {
    printf("%.17f\n", 3.00000000000000044, 1.00000000000000022);
    printf("%.17f\n",
        atof(av[1]) * atof(av[2]));
}
[vlm@nala:~]>


Понятно теперь, что интеловский процессор не виноват?

Date: 2011-09-07 03:24 (UTC)
From: [identity profile] lionet.livejournal.com
Значит у нас разные интеловские процессоры. Попробуй -O2?

Date: 2011-09-07 04:31 (UTC)
From: [identity profile] spamsink.livejournal.com
http://ramlamyammambam.livejournal.com/158379.html?thread=1280427#t1280427

Date: 2011-09-07 08:40 (UTC)
From: [identity profile] mtve.livejournal.com
printf("%.17f\n", 3.00000000000000044, 1.00000000000000022);
имелось в виду умножение?

Date: 2011-09-07 14:45 (UTC)
From: [identity profile] lionet.livejournal.com
Да, тормоз.

Date: 2011-09-07 04:03 (UTC)
From: [identity profile] cema.livejournal.com
У меня Core Duo и i5, работает правильно.

Date: 2011-09-07 04:51 (UTC)
From: [identity profile] oboguev.livejournal.com
Теперь задача -- научиться образующиеся доли центов откладывать на свой банковский счёт ;-)

Я, кстати, однажды благодаря багу в expedia получил место в business классе на билет в экономическом. Это было вскоре после того как они ввели интерактивный выбор места. Я выбрал место в начале самолёта, меня туда и посадили.

Date: 2011-09-07 05:01 (UTC)
From: [identity profile] ircicq.livejournal.com
В финансовых расчетах должны использоваться только целые числа, возможно выраженные в сотых долях копейки

Date: 2011-09-07 06:48 (UTC)
From: [identity profile] tsw.livejournal.com
раз уж зашел разговор про MIPS - а нет ли какого-нибудь форума, где тусуються люди работающие с MIPS ?
меня интересует возможность поговорить с теми, кто asm под MIPS c листа читает =)

Date: 2011-09-11 20:19 (UTC)
From: [identity profile] alec_v.livejournal.com
А давайте сделаем. "See MIPS run" лежит под подушкой ;)

В QEMU -- linux usermode test и куски монитора Malta - моё поделие.

Date: 2011-09-11 22:37 (UTC)
From: [identity profile] tsw.livejournal.com
просто у меня к MIPS достаточно локальный интерес!
тем более, что надо у автора поста узнать что думает про самостийные форумы сам MIPS!

но сделать свое - почему бы и нет!