gcc v8-v10, undefined reference to `CMPLX': what to do? - Page 1
Forum section: electronics / general

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 02:50:10 pm
content: it looks like making sure the macro is defined is not up to GCC, and it sounds like the { compiler{gcc-v8, gcc-v9, gcc-v10} , platform{linux/x86-32bit, linux/mips32r2be-32bit, linux/ppc32be-32bit, linux/ppc64be-32bit} combination doesn't support it.Code:- compiling complex ... complex.c: In function 'hh2':complex.c:72:12: warning: implicit declaration of function 'CMPLX' [-Wimplicit-function-declaration]72 | cplx = CMPLX(real, imag);| ^~~~~complex.c:74:44: warning: implicit declaration of function 'cargf' [-Wimplicit-function-declaration]74 | printf("Phase Angle = %.1f radians\n", cargf(cplx));| ^~~~~complex.c:74:44: warning: incompatible implicit declaration of built-in function 'cargf'complex.c:11:1: note: include '<complex.h>' or provide a declaration of 'cargf'10 | #include <stdio.h>+++ |+#include <complex.h>11 |But that's really weirdCode:-std=c99 -Wall -WextraCode:-std=c11 -Wall -WextraTried c99/c11, same result.Code:#include <complex.h>#include <tgmath.h>#include <math.h>#include <stdio.h>typedef _Complex float zf_t;typedef _Complex double zd_t;typedef _Complex long double zld_t;void hh2(){double real;double imag;zd_t cplx;real = 1.3;imag = 4.9;cplx = CMPLX(real, imag);printf("Phase Angle = %.1f radians\n", cargf(cplx));}...

author: grumpydoc
author_rank: Super Contributor
timestamp: February 24, 2022, 03:16:31 pm
content: Native or cross compile?It works on my Fedora 34 box with -m32 producing a valid i686 binary.

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 04:19:36 pm
content: Quote from: grumpydoc on February 24, 2022, 03:16:31 pmNative on all the mentioned platforms.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 24, 2022, 05:20:09 pm
content: Which C library and C library version?CMPLX()is C11, not C99, so use-std=c11only.Try including<math.h>before<complex.h>. Because of the interdependencies, that might matter even though it shouldn't.

author: grumpydoc
author_rank: Super Contributor
timestamp: February 24, 2022, 05:46:30 pm
content: Quote from: Nominal Animal on February 24, 2022, 05:20:09 pmWhich Linux distribution as well, there are not many that are natively 32 bit any longer, at least on Intel.

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 06:22:39 pm
content: Quote from: Nominal Animal on February 24, 2022, 05:20:09 pm/usr/lib/libc.{a,so} -> glibc/usr/lib/libm.{a,so} -> glibcglibc-v2.32-r5libtool-v2.4.6-r6 (just recompiled)I rebuilt libtool, and gcc-v10 with the last patch-setQuote from: Nominal Animal on February 24, 2022, 05:20:09 pmokQuote from: Nominal Animal on February 24, 2022, 05:20:09 pmI commented "tgmath" because it causes some issuesCode:#include <math.h>#include <complex.h>//#include <tgmath.h>#include <stdio.h>now Gcc-v10 compiles, even if ...Code:sizeof(_Complex float) = 8sizeof(_Complex double) = 16sizeof(_Complex long double) = 24I = 0.0+1.0iI * I = -1.0+0.0ipow(I, 0) = 1.0+0.0ipow(I, 1) = 0.0+0.0ipow(I, 2) = 0.0+0.0ipow(I, 3) = 0.0+0.0iexp(I*PI) = 1.0+0.0i(1+2i)*(1-2i) = 5.0+0.0iPhase Angle = 1.3 radians(tested on Linux/x86-32bit)It seems something is bad: pow(I,2) shouldn't be zeroCode:typedef _Complex float zf_t;typedef _Complex double zd_t;typedef _Complex long double zld_t;void hh1(){zd_t z1;zd_t z2;zd_t z3;zd_t z4;zd_t z5;double PI;z1 = I; // imaginary unitprintf("I = %.1f%+.1fi\n", creal(z1), cimag(z1));z1 = I * I; // imaginary unit squaredprintf("I * I = %.1f%+.1fi\n", creal(z1), cimag(z1));z2 = 1 * I;z2 = pow(z2, 0);printf("pow(I, 0) = %.1f%+.1fi\n", creal(z2), cimag(z2));z2 = 1 * I;z2 = pow(z2, 1);printf("pow(I, 1) = %.1f%+.1fi\n", creal(z2), cimag(z2));z2 = 1 * I;z2 = pow(z2, 2); // imaginary unit squaredprintf("pow(I, 2) = %.1f%+.1fi\n", creal(z2), cimag(z2));z2 = 1 * I;z2 = pow(z2, 3);printf("pow(I, 3) = %.1f%+.1fi\n", creal(z2), cimag(z2));PI = acos(-1);z3 = exp(I * PI); // Euler's formulaprintf("exp(I*PI) = %.1f%+.1fi\n", creal(z3), cimag(z3));z4 = 1+2*I;z5 = 1-2*I; // conjugatesprintf("(1+2i)*(1-2i) = %.1f%+.1fi\n", creal(z4*z5), cimag(z4*z5));}edit:So, there was something wrong with libtool! You have to be careful when you cook gcc >=v10

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 06:26:22 pm
content: pow(I, 0) = 1.0+0.0i: correctpow(I, 1) = 0.0+0.0i: wrongpow(I, 2) = 0.0+0.0i: wrongpow(I, 3) = 0.0+0.0i: wrongwithCode:#include <math.h>#include <complex.h>#include <tgmath.h> // do I have to comment this?!?#include <stdio.h>there is this issueCode:- compiling cplx ... In file included from cplx.c:9:cplx.c: In function 'hh1':cplx.c:55:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'55 | z2 = pow(z2, 0);| ^~~cplx.c:59:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'59 | z2 = pow(z2, 1);| ^~~cplx.c:63:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'63 | z2 = pow(z2, 2); // imaginary unit squared| ^~~cplx.c:67:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'67 | z2 = pow(z2, 3);| ^~~cplx.c:70:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'70 | PI = acos(-1);| ^~~~cplx.c:72:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'72 | z3 = exp(I * PI); // Euler's formula| ^~~make: *** [Makefile:77: obj/macmini-intel-i686/cplx.o] Error 1

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 06:30:59 pm
content: Quote from: grumpydoc on February 24, 2022, 05:46:30 pmGentoo/Catalyst, so they are autobuilt stage0-4I am going to also build for ARM(1)-64bit and HPPA2-32bit(1) ARMv7-gnueabihf (hardware fp)

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 24, 2022, 06:33:38 pm
content: Yes, this macro appeared in C11, it was not defined in C99, so that's the minimum revision you need to use.With that said - I tried with the latest GCC (11.2), and the macro is indeed not supported. Even with std=c2x.So out of curiosity, I did a search in all header files from all GCC compilers I have on my workstation, with a variety of versions. None have the 'CMPLX' macro defined in any of their header files.I have no explanation for this, apart from just a missing macro in GCC's complex.h. Didn't check for Clang yet. Reading the standard (both C11 and the draft of C2x), there is no mention of it being possibly optional.Note that the suggested way of achieving the same (while actually being more readable IMHO, since closer to math notation) before this macro appeared was to use the 'I' macro from complex.h (which is normally defined as '_Complex_I'), such as:Code:double complex x = 1 + 2*I;which I tend to prefer anyway.

author: grumpydoc
author_rank: Super Contributor
timestamp: February 24, 2022, 06:39:59 pm
content: Quote from: SiliconWizard on February 24, 2022, 06:33:38 pmcomplex.h is part of glibc, rather than GCC.

author: magic
author_rank: Super Contributor
timestamp: February 24, 2022, 06:45:15 pm
content: Works for meCode:$ grep -C2 CMPLX /usr/include/complex.h#if defined __USE_ISOC11 && __GNUC_PREREQ (4, 7)/* Macros to expand into expression of specified complex type. */# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y))# define CMPLXF(x, y) __builtin_complex ((float) (x), (float) (y))# define CMPLXL(x, y) __builtin_complex ((long double) (x), (long double) (y))#endif#if __HAVE_FLOAT16 && __GLIBC_USE (IEC_60559_TYPES_EXT)# define CMPLXF16(x, y) __builtin_complex ((_Float16) (x), (_Float16) (y))#endif#if __HAVE_FLOAT32 && __GLIBC_USE (IEC_60559_TYPES_EXT)# define CMPLXF32(x, y) __builtin_complex ((_Float32) (x), (_Float32) (y))#endif#if __HAVE_FLOAT64 && __GLIBC_USE (IEC_60559_TYPES_EXT)# define CMPLXF64(x, y) __builtin_complex ((_Float64) (x), (_Float64) (y))#endif#if __HAVE_FLOAT128 && __GLIBC_USE (IEC_60559_TYPES_EXT)# define CMPLXF128(x, y) __builtin_complex ((_Float128) (x), (_Float128) (y))#endif#if __HAVE_FLOAT32X && __GLIBC_USE (IEC_60559_TYPES_EXT)# define CMPLXF32X(x, y) __builtin_complex ((_Float32x) (x), (_Float32x) (y))#endif#if __HAVE_FLOAT64X && __GLIBC_USE (IEC_60559_TYPES_EXT)# define CMPLXF64X(x, y) __builtin_complex ((_Float64x) (x), (_Float64x) (y))#endif#if __HAVE_FLOAT128X && __GLIBC_USE (IEC_60559_TYPES_EXT)# define CMPLXF128X(x, y) \__builtin_complex ((_Float128x) (x), (_Float128x) (y))#endifI suppose you could use __builtin_complex as a substituteQuote from: grumpydoc on February 24, 2022, 06:39:59 pmYep, that's the case on my system too.

author: newbrain
author_rank: Super Contributor
timestamp: February 24, 2022, 08:19:51 pm
content: Quote from: DiTBho on February 24, 2022, 06:26:22 pmIf<tgmath.h>is not included the above results are exactly what expected, a _Complex value converted to regular float or double simple loses the imaginary part - so 0 is what the (double) pow() sees.Along NominalAnimal lines, I would suggest to try includingonly<tgmath.h>: it already includes both <math.h> and <complex.h>.Correctly written includes should be idempotent, but the matter here is complex...EtA: tgmath.h defines the type generic math library functions and macros - a basic form of overloading that can now (>=C11) be written in standard C using _Generic.So, complex.h will contain the complex specific stuff and math.h the real specific ones.With tgmath.h generic pow calls will be replaced with the right one (powf, pow, powl, cpowf, cpow, cpowl) according to the arguments.

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 09:27:14 pm
content: Quote from: newbrain on February 24, 2022, 08:19:51 pmCode://#include <math.h>//#include <complex.h>#include <tgmath.h>#include <stdio.h>typedef _Complex float zf_t;typedef _Complex double zd_t;typedef _Complex long double zld_t;void hh1(){zd_t z1;zd_t z2;zd_t z3;zd_t z4;zd_t z5;double PI;z1 = I; // imaginary unitprintf("I = %.1f%+.1fi\n", creal(z1), cimag(z1));z1 = I * I; // imaginary unit squaredprintf("I * I = %.1f%+.1fi\n", creal(z1), cimag(z1));z2 = 1 * I;z2 = pow(z2, 0);printf("pow(I, 0) = %.1f%+.1fi\n", creal(z2), cimag(z2));z2 = 1 * I;z2 = pow(z2, 1);printf("pow(I, 1) = %.1f%+.1fi\n", creal(z2), cimag(z2));z2 = 1 * I;z2 = pow(z2, 2); // imaginary unit squaredprintf("pow(I, 2) = %.1f%+.1fi\n", creal(z2), cimag(z2));z2 = 1 * I;z2 = pow(z2, 3);printf("pow(I, 3) = %.1f%+.1fi\n", creal(z2), cimag(z2));PI = acos(-1);z3 = exp(I * PI); // Euler's formulaprintf("exp(I*PI) = %.1f%+.1fi\n", creal(z3), cimag(z3));z4 = 1+2*I;z5 = 1-2*I; // conjugatesprintf("(1+2i)*(1-2i) = %.1f%+.1fi\n", creal(z4*z5), cimag(z4*z5));}Code:- compiling cplx ... In file included from cplx.c:9:cplx.c: In function 'hh1':cplx.c:55:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'55 | z2 = pow(z2, 0);| ^~~cplx.c:59:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'59 | z2 = pow(z2, 1);| ^~~cplx.c:63:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'63 | z2 = pow(z2, 2); // imaginary unit squared| ^~~cplx.c:67:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'67 | z2 = pow(z2, 3);| ^~~cplx.c:70:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'70 | PI = acos(-1);| ^~~~cplx.c:72:10: error: duplicate type-generic parameter type for function argument 2 of '__builtin_tgmath'72 | z3 = exp(I * PI); // Euler's formula| ^~~make: *** [Makefile:77: obj/macmini-intel-i686/cplx.o] Error 1

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 24, 2022, 10:08:48 pm
content: Quote from: grumpydoc on February 24, 2022, 06:39:59 pmNo. complex.h is part of the C std, and *is* provided by GCC.Do not confuse particular file layouts/distributions on particular systems with what is standard C. Complex numbers are part of the C std when supported, glibc is just a particular implementation.GCC absolutely does come with complex.h. The boggling part is why CMPLX is not defined in it, while basic support of complex numbers is. Actually, it looks like complex.h in GCC never got updated past C99 support, although I have no 100% certainty about it.But this is certainly a bug. The std clearly states:Quote*'complex.h'On all configurations I tried (various GCC versions, various targets), this macro is NOT defined, 'complex.h' exists, but it does not define the CMPLX macro. It does define the rest of complex numbers support as far as I've seen though, at least the basic support - didn't thoroughly check whether anything in C11+ but not in C99, apart from this macro, wasn't there. There is no valid reason for that.If complex numbers are not supported on a given platform/version/target, the '__STDC_NO_COMPLEX__ ' should just be defined, end of story. Otherwise, anything defined in the std for "Complex arithmetic" should be available. Something kinda fishy here. Looked for something related in GCC's bugzilla, but could not find anything very interesting, except a remotely related issue that apparently never got even assigned.

author: magic
author_rank: Super Contributor
timestamp: February 24, 2022, 10:24:34 pm
content: It's part of the standard library, like stdlib.h and a few others. It even makes sense.Perhaps you are looking at some C++ headers which do appear to ship with GCC itself.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 24, 2022, 10:43:17 pm
content: I'm used to using a number of different GCC versions for various targets, cross-compilation or native, and most of the time, those headers are part of the GCC's distribution. But digging a bit deeper, this would not be strictly a problem with GCC indeed.For instance, if you're using GCC for ARM-Cortex M, the standard headers are usually provided by newlib.If you're using other targets, it depends. But if you're not doing "native" compilation on Linux (or similar environment), there is often NOT a common 'include' path - each compiler has its own stuff for the std library. It may come from newlib, glibc or yet other implementations.So it looks like the problem lies with newlib - and some other implementations - rather than with GCC itself. I confused the two as they often come as a bundle - unless again you're strictly on native Linux.Why a number of implementations of the C srd lib, including newlib, do not define the CMPLX macro, is now the question.But what the compiler implements, and what the std lib implements, for complex numbers, is still a bit involved. There's of course basic support for complex numbers directly in the compiler itself, not requiring the std lib, for easy to understand reasons. (Otherwise you wouldn't have access to arithmetic on complex numbers with common operators in C, for instance. C does not have operator overloading. =) )

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 11:06:26 pm
content: collisionsgnat-gpl-v2016-r4 -> /usr/lib/gcc/i686-pc-linux-gnu/4.9.4/include/g++-v4/complex.hgcc-v6.5.0-r2 -> /usr/lib/gcc/i686-pc-linux-gnu/6.5.0/include/g++-v6/tr1/complex.hgcc-v6.5.0-r2 -> /usr/lib/gcc/i686-pc-linux-gnu/6.5.0/include/g++-v6/complex.hgcc-v7.5.0-r1 -> /usr/lib/gcc/i686-pc-linux-gnu/7.5.0/include/g++-v7/tr1/complex.hgcc-v7.5.0-r1 -> /usr/lib/gcc/i686-pc-linux-gnu/7.5.0/include/g++-v7/complex.hgcc-v8.4.0-r1 -> /usr/lib/gcc/i686-pc-linux-gnu/8.4.0/include/g++-v8/tr1/complex.hgcc-v8.4.0-r1 -> /usr/lib/gcc/i686-pc-linux-gnu/8.4.0/include/g++-v8/complex.hgcc-v9.3.0-r1 -> /usr/lib/gcc/i686-pc-linux-gnu/9.3.0/include/g++-v9/tr1/complex.hgcc-v9.3.0-r1 -> /usr/lib/gcc/i686-pc-linux-gnu/9.3.0/include/g++-v9/complex.hgcc-v10.2.0-r3 -> /usr/lib/gcc/i686-pc-linux-gnu/10.2.0/include/g++-v10/tr1/complex.hgcc-v10.2.0-r3 -> /usr/lib/gcc/i686-pc-linux-gnu/10.2.0/include/g++-v10/complex.hglibc-2.32-r5 -> /usr/include/complex.hclang-v11.0.0 -> /usr/lib/clang/11.0.0/include/openmp_wrappers/complex.h

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 24, 2022, 11:15:08 pm
content: Yes, it can become a mess if you're using common include paths (such as is usual on Linux), and compilers with their own version of the std library.And, this particular header file (complex.h) also comes with other things like openmp... =)In any case, I'd suggest sticking to the 'I' macro for defining complex constants, as I showed earlier. This macro is defined in complex.h on most plaforms I've tried, contrary to CMPLX.

author: newbrain
author_rank: Super Contributor
timestamp: February 24, 2022, 11:16:00 pm
content: Quote from: DiTBho on February 24, 2022, 09:27:14 pmCrap. I know it's not nice to say "works for me", but it works for me!Note that of course you need to compile as C11 to have access toCMPLX(), see below, and link with -lm.I used exactly your code, but withint main(void)instead of hh() and with an added z0 to test CMPLX.Code:newbrain@MUON:~$ gcc -std=c99 -Wall -Wextra -pedantic cpx.c -o cpx -lmcpx.c: In function ‘main’:cpx.c:20:10: warning: implicit declaration of function ‘CMPLX’ [-Wimplicit-function-declaration]20 | z0 = CMPLX(0,I);| ^~~~~cpx.c:12:10: warning: variable ‘z0’ set but not used [-Wunused-but-set-variable]12 | zd_t z0;| ^~/usr/bin/ld: /tmp/ccxcDS2j.o: in function `main':cpx.c:(.text+0x26): undefined reference to `CMPLX'collect2: error: ld returned 1 exit statusnewbrain@MUON:~$ gcc -std=c11 -Wall -Wextra -pedantic cpx.c -o cpx -lmcpx.c: In function ‘main’:cpx.c:12:10: warning: variable ‘z0’ set but not used [-Wunused-but-set-variable]12 | zd_t z0;| ^~newbrain@MUON:~$ gcc --versiongcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0Copyright (C) 2019 Free Software Foundation, Inc.This is free software; see the source for copying conditions. There is NOwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.newbrain@MUON:~$ ./cpxI = 0.0+1.0iI * I = -1.0+0.0ipow(I, 0) = 1.0+0.0ipow(I, 1) = 0.0+1.0ipow(I, 2) = -1.0+0.0ipow(I, 3) = -0.0-1.0iexp(I*PI) = -1.0+0.0i(1+2i)*(1-2i) = 5.0+0.0inewbrain@MUON:~$

author: newbrain
author_rank: Super Contributor
timestamp: February 24, 2022, 11:23:38 pm
content: Quote from: SiliconWizard on February 24, 2022, 10:08:48 pmIf you compile with -std=C11 the "macro" will magically appear, as demonstrated above.System include files must externally behave as if they are written in C (e.g. you can undefine a macro to make sure to get to a library function), but can use any trick the compiler writers fancy. The standard just describe how they work and what they provide, not their actualcodecontent - in theory (but here I'm not 100% sure) they could just contain a single #pragma that tells the compiler to make their definitions available.Edit: extended a bit my ramblings, as if someone needs them...

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 11:43:11 pm
content: ok, some progress:applied Gentoo Glibc Patchset 2.32-4-rc9 and rebuilt glibc-v2.32-r5 with gcc-v10.2.0now the above code compiles, and works as expected ...... but glibc has still some glitchesCode:# make interfaces- touching interface private- touching interface public- generating interface private for cplx/usr/include/bits/mathcalls-helper-functions.h:20: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:24: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:29: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:33: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:37: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:41: syntax error at token '__x'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:44: syntax error at token '__value'Expected: ')'- generating interface public for cplx/usr/include/bits/mathcalls-helper-functions.h:20: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:24: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:29: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:33: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:37: syntax error at token '__value'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:41: syntax error at token '__x'Expected: ')'/usr/include/bits/mathcalls-helper-functions.h:44: syntax error at token '__value'Expected: ')'glibc-2.32-r5 -> /usr/include/bits/mathcalls-helper-functions.hCode:/* Classify given number. */__MATHDECL_ALIAS (int, __fpclassify,, (_Mdouble_ __value), fpclassify)__attribute__ ((__const__));(/usr/include/bits/mathcalls-helper-functions.h)it doesn't understand what is "_Mdouble_"

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 11:46:56 pm
content: Code:# make- compiling cplx ... done- linking to appCode:# make runsizeof(data): 64alignof(data): 32sizeof(_Complex float) = 8sizeof(_Complex double) = 16sizeof(_Complex long double) = 24I = 0.0+1.0iI * I = -1.0+0.0ipow(I, 0) = 1.0+0.0ipow(I, 1) = 0.0+1.0ipow(I, 2) = -1.0+0.0ipow(I, 3) = -0.0-1.0iexp(I*PI) = -1.0+0.0i(1+2i)*(1-2i) = 5.0+0.0iPhase Angle = 1.3 radians

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 24, 2022, 11:51:16 pm
content: Std lib quirkiness aside, I was wondering lately whether using the complex types in C would be any more efficient than just hand-implementing complex operations with FP numbers. (Sure it's more convenient, but is it any more efficient?) Or is it even possibly less efficient? I'm curious!

author: DiTBho
author_rank: Super Contributor
timestamp: February 25, 2022, 12:03:20 am
content: Quote from: SiliconWizard on February 24, 2022, 11:51:16 pmConsidering that I have already recompiled glibc and gcc four times, I hope at least it will be more efficient

author: DiTBho
author_rank: Super Contributor
timestamp: February 25, 2022, 12:03:59 am
content: /usr/include/math.h -> includes <bits/mathcalls-helper-functions.h>Code:#define _Mdouble_ double#define __MATH_PRECNAME(name,r) __CONCAT(name,r)#define __MATH_DECLARING_DOUBLE 1#define __MATH_DECLARING_FLOATN 0#include <bits/mathcalls-helper-functions.h>(/usr/include/math.h)it defines Mdoble several times#define _Mdouble_ double# define _Mdouble_ float# define _Mdouble_ long double# define _Mdouble_ _Float16# define _Mdouble_ _Float32# define _Mdouble_ _Float64# define _Mdouble_ _Float128# define _Mdouble_ _Float32x# define _Mdouble_ _Float64x# define _Mdouble_ _Float128x

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 25, 2022, 12:28:04 am
content: Quote from: DiTBho on February 25, 2022, 12:03:20 amI admit I didn't take the time to compare this as of yet. I'll try to do that - at least look at the assembly - to get an idea.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 25, 2022, 01:31:09 am
content: So, just made a quick comparison for just one operation: multiplication. Optimization on. GCC 11.2.0, x86_64.Code:double complex cmul(double complex x, double complex y){return x*y;}typedef struct { double Re, Im; } complex_t;complex_t cmul2(complex_t x, complex_t y){return (complex_t){ .Re = x.Re*y.Re - x.Im*y.Im, .Im = x.Re*y.Im + x.Im*y.Re };}Ready for the resulting assembly?Code:cmul:subq$104, %rsp.seh_stackalloc104movups%xmm6, 64(%rsp).seh_savexmm%xmm6, 64movups%xmm7, 80(%rsp).seh_savexmm%xmm7, 80.seh_endprologuemovsd8(%r8), %xmm4movsd8(%rdx), %xmm2movsd(%r8), %xmm3movsd(%rdx), %xmm1movapd%xmm2, %xmm5movapd%xmm4, %xmm0movapd%xmm2, %xmm7unpcklpd%xmm3, %xmm0unpcklpd%xmm5, %xmm5movapd%xmm3, %xmm6mulpd%xmm0, %xmm5movapd%xmm1, %xmm0unpcklpd%xmm4, %xmm6movq%rcx, %raxmulsd%xmm3, %xmm7unpcklpd%xmm0, %xmm0mulpd%xmm6, %xmm0movapd%xmm0, %xmm6addpd%xmm5, %xmm0subpd%xmm5, %xmm6movapd%xmm2, %xmm5mulsd%xmm4, %xmm5movsd%xmm6, %xmm0movapd%xmm1, %xmm6mulsd%xmm3, %xmm6subsd%xmm5, %xmm6movapd%xmm1, %xmm5mulsd%xmm4, %xmm5addsd%xmm7, %xmm5ucomisd%xmm5, %xmm6jp.L9.L8:movups%xmm0, (%rax)movups64(%rsp), %xmm6movups80(%rsp), %xmm7addq$104, %rspret.L9:movq%rcx, 112(%rsp)leaq48(%rsp), %rcxmovsd%xmm4, 32(%rsp)call__muldc3movupd48(%rsp), %xmm0movq112(%rsp), %raxjmp.L8Code:cmul2:.seh_endprologuemovdqu(%rdx), %xmm0movdqu(%r8), %xmm2movapd%xmm0, %xmm1unpcklpd%xmm0, %xmm1unpckhpd%xmm0, %xmm0mulpd%xmm2, %xmm1shufpd$1, %xmm2, %xmm2movq%rcx, %raxmulpd%xmm2, %xmm0movapd%xmm1, %xmm2subpd%xmm0, %xmm2addpd%xmm0, %xmm1movsd%xmm2, %xmm1movups%xmm1, (%rcx)ret

author: magic
author_rank: Super Contributor
timestamp: February 25, 2022, 07:32:09 am
content: Quote from: DiTBho on February 24, 2022, 11:06:26 pmThis is the one to worry about.Others are C++ which doesn't have this macro because it has constructors to deal with it.

author: newbrain
author_rank: Super Contributor
timestamp: February 25, 2022, 08:49:46 am
content: Quote from: SiliconWizard on February 25, 2022, 01:31:09 amThe two code snippets are not equivalent.Consider what happens with all the combinations of Inf, 0 and NaN: the simple routine will return some surprising results, all the pirouetting in thecomplex.hversion is needed to keep them straight.The way they should work is described for C11 in Annex G IEC 60559-compatible complex arithmetic.Note that the annex is normative (if __STDC_IEC_559_COMPLEX__ is defined - which gcc does not by default, AFAICS), so a compliant implementation needs to abide by it.So, even withoutdeclaring full compliance, they are trying to make things right.Use -ffast-math (or -ffinite-math-only) to getthe same(unsafe, but mostly OK if one accepts the non compliance) results.Yes, definitely complex stuff...

author: DiTBho
author_rank: Super Contributor
timestamp: February 25, 2022, 12:28:35 pm
content: Quote from: magic on February 25, 2022, 07:32:09 amYup, that's one of the reasons why I rebuilt glibc

author: DiTBho
author_rank: Super Contributor
timestamp: February 25, 2022, 12:30:40 pm
content: I haven't yet understood how things work, mathcalls-helper-functions.h looks invoked several times with different data-types.

author: rstofer
author_rank: Super Contributor
timestamp: February 25, 2022, 04:02:18 pm
content: When I have to deal with numbers, I am more comfortable with Fortran.Code:program Compleximplicit nonecomplex z /(4,3)/ ! this is 4+3iprint *, abs(z) ! result is 5.00000000end program ComplexThis result is from the GNU Fortran compiler where complex arithmetic just works. It wasn't an afterthought, it has always been part of the language back to at least FORTRAN IV released in 1962.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 25, 2022, 06:00:29 pm
content: Quote from: newbrain on February 25, 2022, 08:49:46 amClearly not.Quote from: newbrain on February 25, 2022, 08:49:46 amOf course. But that's just to show that you could get unexpectedly worse performance when using the complex type in C, without necessarily realizing it, or even less so requiring the overhead in your case.Although this is a reasonable approach, this kind of "hand-holding" is still questionable. And, if you set -ffast-math or -ffinite-math-only, this has other implications on your code, especially if you're also using FP numbers.Sure, expressions like 'a*c + b*d' can yield funky results or loss of precision depending on a, b, c and d using FP arithmetic. But that's true in any case, not just when dealing with complex numbers, and expressions like this are extremely common.

author: DiTBho
author_rank: Super Contributor
timestamp: February 26, 2022, 04:39:50 am
content: Got tired and found a workaroundCode:/** ISO C99 Standard: 7.22 Type-generic math <tgmath.h>*/#ifndef _TGMATH_H#define _TGMATH_H 1#define __GLIBC_INTERNAL_STARTING_HEADER_IMPLEMENTATION#include <bits/libc-header-start.h>/* Include the needed headers. */#include <bits/floatn.h>#ifndef myautoproto // myhack#include <math.h> // myhack#endif // myhack#include <complex.h>...(/usr/include/tgmath.h)added "-Dmyautoproto=1" *only* to the interface generator of my autobuilderCode:# myproject-autobuild-makefile-v5-revC# make clean_all- cleaning interfaces /usr/include- cleaning interfaces app- cleaning interfaces .- cleaning all# make interfaces- touching interface private- touching interface public- generating interface private for app- generating interface private for cplx- generating interface public for app- generating interface public for cplx# make- compiling app ... done- compiling cplx ... done- linking to appand boooom, now it works without annoying unintelligible messages

author: DiTBho
author_rank: Super Contributor
timestamp: February 26, 2022, 05:02:59 am
content: Quote from: rstofer on February 25, 2022, 04:02:18 pmOf course, GNU Fortran is alsoless messed upthan Gcc, but I have to support complex numbers in the cleanest and safest way for the firmware I wrote for industrial embroidery machine and I don't want to also implement complex numbers because I am already about 7K lines of C.The next steps are- to get it working for Linux-{ MIPS32/be, HPPA2/32bit, PPC32/be, PPC64/be-32bit, ARM7-32bit }-glibc- to get it working for cross-compiling, for the industrial embroidery machine, target = PPC32/bePPC64/be-32bit will also have to support complex numbersnativelyfor a radio-telescope application. Next month.

author: rstofer
author_rank: Super Contributor
timestamp: February 26, 2022, 10:42:20 pm
content: Quote from: DiTBho on February 26, 2022, 05:02:59 amIt's pretty easy to mix C and Fortran once you get used to the idea that Fortran is 'call by reference' and C is 'call by value'. I used an RS232 library written in C on a Fortran project by writing a little shim code. In Fortran, a complex value is held as a 2 element vector of real numbers. I suppose that would be a 'struct' in C were I writing my own complex library but I haven't tried it.But really, you're probably too far along to pull the math bits out and rewrite them in Fortran. It's just that FORTRAN was my first language and will always be my favorite. When it comes to numbers, I tend to prefer it to all others.

author: newbrain
author_rank: Super Contributor
timestamp: February 27, 2022, 09:39:23 pm
content: Quote from: rstofer on February 26, 2022, 10:42:20 pmFrom ISO/IEC 9899:2011 (C11 standard) "6.2.5 Types", paragraph 13:QuoteSo yes, it's an array exactly as in Fortran, not a structure.In practice, it's a difference without a distinction:the amount of padding between members in a struct is not specified (at least, that's how I read the standard), while an array can have only as much as to guarantee alignment of its elements (due to pointer arithmetic) though I would not expect any sane compiler to actually insert pointless padding.