Платформа компиляции с учетом режима округления FPU при печати, конверсиях - PullRequest
4 голосов
/ 08 апреля 2010

РЕДАКТИРОВАТЬ: я сделал ошибку во время сеанса отладки, что заставило меня задать этот вопрос. Различия, которые я видел, на самом деле заключались в печати двойного и в парсинге двойного (strtod). Ответ Стивена по-прежнему очень хорошо охватывает мой вопрос даже после этого исправления, поэтому я думаю, что оставлю вопрос один, если он кому-нибудь пригодится.

Некоторые (большинство) платформ компиляции C, к которым у меня есть доступ, не учитывают режим округления FPU при

  • преобразование 64-разрядного целого числа в double;
  • печать double.

Здесь нет ничего необычного: Mac OS X Leopard, различные последние версии Linux и BSD, Windows.

С другой стороны, Mac OS X Snow Leopard, похоже, учитывает режим округления при выполнении этих двух задач. Конечно, меня не устраивает разное поведение.

Вот типичные фрагменты для двух случаев:

#if defined(__OpenBSD__) || defined(__NetBSD__) 
# include <ieeefp.h>
# define FE_UPWARD FP_RP
# define fesetround(RM) fpsetround(RM)
#else 
# include <fenv.h>
#endif

#include <float.h>
#include <math.h>

fesetround(FE_UPWARD);

...
double f;
long long b = 2000000001;
b = b*b;
f = b;

...
printf("%f\n", 0.1);

Мои вопросы:

  1. Могу ли я что-нибудь сделать, чтобы нормализовать поведение на всех платформах? Какие-то скрытые настройки, чтобы сообщать платформам, которые принимают во внимание режим округления, а не наоборот?
  2. Является ли стандарт поведения одним из них?
  3. С чем я могу столкнуться, когда режим округления FPU не используется? Вокруг к нулю? Округлить до ближайшего? Пожалуйста, скажите мне, что есть только одна альтернатива:)

Относительно 2. Я нашел место в стандарте, где говорится, что числа с плавающей точкой, преобразованные в целые числа, всегда усекаются (округляются до нуля), но я не смог найти ничего для целого числа -> направление числа с плавающей точкой.

1 Ответ

4 голосов
/ 08 апреля 2010

Если вы не установили режим округления, это должен быть режим по умолчанию IEEE-754, который округляется до ближайшего.

Для преобразования из целого числа в число с плавающей точкой стандарт C говорит (§6.3.1.4):

Когда значение целочисленного типа конвертируется в реальный плавающий тип, если конвертируемое значение может быть представлены именно в новом типе, это без изменений. Если значение конвертируется в диапазоне значений это может быть представлено, но не может быть представлены точно, результат либо ближайший выше или ближайший нижнее представимое значение, выбранное в определяемый реализацией способ. Если конвертируемое значение находится за пределами диапазон значений, которые могут быть представлен, поведение не определено.

Таким образом, оба поведения соответствуют стандарту C.

Стандарт C гласит (§F.5), что преобразования между форматами с плавающей точкой IEC60559 и последовательностями символов должны быть правильно округлены согласно стандарту IEEE-754. Для форматов не-IEC60559 это рекомендуется, но не обязательно. Стандарт IEEE-754 1985 года гласит (пункт 5.4):

Преобразования должны быть правильно округлены как указано в разделе 4 для операндов лежащий в пределах, указанных в Таблица 3. В противном случае для округления до ближайшее, ошибка в преобразованном результат не должен превышать более чем 0,47 единиц в наименее значимой цифре адресата ошибка, которая понесенные в результате округления спецификации раздела 4, при условии что показатель степени over / underflow не происходят. В режимах направленного округления ошибка должна иметь правильный знак и не должен превышать 1,47 единиц в последнее место.

Что в разделе (4) фактически говорится о том, что операция должна выполняться в соответствии с преобладающим режимом округления. То есть если вы измените режим округления, IEEE-754 говорит, что результат преобразования float-> string должен измениться соответственно. То же самое для целочисленных преобразований с плавающей запятой.

Пересмотренный в 2008 году стандарт IEEE-754 гласит (пункт 4.3):

Атрибут направления округления влияет на все вычислительные операции это может быть неточным. Неточное число результаты с плавающей точкой всегда имеют тот же знак, что и необоснованный результат.

Оба преобразования определены как вычислительные операции в разделе 5, поэтому они снова должны выполняться в соответствии с преобладающим режимом округления.

Я бы сказал, что Snow Leopard ведет себя корректно (при условии, что округляет результаты в соответствии с преобладающим режимом округления) Если вы хотите использовать старое поведение, вы всегда можете заключить вызовы printf в код, который меняет режим округления, я полагаю, хотя это явно не идеально.

Кроме того, вы можете использовать спецификатор формата %a (шестнадцатеричное число с плавающей запятой) на платформах, совместимых с C99. Поскольку результат этого преобразования всегда точен, он никогда не будет зависеть от преобладающего режима округления. Я не думаю, что библиотека Windows C поддерживает %a, но, возможно, вы могли бы достаточно легко перенести реализацию BSD или glibc, если вам это нужно.

...