GCC — C ++ обработка избыточной точности

Я сейчас смотрю на код, который делает арифметику с плавающей точкой с множественной точностью. Для корректной работы этого кода требуется, чтобы значения были сведены к конечной точности в четко определенных точках. Таким образом, даже если промежуточный результат был вычислен для 80 бит повышенной точности регистр с плавающей запятой, в какой-то момент он должен быть округлен до 64 бит двойной для последующих операций.

Код использует макрос INEXACT описать это требование, но не имеет идеального определения. Руководство по gcc упоминает -fexcess-precision=standard как способ заставить четко определенную точность для операций приведения и присваивания. Тем не менее, он также пишет:

Fe -fexcess-precision = standard ’не реализован для языков, отличных от C

Сейчас я думаю о переносе этих идей на C ++ (комментарии приветствуются, если кто-нибудь знает существующую реализацию). Так что, похоже, я не могу использовать этот переключатель для C ++. Но каково поведение g ++ по умолчанию при отсутствии какого-либо переключателя? Существуют ли еще C ++-подобные способы управления обработкой избыточной точности?

Я думаю, что для моего текущего случая использования, я, вероятно, буду использовать -mfpmath=sse в любом случае, что, насколько я знаю, не должно приводить к какой-либо излишней точности. Но мне все еще интересно.

9

Решение

Существуют ли еще C ++-подобные способы управления обработкой избыточной точности?

Стандарт C99 определяет FLT_EVAL_METHODмакрос, установленный компилятором, который определяет, как должна происходить избыточная точность в программе на C (многие компиляторы C по-прежнему ведут себя так, что не совсем соответствуют наиболее разумной интерпретации значения FP_EVAL_METHOD что они определяют: более старые версии GCC, генерирующие код 387, Clang при генерации кода 387,…). Тонкие моменты в связи с последствиями FLT_EVAL_METHOD были уточнены в стандарте C11.

Начиная со стандарта 2011 года, C ++ откладывает до C99 для определения FLT_EVAL_METHOD (заголовок cfloat).

Так что GCC должен просто позволить -fexcess-precision=standard для C ++, и, надеюсь, это в конечном итоге будет. Та же семантика, что и в C, уже есть в стандарте C ++, их нужно реализовывать только в компиляторах C ++.


Я предполагаю, что для моего текущего варианта использования я, вероятно, буду использовать -mfpmath = sse в любом случае, что, насколько я знаю, не должно приводить к избыточной точности.

Это обычное решение.

Помните, что C99 также определяет FP_CONTRACT в math.h, на который вы, возможно, захотите взглянуть: это относится к той же проблеме некоторых выражений, которые вычисляются с более высокой точностью, с совершенно другой стороны (современная инструкция fused-multiply-add вместо старого набора команд 387 ). Это прагма, позволяющая решить, разрешено ли компилятору заменять сложения и умножения на уровне исходного кода инструкциями FMA (это приводит к тому, что умножение фактически вычисляется с бесконечной точностью, потому что именно так работает эта инструкция, а не округляется до точность типа, как это было бы с отдельными инструкциями умножения и сложения). Эта прагма, по-видимому, не была включена в стандарт C ++ (насколько я вижу).

Значение по умолчанию для этой опции определяется реализацией, и некоторые люди утверждают, что по умолчанию разрешено генерировать инструкции FMA (для компиляторов C, которые в противном случае определяют FLT_EVAL_METHOD как 0).
Вы должны, в C, на будущее
ваш код с:

#include <math.h>
#pragma STDC FP_CONTRACT off

И эквивалентное заклинание в C ++, если ваш компилятор документирует его.


каково поведение g ++ по умолчанию при отсутствии какого-либо переключателя?

Я боюсь, что ответ на этот вопрос заключается в том, что поведение GCC, скажем, при генерации кода 387, является бессмысленным. Смотрите описание ситуация Это побудило Джозефа Майерса исправить ситуацию для C. Если g ++ не реализует -fexcess-precision=standard, это, вероятно, означает, что 80-битные вычисления случайным образом округляются до точности типа, когда компилятору пришлось пролить некоторые регистры с плавающей запятой в память, что привело к тому, что программа в некоторых случаях выводила «foo» вне контроля программиста :

if (x == 0.0) return;
... // code that does not modify x
if (x == 0.0) printf("foo\n");

… потому что код в многоточии вызвал x, который был сохранен в 80-битном регистре с плавающей запятой, чтобы быть разлитым в 64-битный слот в стеке.

4

Другие решения

Но каково поведение g ++ по умолчанию при отсутствии какого-либо переключателя?

Я нашел один ответ сам с помощью эксперимента, используя следующий код:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
double a = atof("1.2345678");
double b = a*a;
printf("%.20e\n", b - 1.52415765279683990130);
return 0;
}

Если b округляется (-fexcess-precision=standard), то результат равен нулю. Иначе (-fexcess-precision=fast) это что-то вроде 8e-17, Компилирование с -mfpmath=387 -O3Я мог бы воспроизвести оба случая для gcc-4.8.2, За g++-4.8.2 Я получаю ошибку за -fexcess-precision=standard если я попробую это, и без флага я получу то же поведение, что и -fexcess-precision=fast дает за C. Добавление -std=c++11 не помогает. Так что теперь подозрение, уже высказанное Паскалем, является официальным: g ++ не обязательно округляется везде, где следует.

4

По вопросам рекламы [email protected]