Спасибо всем, кто ответил.Я ожидал бы, что стандарт будет немного более точным в этом вопросе, но я думаю, что нет.Я резюмирую то, что я почерпнул из этого обсуждения, используя мой канонический пример алгоритма суммирования Кахана в качестве конкретного экземпляра:
Рассмотрим следующее (err, item и totalSum имеют тип «double»):
err = ((nextItem + totalSum) - totalSum) - nextItem;
Здесь мы находимся в цикле, суммируя элементы массива элементов.ЕСЛИ вышеприведенное утверждение фактически выполняется точно так, как написано, «err» будет содержать биты, которые иначе «выпали бы из конца» из-за ограниченной точности (totalSum велико по сравнению со nextItem).
Аннотация Cмашина требует, чтобы выражение оценивалось «как если бы», оно было вычислено как записанное.C обычно позволяет оценивать промежуточное выражение с более высокой точностью, а затем округлять конечный результат для соответствия.К сожалению, абстрактная машина C обладает бесконечной точностью.Это означает, что является легальным / соответствующим для реализации для оптимизации вышеприведенного выражения в:
err = 0.0;
, поскольку, если бы вы имели бесконечную точность, они были бы одинаковыми.
Конкретная реализация может выбрать реализацию более строгих правил оценки выражений (и, по общему признанию, все реальные реализации), например, для соответствия семантике IEEE 754, но это не требуется по стандарту C.
ОДНАКО, по стандарту требуется, чтобы при наличии присваивания или приведения соответствующее значение в этой точке было преобразовано в правильный тип.На практике это нарушает парадигму бесконечной точности.Поэтому, если мы напишем выражение следующим образом:
double tmp = nextItem + totalSum;
tmp = tmp - totalSum;
err = tmp - nextItem;
Здесь стандартные гарантии того, что мы do получат желаемый эффект, поскольку, когда мы присваиваем подвыражение tmp,значение должно быть округлено, чтобы соответствовать, и поэтому прямая замена не позволяет отменить условия.Мы могли бы даже сделать это следующим образом:
err = ((double)((double)(nextItem + totalSum)) - totalSum) - nextItem;
, что выглядит довольно странно, поскольку все переменные уже удвоены, но приведение вынуждает компилятор учитывать эффект ограниченной точности при оптимизации.
Является ли конкретная реализация на самом деле стандартом, соответствующим этому пункту, - это другой вопрос.