The four-letter word that made me avoid computers and programming for far too long is math.
I’m not good at math. Or I wasn’t good. But then I realized that computer programming isn’t just another complicated way to frustrate oneself with math. It’s different! This difference doesn’t mean, however, that programming and math never intersect. A case in point is the square root of -1.
A square root is the inverse of a square. So 22 = 4. And √4 = 2, which can also be written as 4½ = 2.
But what about -4? Which value when multiplied by itself equals -4? It can’t be -2, because -2 × -2 = 4; a negative value multiplied by a negative value is a positive value. Also, -2 × 2 are two different values.
To avoid killing each other, mathematicians devised the constant i, the imaginary unit. This value is defined as the square root of -1, or √-1. So, √-4 = 2i. In the end, all the math works out well and the lives of thousands of mathematicians have been spared.
But what about your C programs?
Consider the following:
2025_05_03-Lesson-a.c
#include <stdio.h> #include <math.h> int main() { double i; i = sqrt((double)-1.0); printf("i = %f\n",i); return 0; }
This code attempts to find the square root of -1.0. The sqrt() function is called, which means you must link in the math library, -lm
when compiling this code at the command prompt in Linux.
A printf() statement outputs the result, stored in variable i
:
i = -nan
The value of variable i
is -nan
, or negative “not a number.” Darn.
Not being one to give up, I tried a second approach with this code:
2025_05_03-Lesson-b.c
#include <stdio.h> #include <math.h> int main() { double i,x; x = pow( sqrt((double)-1.0),(double)2.0); printf("i^2 = %f\n",x); return 0; }
The goal of this code is to raise -1 to the second power, to square -1. The result should be 1. Can the computer handle it?
Here’s the output:
i^2 = -nan
Nope. Again, the result is negative not-a-number.
In both cases, the computer is doing exactly what it’s told. As floating point values are (mostly) accurate, the result can’t be defined, so a nan
(not a number) exception is thrown. This condition doesn’t mean that all hope is lost.
It’s entirely possible for the C compiler to handle complex numbers. I explain how in next week’s Lesson.
where I am currently working on the same point in another mission:
– sqrt( -1.0 ) is an, extremely slow, variant to generate a NAN ( my compiler prefers NAN over nan ), here 14 to 83 readtsc() cycles instead of ~1 for x1d = NAN,
– IMHO a weak point in sqrt code, a “= 0 )
? sqrt( x )
: strcat(snprintf(sqrt( -x )),i);
or {sqrt(-x), 1.0(i)} ( pseudocode! ) would produce mathematically useful output, but you don’t need to knit it yourself, since C99 there should be a “complex.h” header that provides the nonsense of the shelf.
My questions: why do 0 * INFNITY, 0 / 0 and sqrt( -1.0 ) result in : -NAN ( -nan ), what is negative about it?
And second problem, performance: x1d = NAN as direct call: 1 cycle, as function setnan(): return( NAN ); : 1 cycle, setnan as prototype and “header only” function in *. h file: 1 cycle,
BUT: as a function in a static library: 8 to 15 cycles for direct assignments and 7 to 50 for the computing variants, and in a dynamic ( shared ) library , 6 to 52 cycles for direct assignments, and 5 to 91 cycles for the computing variants,
So far understandable, libraries have overhead ( more than AI bots assume that state “minimal” ), BUT as soon as libraries are in play, also the “non-library” assignments become slower by a factor of 5 on average . … that’s not meaningful … IMHO. environment intel, linux, gcc.
>My questions: why do 0 * INFNITY, 0 / 0 and sqrt( -1.0 ) result in : -NAN ( -nan ), what is negative about it?
Probably because whatever the weird result, the sign bit in the value is set to negative. Just a guess.
«My questions: why do 0 * INFNITY, 0 / 0 and sqrt( -1.0 ) result in : -NAN ( -nan ), what is negative about it?»
By default, floating-point exceptions are usually masked. If the floating-point unit then encounters an operation that has, as mathematicians like to say, an indeterminate form, a special form of QNaN (quiet NaN) will be generated on x86 that Intel likes to refer to as an “QNaN Floating-Point Indefinite”. (In addition, such a value is also generated if too many floating values are pushed onto the x87 floating-point stack.)
The encoding for such an Indefinite NaN can be found in Table 4-3. Floating-Point Number and NaN Encodings, in Intelʼs Software Developerʼs Manual Volume 1: Basic Architecture. Under the blog entry Splitting a Decimal Value I have previously already tried to provide a listing of all the different bit patterns Intel describes for floating-point values… hereʼs the relevant part in corrected form (I made a mistake back then in that indefinite NaNs always come with their sign bit set to one):
s bias_exp significand [Intel SDM, Table 4-3]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1 11111111 10000000000000000000000 QNaN Indefinite (e.g. result of 0/0)
So, this 1 (sign bit) has no real significance, is only part of the encoding (this goes for other NaNs as well, where the value of the sign bit is shown as ? in Intelʼs documentation).
Of course, if floating-point exceptions are not masked, i.e. if feenableexcept() or, on Windows, _control87() has been called,
#if defined(_MSC_VER)
_control87 (0, _EM_INVALID);
#else
feenableexcept(FE_ALL_EXCEPT);
#endif
then doing any of these operations should result in a SIGFPE signal.
I canʼt comment on the observed performance differences with regards to the described setnan() function.
Thank you, so we have to use as it is, we won’t get the idea behind Intels definitions. IEEE 754 doesn’t use negative NaN’s as far as I know, but leaves to implementers what to do with bits not defined.
The performance issue has turned into a horrible mix of compiler cheating, wrong commands, missing flags, remnants of prior compiles … seems solved for the moment.
If someone needs a template to compile into different levels of code, headers, libraries, performance check and use libdfp ( IEEE decimal datatypes ) a “try to” in https://gitlab.gnome.org/newbie-02/dec_perf/-/tree/main/performance_breakdown_reg_libraries, feedback welcome.