<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>Блоги Intel® Software Network &#187; Открытый код</title>
	<atom:link href="http://softwareblogs-rus.intel.com/category/oss/feed/" rel="self" type="application/rss+xml" />
	<link>http://softwareblogs-rus.intel.com</link>
	<description></description>
	<pubDate>Wed, 27 Aug 2008 15:42:06 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.1</generator>
	<language>en</language>
			<item>
		<title>Точность и вежливость компилятора</title>
		<link>http://softwareblogs-rus.intel.com/2008/08/18/84/</link>
		<comments>http://softwareblogs-rus.intel.com/2008/08/18/84/#comments</comments>
		<pubDate>Sun, 17 Aug 2008 23:12:16 +0000</pubDate>
		<dc:creator>Maxym Dmytrychenko (Intel)</dc:creator>
		
		<category><![CDATA[Открытый код]]></category>

		<category><![CDATA[Разработка софта]]></category>

		<category><![CDATA[compiler]]></category>

		<category><![CDATA[FPU]]></category>

		<category><![CDATA[precision]]></category>

		<category><![CDATA[SSE]]></category>

		<guid isPermaLink="false">http://softwareblogs-rus.intel.com/2008/08/18/84/</guid>
		<description><![CDATA[В процесе нахождения высшей истины иногда приходиться спотыкать и полностью осознавать базис.
Возьмем к примеру, следующий код
:#include &#60;stdio.h&#62;
int main (void)
{
  double a = 3.0, b = 7.0, c;
  c = a / b;
  if (c == a / b) {
    printf ("comparison succeeds\n");
  } else {
    printf ("unexpected result\n");
  }
  return 0;
}
и оказываеться что например на gcc, [...]]]></description>
			<content:encoded><![CDATA[<p>В процесе нахождения высшей истины иногда приходиться спотыкать и полностью осознавать базис.</p>
<p>Возьмем к примеру, следующий код</p>
<p>:#include &lt;stdio.h&gt;</p>
<p>int main (void)<br />
{<br />
  double a = 3.0, b = 7.0, c;</p>
<p>  c = a / b;</p>
<p>  if (c == a / b) {<br />
    printf ("comparison succeeds\n");<br />
  } else {<br />
    printf ("unexpected result\n");<br />
  }</p>
<p>  return 0;<br />
}</p>
<p>и оказываеться что например на gcc, наверное и на других компиляторах, он вполне может выдавать unexpected result.</p>
<p>странно ?</p>
<p>Тут надо вспомнить немного теории о точности и FPU:</p>
<p>FPU оперирует в трех вариантах точности:</p>
<p>24 bits precision - одинарная точность (single precision), 32 bit length</p>
<p>53 bits precision - двойная точность (double precision),64 bit length</p>
<p>64 bits precision - повышеная точность  (extended precision or double extended precision), 80 bit length , вариант работы по умолчанию.</p>
<p> </p>
<p>где например 80bit IEEE754 формат = 1bit sign + 15bit exponent + 64bit mantissa.</p>
<p>Кстати, уже должно быть понятно что SSE инструкции, вплоть до SSE4, по определению не могут работать с double extended precision ибо максимальный размер базовых типов - 64 bit length еще с SSE2 времен - просьба не путать с разрядностью XMM регистров (128 бит)</p>
<p>Немного я отошел в сторону - возращаясь к нашему коду , взглянем на asm и видим</p>
<p>00401082  |. DD05 40204000  FLD QWORD PTR DS:[402040]<br />
00401088  |. DD5D F8        FSTP QWORD PTR SS:[EBP-8]<br />
0040108B  |. DD05 48204000  FLD QWORD PTR DS:[402048]<br />
00401091  |. DD5D F0        FSTP QWORD PTR SS:[EBP-10]<br />
00401094  |. DD45 F8        FLD QWORD PTR SS:[EBP-8]<br />
00401097  |. DC75 F0        FDIV QWORD PTR SS:[EBP-10]<br />
0040109A  |. DD5D E8        FSTP QWORD PTR SS:[EBP-18]<br />
0040109D  |. DD45 F8        FLD QWORD PTR SS:[EBP-8]<br />
004010A0  |. DC75 F0        FDIV QWORD PTR SS:[EBP-10]<br />
004010A3  |. DD45 E8        FLD QWORD PTR SS:[EBP-18]<br />
004010A6  |. D9C9           FXCH ST(1)<br />
004010A8  |. DAE9           FUCOMPP<br />
004010AA  |. DFE0           FSTSW AX<br />
004010AC  |. 9E             SAHF</p>
<p>как бы не так все и плохо</p>
<p>Присмотревшись поближе увидим/вспомним что</p>
<p>QWORD = quadruple word (64-bit)</p>
<p>(Кстати FDIV с операндом в памяти не может использовать повышеную точность  (double extended precision) )</p>
<p>компилятор постарался.</p>
<p>Например может еще и так</p>
<p>00401013  |&gt; D97D FE        FSTCW WORD PTR SS:[EBP-2]<br />
00401016  |. 0FB745 FE      MOVZX EAX,WORD PTR SS:[EBP-2]<br />
0040101A  |. 25 C0F0FFFF    AND EAX,FFFFF0C0<br />
0040101F  |. 66:8945 FE     MOV WORD PTR SS:[EBP-2],AX<br />
00401023  |. 0FB745 FE      MOVZX EAX,WORD PTR SS:[EBP-2]<br />
00401027  |. 0D 3F030000    OR EAX,33F<br />
0040102C  |. 66:8945 FE     MOV WORD PTR SS:[EBP-2],AX<br />
00401030  |. D96D FE        FLDCW WORD PTR SS:[EBP-2]</p>
<p>принудительно сбрасывая FPU в</p>
<p>64 bits precision - повышеная точность  (extended precision or double extended precision), 80 bit length</p>
<p>В итоге имеем:</p>
<p>Для сохранения/восстановления промежуточного значения используеться QWORD PTR (8 байт)  а для последующего сравнения используеться результат  в 10 байт (повышеная точность) !</p>
<p> </p>
<p>Вариантов решения "unexpected result" недопонимания как бы несколько:</p>
<p>1. Например принудительное переключение FPU на работу с двойной точностью вместо повышеной точности, например так</p>
<p>void<br />
set_fpu (unsigned int mode)<br />
{<br />
  asm ("fldcw %0" : : "m" (*&amp;mode));<br />
}</p>
<p>и дальше перед вычислениями</p>
<p>set_fpu (0x27F);</p>
<p>главное что мы устанавливаем контрольный регистр FPU , а именно биты 8 и 9 в соответствии:</p>
<p>The <strong>PC</strong> field (bits 9 and 8 ) or <strong>P</strong>recision <strong>C</strong>ontrol determines to what precision the FPU rounds results after each arithmetic instruction in one of three ways:</p>
<p>00 = 24 bits (REAL4)<br />
01 = <em>Not used</em><br />
10 = 53 bits (REAL8)<br />
11 = 64 bits (REAL10) (this is the initialized state)</p>
<p>2. Так как размерность double подходит к SSE типам можно попросить компилятор использовать SSE вместо FPU</p>
<p>для gcc например так -mfpmath=sse -msse2</p>
<p>что обратиться во вполне вероятное использование SSE инструкций типа DIVSD и далее UCOMISD .</p>
<p>Однако этот подход не отрицает полное использование FPU, например для transcendental functions.</p>
<p> </p>
<p>3. Вместо double переключаемся на long double ( размерность которого (попрошу еще раз свериться с используемым компилятором) должна быть 12 байт по сравнению с 8 )</p>
<p>что позволяет нам с успехом и без проблем сохранять</p>
<p>64 bits precision - повышеная точность  (double extended precision), 80 bit length , вариант работы по умолчанию для FPU</p>
<p> и компилятор начнет использовать FPU деление в виде</p>
<p>FDIVP ST(1),ST</p>
<p>тем самым сохраняя наши ценные биты и будет использовать правильный размер операндов</p>
<p>004010AA  |. DB6D E8        FLD TBYTE PTR SS:[EBP-18]<br />
004010AD  |. DB6D D8        FLD TBYTE PTR SS:[EBP-28]<br />
004010B0  |. DEF9           FDIVP ST(1),ST<br />
004010B2  |. DB7D C8        FSTP TBYTE PTR SS:[EBP-38]<br />
004010B5  |. DB6D E8        FLD TBYTE PTR SS:[EBP-18]<br />
004010B8  |. DB6D D8        FLD TBYTE PTR SS:[EBP-28]<br />
004010BB  |. DEF9           FDIVP ST(1),ST<br />
004010BD  |. DB6D C8        FLD TBYTE PTR SS:[EBP-38]</p>
<p> где</p>
<p>TBYTE - temporary real, 80 bit.</p>
<p> </p>
<p>Вообщем и все.</p>
<p> </p>
<p>Пару полезностей по-ходу дела:</p>
<p>- прочитать текущие контольный регистр FPU можно например так</p>
<p>volatile unsigned short int cw;<br />
__asm__ volatile("fstcw %0" : "=m" (*&amp;cw));</p>
<p> - для использования в Windows</p>
<p>#define PRECISION_SIGNLE 0x000<br />
#define PRECISION_DOUBLE 0x200<br />
#define PRECISION_EXTENDED 0x300</p>
<p>unsigned short int set_x87_precision( unsigned short int m_precision){<br />
 volatile unsigned short int old_cw;<br />
 volatile unsigned short int new_cw;<br />
 _asm{<br />
  fstcw old_cw<br />
  mov ax,old_cw<br />
  and ax,0fcffh<br />
  or ax,m_precision<br />
  mov new_cw,ax<br />
  fldcw new_cw<br />
 }<br />
 return old_cw;<br />
}</p>
<p>unsigned short int get_x87_control_word(){<br />
    volatile unsigned short int cw;<br />
 __asm { fstcw cw }<br />
 return cw;<br />
}</p>
<p>- по умолчанию приложения в Linux OS, MacOS оперируют с FPU в  повышеной точностью</p>
<p>и в Windows OS - двойная точность</p>
<p> </p>
<p>вообщем , моя рука писать устала ...</p>
<p>надеюсь это было полезно, комментарии принимаются.</p>
]]></content:encoded>
			<wfw:commentRss>http://softwareblogs-rus.intel.com/2008/08/18/84/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
