Операции с плавающей запятой в C-ассоциативном?

Дополнение математически содержит ассоциативное свойство:

(a + b) + c = a + (b + c)

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

Можно ли компилятору разрешить эту замену при генерации машинного кода из программы C как часть оптимизации? Где он точно говорит в стандарте C?

Ответ 1

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

5.1.2.3 Выполнение программы

[# 1] Семантические описания в этом Интернационале        Стандарт описывает поведение абстрактной машины в        какие вопросы оптимизации не имеют значения.

[# 3] В абстрактной машине все выражения оцениваются      как определено семантикой.

[# 13] ПРИМЕР 5 Перестановка для выражений с плавающей точкой        часто ограничивается из-за ограничений точности        а также диапазон. Реализация не может в целом применяться        математические ассоциативные правила для добавления или        умножения или распределения, из-за        ошибка округления, даже при отсутствии переполнения и        опустошения.

В вашем примере:

(a + b) + c

или даже без круглых скобок:

a + b + c

имеем

   +
  / \
  +  c
 / \
 a  b

и компилятор должен генерировать код, как если бы a суммируется с b, и результат суммируется с c.

Ответ 2

Умножение с плавающей запятой в C не является ассоциативным.

In C, Floating point multiplication is not associative.

Некоторые данные относятся к этому коду C:

Выберите три случайных значения поплавка.
Проверьте, не соответствует ли a*(b*c) (a*b)*c

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
using namespace std;
int main() {
    int counter = 0;
    srand(time(NULL));
    while(counter++ < 10){
        float a = rand() / 100000;
        float b = rand() / 100000;
        float c = rand() / 100000;

        if (a*(b*c) != (a*b)*c){
            printf("Not equal\n");
        }
    }
    printf("DONE");
    return 0;
}

Программа печатает:

Not equal
Not equal
Not equal
Not equal
DONE
RUN FINISHED; exit value 0; real time: 10ms; user: 0ms; system: 0ms

Вывод:

Для моего теста три случайно выбранных значения умножения с плавающей запятой ассоциативны примерно в 70% случаев.