为什么编译器不能优化浮点数与0的加法?

9 浏览
0 Comments

为什么编译器不能优化浮点数与0的加法?

我有四个身份函数,基本上什么都不做。只有与1相乘的乘法可以被clang优化为单个ret语句。

float id0(float x) {
    return x + 1 - 1;
}
float id1(float x) {
    return x + 0;
}
float id2(float x) {
    return x * 2 / 2;
}
float id3(float x) {
    return x * 1;
}

以下是编译器的输出结果:(clang 10,在-O3优化级别下)

.LCPI0_0:
        .long   1065353216              # float 1
.LCPI0_1:
        .long   3212836864              # float -1
id0(float):                                # @id0(float)
        addss   xmm0, dword ptr [rip + .LCPI0_0]
        addss   xmm0, dword ptr [rip + .LCPI0_1]
        ret
id1(float):                                # @id1(float)
        xorps   xmm1, xmm1
        addss   xmm0, xmm1
        ret
.LCPI2_0:
        .long   1056964608              # float 0.5
id2(float):                                # @id2(float)
        addss   xmm0, xmm0
        mulss   xmm0, dword ptr [rip + .LCPI2_0]
        ret
id3(float):                                # @id3(float)
        ret

我可以理解为什么不能优化id0id2。它们增加了一个值,可能会变成正无穷大,第二个操作将无法将其改回。

但是为什么不能优化id1?与无穷大相加会得到无穷大,与任何常规数字相加都会得到该数字,与NaN相加会得到NaN。那么为什么它不是像* 1那样的“真正”的身份运算。

使用Compiler Explorer查看示例

0
0 Comments

浮点数的加法与0的优化问题

IEEE 754浮点数有两个零值,一个是负零,一个是正零。当它们相加时,结果是正零。

所以id1(-0.f)等于0.f,而不是-0.f

需要注意的是,id1(-0.f) == -0.f,因为0.f == -0.f

演示链接

此外,需要注意的是,在GCC中使用-ffast-math编译选项可以进行优化,并改变结果。

可惜,你回答得更快。这是一个用于说明的godbolt演示链接

写得很好。我也想回答,但是你更清晰地表达了这个情况。

0
0 Comments

浮点数是计算机中用来表示实数的一种数值类型。然而,在使用浮点数进行计算时,可能会出现一些问题,其中之一就是浮点数与0进行相加时,编译器无法进行优化。本文将探讨这个问题的原因以及解决方法。

首先,我们需要了解IEEE 754浮点数标准。该标准定义了浮点数的表示方法和计算规则。在IEEE 754中,浮点数由三个部分组成:符号位、指数位和尾数位。浮点数的范围和精度都是有限的,因此在进行计算时可能会产生舍入误差。

在C语言和C++语言中,浮点数的计算通常由编译器完成。编译器在进行浮点数计算时会进行优化,以提高计算的效率。然而,当浮点数与0进行相加时,编译器无法进行优化。

这是因为0在IEEE 754中有多种表示方式。除了正常的0之外,还有负0。虽然这两个值在数学上是相等的,但在浮点数的表示中它们是不同的。正常的0表示为全零位,而负0表示为全零位并设置符号位为负。因此,当浮点数与0进行相加时,编译器无法确定要使用哪个0进行计算,从而无法进行优化。

为了解决这个问题,可以使用以下方法之一:

1. 避免使用浮点数与0进行相加的操作,而是使用其他方式实现相同的计算结果。

2. 使用特定的浮点数库,如GMPlib,来进行浮点数计算。这些库提供了更高精度的浮点数计算,可以避免舍入误差和优化问题。

总之,由于浮点数与0进行相加时编译器无法进行优化,可能会导致计算结果不准确。为了解决这个问题,可以采用其他方法实现相同的计算结果,或者使用特定的浮点数库进行计算。

0
0 Comments

编译器为什么不能优化浮点数加法与0的问题?

浮点数的加法不等于加0,而是等于加1然后减1,即`(x + 1) - 1`。如果`x`非常小,那么在`x + 1`的步骤中,可能会丢失这个非常小的部分,编译器无法知道这是否是你的意图。

同样,在`x * 2 / 2`的情况下,`x * 2`也可能不是精确的,这是由于浮点精度造成的。因此,你也面临类似的情况,编译器无法知道你是否有某种原因要以这种方式更改`x`的值。

所以以下代码是相等的:

float id0(float x) {
    return x + (1. - 1.);
}
float id1(float x) {
    return x + 0;
}

同样,以下代码也是相等的:

float id2(float x) {
    return x * (2. / 2.);
}
float id3(float x) {
    return x * 1;
}

当然,可以以不同的方式定义预期的行为。但正如Nelfeal所提到的,必须使用`-ffast-math`显式激活此优化。

`-ffast-math`是对于clang和gcc而言的一个特殊集合(这里列出了clang的一个选项):

-fno-honor-infinities
-fno-honor-nans
-fno-math-errno
-ffinite-math
-fassociative-math
-freciprocal-math
-fno-signed-zeros
-fno-trapping-math
-ffp-contract=fast

`x * 2`可能不是精确的,但不是因为浮点精度的问题,而是因为其范围:如果`x`过大,`x * 2`可能会产生无穷大。而`x * 2.`的情况较少发生这种情况,因为`2.`的类型是`double`,而`double`通常具有比`float`更大的范围。

0