I have a C program which includes math.h
and makes use of the sqrt
function from that header. Very strangely, when I do not pass the -Ofast
flag, my code does not compile.
If I use the following to compile my code:
gcc -std=c99 foo.c
Either by itself, or add any of -O1
, -O2
or -Os
(those are uppercase O's) to that command, I get the following error:
/tmp/ccAcT2Bz.o: In function `sum_of_divisors':
foo.c:(.text+0xb): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
-O3
gives a similar, but more elaborate error (note that I don't call sqrt
within main
):
/tmp/ccBKvvFS.o: In function `sum_of_divisors':
foo.c:(.text+0x5c): undefined reference to `sqrt'
/tmp/ccBKvvFS.o: In function `main':
foo.c:(.text.startup+0xe5): undefined reference to `sqrt'
foo.c:(.text.startup+0xf3): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
However, -Ofast
compiles without error and the program runs perfectly. So,
- Why does this happen? Why must a certain optimization level be enabled for it to compile? Is it a GCC bug?
- How can I fix it, if I choose not to use
-Ofast
?
I'll try to phrase this as an answer based on the comments I've provided.
Essentially -ffast-math
allows for mathematical 'optimizations' that do not conform to the IEEE-754 standard. Some examples includes allowing floating-point operations to obey the laws of associativity, e.g., they behave like 'real' numbers: (a + b) + c == a + (b + c)
- and this is not a correct assumption with floating-point numbers. You can look at the man page for gcc
to see the options that -ffast-math
enables.
The option also allows for other code generation options that depart from the IEEE-754 standard. Operations that should raise exceptions, signalling NaNs, etc., may not be honoured. The example in the comments was sqrt
; if we pass a negative value to sqrt
, the results may not conform to the IEEE-754 standard. Trying to find the source of these inconsistencies far outweighs any benefit on modern processors. Modern CPUs have massive floating point resources, and correctness is far more important than any misplaced sense of efficiency.
There are very real examples of where honouring the associative property of real numbers when dealing with floating-point numbers leads to incorrect results. One example is Kahan summation. It relies on the non-associative property of floating-point arithmetic. There are other examples where careful analysis of numeric algorithms rely on IEEE-754 properties. Another example is Heron's formula for the area of a triangle.
Numerical analysis is a broad field, and the IEEE-754 standards represent a very careful and well-researched effort to standardize the idiosyncratic behaviour of floating-point operations, and their deviation from the naive ideal of 'real' numbers. It represents a massive effort over decades of research and experience (not to mention frustration) in numerically intensive computation.
There are people who frequently answer floating-point questions on this site with a much broader knowledge of the topic than I have. I just hope to convince you that -ffast-math
is simply ill-advised in many cases (often an algorithm with better numerical conditioning is a better first step), and introduce sources of error that are extremely difficult to find, with results that are often impossible to reproduce on other platforms. Avoid it like the plague.
The math library must be linked in when building the executable
so you need to compile with -lm
option.