From: Sergey Vlasov <vsu@altlinux.ru>
To: community@lists.altlinux.org
Subject: Re: [Comm] g++ и неточности с плавающей точкой
Date: Tue, 8 Apr 2008 15:00:55 +0400
Message-ID: <20080408110055.GC12554@newmaster.mivlgu.local> (raw)
In-Reply-To: <m363uty0u0.fsf@blard.localdomain>
[-- 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 --]
next prev parent reply other threads:[~2008-04-08 11:00 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-04-07 18:30 Michael Pozhidaev
2008-04-08 11:00 ` Sergey Vlasov [this message]
2008-04-08 13:43 ` Michael Pozhidaev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080408110055.GC12554@newmaster.mivlgu.local \
--to=vsu@altlinux.ru \
--cc=community@lists.altlinux.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
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