我有下面的代码,它完成了从16bpp图像到1bpp图像的非常基本的转换,代码按照预期运行,直到我启用了编译器优化,在这一点上我只得到了一个黑色图像。
#define RSCALE 5014709
#define GSCALE 9848225
#define BSCALE 1912602
uint16_t _convertBufferTo1bit(uint8_t* buffer, uint16_t size)
{
uint8_t* dst_ptr = buffer;
uint8_t* end_ptr = buffer + size;
uint16_t pos = 0;
uint8_t r, g, b, i;
uint32_t lum;
while(buffer < end_ptr)
{
for(i = 8; i > 0; i--)
{
r = (*buffer & 0xF8);
g = ((*buffer & 0x07) << 5);
buffer += 1;
g |= (*buffer & 0x03);
b = ((*buffer & 0x1F) << 3);
buffer += 1;
lum = ((RSCALE * r) + (GSCALE * g) + (BSCALE * b));
if(lum > 0x7FFFFFFF)
{
//White
dst_ptr[pos] |= (1 << (i-1));
}
else
{
//black
dst_ptr[pos] &= ~(1 << (i-1));
}
}
pos++;
}
return pos;
}
在查看反编译的程序集时,我可以看到编译器已经删除了if(lum > 0x7FFFFFFF)
语句和所有相关的计算。有人能告诉我为什么吗?
-O0 -std=c++17 -Wall -Wextra
https://godbolt.org/z/GhPezzh33
-O1 -std=c++17 -Wall -Wextra
发布于 2021-11-21 04:00:47
在此代码中:
lum = ((RSCALE * r) + (GSCALE * g) + (BSCALE * b));
if(lum > 0x7FFFFFFF)
RSCALE
、GSCALE
和BSCALE
分别是5014709
、9848225
和1912602
。假设在使用的C实现中int
是32位,这些都是int
常量。
r
、g
和b
都是uint8_t
类型,因此它们在乘法中被提升为int
。
则((RSCALE * r) + (GSCALE * g) + (BSCALE * b))
完全是使用int
操作数生成int
结果的计算。因此,编译器可以看到lum
被分配了int
结果的值,并且它有权假定结果在INT_MIN
到INT_MAX
的范围内。此外,它可以看到所有操作数都是非负的,因此不可能产生负结果,从而将可能的范围缩小到0
到INT_MAX
。这排除了将负值指定给uint32_t
会导致包络到高值的可能性。因此,编译器可能会假设lum > 0x7FFFFFFF
永远不是真的。
计算可能会溢出int
,然后行为是未定义的,编译器仍然允许使用假设。
要更正此问题,请将每个乘法的至少一个操作数更改为unsigned
。
https://stackoverflow.com/questions/70054164
复制相似问题