Why can the compiler not optimize floating point addition with 0? [duplicate]

眉间皱痕 提交于 2020-06-10 02:25:12

问题


I have four identity functions which do essentially nothing. Only multiplication with 1 could be optimized by clang to a single ret statement.

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;
}

And the following compiler output is: (clang 10, at -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

I can understand why id0 and id2 can't be optimized. They increase the value which could then turn into positive infinity and the second operation would not change it back.

But why can't id1 be optimized? Additon with infinity would yield infinity, addition with any regular number would yield that number and addition with NaN would yield NaN. So why is it not a "true" identity operation like * 1.

Example with Compiler Explorer


回答1:


IEEE 754 floating-point numbers have two zero values, one negative, one positive. When added together, the result is the positive one.

So id1(-0.f) is 0.f, not -0.f.
Note that id1(-0.f) == -0.f because 0.f == -0.f.

Demo

Also, note that compiling with -ffast-math in GCC does make the optimization and changes the result.




回答2:


"I have four identity functions which do essentially nothing."

That's not true.

For floating-point numbers x + 1 - 1 is not equal x + 0, it is equal (x + 1) - 1. So if you have e.g. a very small x then you will lose that very small portion in the x + 1 step, and the compiler can't know if that was your intent or not.

And in the case of x * 2 / 2, the x * 2 might not be exact either, due to floating-point precision, so you have a similar case here, the compiler does not know if you for some reason want to change the value of x in that manner.

So these would be equal:

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

float id1(float x) {
    return x + 0;
}

And these would be equal:

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

float id3(float x) {
    return x * 1;
}

The desired behavior could for sure be defined in another way. But as already mentioned by Nelfeal this optimization has to be explicitly activated using -ffast-math

Enable fast-math mode. This option lets the compiler make aggressive, potentially-lossy assumptions about floating-point math. These include:

  • Floating-point math obeys regular algebraic rules for real numbers (e.g. + and * are associative, x/y == x * (1/y), and (a + b) * c == a * c + b * c),
  • Operands to floating-point operations are not equal to NaN and Inf, and
  • +0 and -0 are interchangeable.

fast-math is for clang and gcc a collection of flags (here the one listed by 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



回答3:


Read the floating-number-gui.de web page, more about IEEE 754, the C11 standard n1570, the C++11 standard n3337.

float id1(float x) {
    return x + 0;
}

If x happens to be a signaling NaN, your id1 might even not return (and probably should not return).

If x is a quiet NaN, then id1(x) != x since NaN != NaN (at least NaN == NaN should be false).

In some cases, you want costly arbitrary precision arithmetic. Then consider using GMPlib.

PS. Floating point numbers can give you nightmares or a PhD, at your choice. They sometimes kill people or at least make huge financial disasters (e.g. a loss of several hundred millions US$ or €).



来源:https://stackoverflow.com/questions/62129248/why-can-the-compiler-not-optimize-floating-point-addition-with-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!