* [Comm] g++ и неточности с плавающей точкой
@ 2008-04-07 18:30 Michael Pozhidaev
2008-04-08 11:00 ` Sergey Vlasov
0 siblings, 1 reply; 3+ messages in thread
From: Michael Pozhidaev @ 2008-04-07 18:30 UTC (permalink / raw)
To: community
Привет всем!
Вопрос к знатокам глубин работы g++. Во время отладки порядочно нагромождённой
программы на C++ обратил внимание на то, что результаты вычислений с плавающей точкой отличаются в зависимости от параметров оптимизации и отладки.
То, что получается при -g, отличается от того, что получается при -O2.
Вопрос в том, результат ли это недоглядок, вроде использования
неинициализированных переменных или такое поведение нормально.
Можно ли добиться, чтобы вычисления всегда проходили стабильно или
g++ явно генерирует различный код, приводящий к разным результатам даже на корректной программе?
--
С уважением и наилучшими пожеланиями, Михаил Пожидаев. E-mail: msp@altlinux.ru.
Томский государственный университет. Факультет информатики. http://www.csd.tsu.ru
AltLinux Team. http://www.altlinux.ru
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [Comm] g++ и неточности с плавающей точкой
2008-04-07 18:30 [Comm] g++ и неточности с плавающей точкой Michael Pozhidaev
@ 2008-04-08 11:00 ` Sergey Vlasov
2008-04-08 13:43 ` Michael Pozhidaev
0 siblings, 1 reply; 3+ messages in thread
From: Sergey Vlasov @ 2008-04-08 11:00 UTC (permalink / raw)
To: community
[-- Attachment #1: Type: text/plain, Size: 3492 bytes --]
On Tue, Apr 08, 2008 at 01:30:31AM +0700, Michael Pozhidaev wrote:
> Вопрос к знатокам глубин работы g++. Во время отладки порядочно
> нагромождённой программы на C++ обратил внимание на то, что результаты
> вычислений с плавающей точкой отличаются в зависимости от параметров
> оптимизации и отладки. То, что получается при -g, отличается от того,
> что получается при -O2. Вопрос в том, результат ли это недоглядок,
> вроде использования неинициализированных переменных или такое поведение
> нормально. Можно ли добиться, чтобы вычисления всегда проходили
> стабильно или g++ явно генерирует различный код, приводящий к разным
> результатам даже на корректной программе?
Эффективное средство для поиска неинициализированных переменных,
обращений к освобождённой памяти и некоторых других подобных ошибок -
valgrind. Впрочем, как раз при работе с плавающей точкой у этого
инструмента есть определённые проблемы.
В архитектуре x86 регистры сопроцессора i387, используемые при
операциях с плавающей точкой, имеют разрядность 80 бит (что
соответствует типу long double). Если производятся вычисления с типом
double, компилятор в зависимости от опций оптимизации может не
сохранять промежуточные значения в памяти (что привело бы к их
округлению до точности, обеспечиваемой 64-разрядным типом double), а
брать их из регистров сопроцессора (с сохранением 80-разрядной
точности); возможно, как раз это и является причиной изменения
результатов при смене флагов оптимизации. У gcc есть опция
-ffloat-store, включение которой заставляет gcc сохранять все
промежуточные результаты вычислений с плавающей точкой в памяти, что
устраняет избыточную точность, но приводит к замедлению работы кода
из-за большого количества лишних команд пересылки между регистрами
сопроцессора и памятью.
Конечно, можно использовать для всех переменных тип long double, чтобы
все расчёты производились с максимально возможной точностью независимо
от того, будет ли компилятор сохранять промежуточные результаты в
памяти. Однако такой код может менее эффективно работать на x86_64,
где по умолчанию операции с плавающей точкой выполняются через SSE2
(команды SSE2 поддерживают только float и double, а вычисления с long
double могут выполняться только с использованием команд i387). Кроме
того, текущая версия valgrind не поддерживает эмуляцию точности long
double для i387 - при запуске под valgrind все операции с плавающей
точкой выполняются с точностью типа double, в результате поведение
программы может измениться.
Также есть возможность ограничить точность операций i387 через
соответствующие флаги управляющего слова - в <fpu_control.h>:
/* precision control */
#define _FPU_EXTENDED 0x300 /* libm requires double extended precision. */
#define _FPU_DOUBLE 0x200
#define _FPU_SINGLE 0x0
Однако, судя по комментариям в этом файле, при включении такого
округления функции из библиотеки libm могут работать неправильно
(впрочем, под valgrind они как-то работают - возможно, в некоторых
случаях точность окончательного результата типа double будет ниже, чем
должна была бы получиться при 80-разрядных промежуточных переменных).
Наконец, если допустимо требование поддержки процессором команд SSE2,
можно собирать код с опциями -msse2 -mfpmath=sse - в этом случае
вычисления с типами float и double будут выполняться с помощью команд
SSE и SSE2, не использующих 80-разрядную точность. На x86_64 эти
опции используются по умолчанию.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [Comm] g++ и неточности с плавающей точкой
2008-04-08 11:00 ` Sergey Vlasov
@ 2008-04-08 13:43 ` Michael Pozhidaev
0 siblings, 0 replies; 3+ messages in thread
From: Michael Pozhidaev @ 2008-04-08 13:43 UTC (permalink / raw)
To: ALT Linux Community general discussions
Здравствуйте, Sergey Vlasov!
> В архитектуре x86 регистры сопроцессора i387, используемые при
> операциях с плавающей точкой, имеют разрядность 80 бит (что
> соответствует типу long double). Если производятся вычисления с типом
> double, компилятор в зависимости от опций оптимизации может не
> сохранять промежуточные значения в памяти (что привело бы к их
> округлению до точности, обеспечиваемой 64-разрядным типом double), а
> брать их из регистров сопроцессора (с сохранением 80-разрядной
> точности); возможно, как раз это и является причиной изменения
> результатов при смене флагов оптимизации. У gcc есть опция
> -ffloat-store, включение которой заставляет gcc сохранять все
О, просто блестящий совет! :)
Да, включение -ffloat-store выровняло вычисления. Сейчас это важнее,
чем экстремальная скорость.
Уже собирался предпринимать поиски ошибок,
но теперь порядок. По крайней мере понятно, что происходит, и насколько адекватные результаты получаются.
Большое спасибо.
--
С уважением и наилучшими пожеланиями, Михаил Пожидаев. E-mail: msp@altlinux.ru.
Томский государственный университет. Факультет информатики. http://www.csd.tsu.ru
AltLinux Team. http://www.altlinux.ru
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2008-04-08 13:43 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-04-07 18:30 [Comm] g++ и неточности с плавающей точкой Michael Pozhidaev
2008-04-08 11:00 ` Sergey Vlasov
2008-04-08 13:43 ` Michael Pozhidaev
ALT Linux Community general discussions
This inbox may be cloned and mirrored by anyone:
git clone --mirror http://lore.altlinux.org/community/0 community/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 community community/ http://lore.altlinux.org/community \
mandrake-russian@linuxteam.iplabs.ru community@lists.altlinux.org community@lists.altlinux.ru community@lists.altlinux.com
public-inbox-index community
Example config snippet for mirrors.
Newsgroup available over NNTP:
nntp://lore.altlinux.org/org.altlinux.lists.community
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git