Я сейчас смотрю на код, который делает арифметику с плавающей точкой с множественной точностью. Для корректной работы этого кода требуется, чтобы значения были сведены к конечной точности в четко определенных точках. Таким образом, даже если промежуточный результат был вычислен для 80 бит повышенной точности регистр с плавающей запятой, в какой-то момент он должен быть округлен до 64 бит двойной для последующих операций.
Код использует макрос INEXACT
описать это требование, но не имеет идеального определения. Руководство по gcc упоминает -fexcess-precision=standard
как способ заставить четко определенную точность для операций приведения и присваивания. Тем не менее, он также пишет:
Fe -fexcess-precision = standard ’не реализован для языков, отличных от C
Сейчас я думаю о переносе этих идей на C ++ (комментарии приветствуются, если кто-нибудь знает существующую реализацию). Так что, похоже, я не могу использовать этот переключатель для C ++. Но каково поведение g ++ по умолчанию при отсутствии какого-либо переключателя? Существуют ли еще C ++-подобные способы управления обработкой избыточной точности?
Я думаю, что для моего текущего случая использования, я, вероятно, буду использовать -mfpmath=sse
в любом случае, что, насколько я знаю, не должно приводить к какой-либо излишней точности. Но мне все еще интересно.
Существуют ли еще 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-битный слот в стеке.
Но каково поведение 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 ++ не обязательно округляется везде, где следует.