Floating point order and optimisation

When refactoring a particular piece of C++ code that converted radians to degrees, it was suggested by a friend that I swap the order of the operations in the function as it would increase performance. I thought it would be interesting to actually see if this was the case, consider;

define PI 3.1415

double torad1(double n) {
    return n * 180 / PI;
}

The assembler code produced by g++ 4.8 looks like this:

.LCPI0_0:
    .quad   4640537203540230144 # double 180
.LCPI0_1:
    .quad   4614256447914709615 # double 3.1415
torad1(double):
    mulsd   .LCPI0_0(%rip), %xmm0
    divsd   .LCPI0_1(%rip), %xmm0
    ret

Notice the presence of a division operation. The compiler has conformed with what it has been taught: that floating point maths is not associative. The interesting part is that by swapping the order of the operations, the two constants can be folded;

define PI 3.1415

double torad2(double n) {
    return 180 / PI * n;
}

Which produces assembler like this;

.LCPI1_0:
    .quad   4633260719236591239 # double 57.29746936176985
torad2(double):
    mulsd   .LCPI1_0(%rip), %xmm0
    ret

Notice how it has converted the division into a single multiplication of n by 180 / PI? This is an interesting subtlety that I will be paying more attention to when I write code from now on. Explicit brackets around the constant terms will also work.

If you’re using gcc/clang and would like to see how much faster your code would be if floating point operations were associative, but you’d rather not rewrite any code, you can compile using the -ffast-math flag, which disables this behaviour.