ALT Linux Community general discussions
 help / color / mirror / Atom feed
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 --]

  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