PWR046: Replace two divisions with a division and a multiplication
Issue
Divisions are expensive operations on modern hardware, so replacing them with cheaper operations can often improve performance.
Actions
Replace a double division with a division and a multiplication.
Relevance
Double divisions can be replaced with a division and multiplication, according to the following patterns:
-
(a / b) / c = a / (b * c)
. -
a / (b / c) = (a * c) / b
.
Although these transformations are mathematically equivalent, floating-point
arithmetic can introduce slight differences in results due to precision
limits. Modern compilers can often apply similar optimizations automatically
when explicitly instructed with special compilation flags, such as gcc
's
and gfortran
's -funsafe-math-optimizations
.
Code example
C
Have a look at the following code:
// example.c
#include <stdio.h>
__attribute__((const)) float example(float a, float b, float c) {
return a / b / c;
}
int main() {
printf("Result: %0.7f\n", example(1.23, 1.41, 1.89));
return 0;
}
Compiling and running this code produces the following result:
$ gcc --version
gcc (Debian 12.2.0-14) 12.2.0
$ gcc example.c -o example
$ ./example
Result: 0.4615558
The expression a / b / c
can be rewritten with a single division and a
multiplication:
// solution.c
...
__attribute__((const)) float example(float a, float b, float c) {
return a / (b * c);
}
...
When compiled and run, this optimized code yields the same result:
$ gcc solution.c -o solution
$ ./solution
Result: 0.4615558
Fortran
Have a look at the following code:
// example.f90
program main
implicit none
print *, "Result:", example(1.23, 1.41, 1.89)
contains
function example(a, b, c)
use iso_fortran_env, only: real32
implicit none
real(kind=real32), intent(in) :: a, b, c
real(kind=real32) :: example
example = a / b / c
end function example
end program main
Compiling and running this code produces the following result:
$ gfortran --version
GNU Fortran (Debian 12.2.0-14) 12.2.0
$ gfortran example.c -o example
$ ./example
Result: 0.461555779
The expression a / b / c
can be rewritten with a single division and a
multiplication:
// solution.f90
...
example = a / (b * c)
end function example
...
When compiled and run, the result remains very close to the original, with negligible differences due to the precision limitations of floating-point arithmetic:
$ gfortran solution.c -o solution
$ ./solution
Result: 0.461555809
Related resources
References
-
"Instruction tables", Agner Fog. [last checked December 2024]
-
"IEEE Arithmetic", Sun Microsystems, Inc. [last checked December 2024]
-
"Semantics of Floating Point Math in GCC", Free Software Foundation, Inc. [last checked December 2024]
-
"Floating-point arithmetic - Accuracy problems", Wikipedia Contributors. [last checked December 2024]