Оптимизация Clang изменяет результат вычислений в C, даже когда я добавляю явное приведение типов - PullRequest
1 голос
/ 26 апреля 2020

Я заметил, что код ниже, скомпилированный с clang 11.0.3, дает другой результат, когда я использую флаг -O0 и -O3.

#include <stdio.h>
#include <inttypes.h>

int64_t foo(int64_t a, int32_t b, int32_t c) {
    const int32_t p1 = 7654321;
    const int32_t p2 = 8765432;
    const int64_t p3 = 1234567LL;
    const int32_t p4 = 987654;
    const int64_t e = a + b * b * p1 + b * p2 + c * c * p3 + c * p4; 
    return e;
}

int main(void) {
    const int64_t a = 1234LL;
    int32_t b = 130;
    int32_t c = -148;
    printf("%lld\n", foo(a, b, c)); // -O0: 28544296190, -O3: 28544296190
    b = 167;
    c = -93;
    printf("%lld\n", foo(a, b, c)); // -O0: 10772740108, -O3: 15067707404
    return 0;
}

Первый результат тот же, но второй отличается. Я думал, что это происходит из-за неявного преобразования типов. Я скомпилировал код в сборку с флагом -O0, чтобы увидеть, в каком порядке выполняются все вычисления. В соответствии с этим я добавил явное приведение и скобки в функцию foo:

const int64_t e = (((a + (int64_t)(b * b * p1)) + (int64_t)(b * p2)) + (int64_t)((int64_t)(c * c) * p3)) + (int64_t)(c * p4);

Это не помогло, хотя я действительно не знаю, как это исправить. Как должен выглядеть код для правильной работы с оптимизацией O3?

1 Ответ

1 голос
/ 26 апреля 2020

Здесь у вас переполнение:

 b * b * p1

Когда b равно 167, у вас сначала (int32_t) 167 * (int32_t) 167 == (int32_t) 27889. Тогда у вас есть (int32_t) 27889 * (int32_t) 7654321 == 213471358369, который находится вне диапазона 32-разрядного целого числа со знаком. Переполнение целых чисел со знаком вызывает неопределенное поведение , которое, по-видимому, использовалось лягушкой при -O3.

Приведения, которое вы выполнили, было недостаточно, потому что приведение было применено после переполнения произошло. Вам необходимо добавить приведение по крайней мере к первому операнду каждого умножения, чтобы все операнды были преобразованы в int64_t.

const int64_t e = a + (int64_t)b * b * p1 + (int64_t)b * p2 + (int64_t)c * c * p3 + (int64_t)c * p4; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...