即使在我添加显式类型转换时,铿锵优化也会改变C中的计算结果

即使在我添加显式类型转换时,铿锵优化也会改变C中的计算结果,c,optimization,casting,clang,C,Optimization,Casting,Clang,我注意到下面用Clang11.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; co

我注意到下面用Clang11.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;
}


这并没有帮助,但我真的不知道如何修复它。为了正确使用O3优化,代码应该是什么样子

这里有溢出:

 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; 

您必须强制转换操作数以强制执行更大的算术运算<代码>(b*b*p1)可能已经溢出,在乘法后进行强制转换不会停止它。因此
(int64_t)(b*b*p1)
需要
((int64_t)b*b*p1)
强制转换不告诉如何做算术,只告诉操作数是什么,这是决定算术的因素(对于大于
int
的值,较小的类型会自动升级为
int
).我用
double
替换了程序中的所有类型,以查看真正的答案。它们是15739315070和225521104908,与您得到的任何答案都不匹配。结论:您的代码有符号整数溢出,这是一种未定义的行为。当优化更改了您的程序的可观察行为时,通常是因为您在某个地方有未定义的行为。如果您使用
clang-fsanitize=undefined
编译,您的程序会准确地告诉您问题所在:
example.c:9:33:运行时错误:有符号整数溢出:16900*7654321不能用“int”类型表示。
谢谢您的回答。我认为有符号整数总是像无符号整数一样包装。然而,包装是我在这里实际期望的东西,因为我从java移植了一些代码,据我所知,这些代码是整数包装的。
const int64_t e = a + (int64_t)b * b * p1 + (int64_t)b * p2 + (int64_t)c * c * p3 + (int64_t)c * p4;