Date: Tue, 25 Mar 2014 17:22:05 -0700 From: Steve Kargl <sgk@troutmask.apl.washington.edu> To: freebsd-numerics@freebsd.org, freebsd-toolchain@freebsd.org Subject: clang is almost useless for complex arithmetic Message-ID: <20140326002205.GA9940@troutmask.apl.washington.edu>
next in thread | raw e-mail | index | archive | help
It appears that clang developers have chosen the naive complex division algorithm, and it does not matter whether one turns CX_LIMITED_RANGE on or off. This means that if one uses clang with complex types, one must be careful with the range of values allowed in complex division. In other words, implementation of complex libm routines cannot use complex data types and must fallback to a decomposition into real and imaginary components. For the record, I am using % cc --version FreeBSD clang version 3.4 (tags/RELEASE_34/final 197956) 20140216 Target: x86_64-unknown-freebsd11.0 With the program that follows my sig, if I do % cc -o z -O -DOFF cmplx.c -lm && ./z > pragma_off % cc -o z -O cmplx.c -lm && ./z > pragma_on % diff -u pragma_off pragma_on | wc -l 0 % cat pragma_on Expected: 0x1p-1023 -0x1p-1023 Compiler: 0x0p+0 -0x0p+0 Improved: 0x1p-1023 -0x1p-1023 Expected: 0x1p+1023 0x0p+0 Compiler: inf nan Improved: 0x1p+1023 0x0p+0 Expected: 0x1p+346 -0x1p-1008 Compiler: nan -0x0p+0 Improved: 0x1p+346 -0x1p-1008 Expected: 0x1p+1023 0x0p+0 Compiler: inf 0x0p+0 Improved: 0x1p+1023 0x0p+0 Expected: 0x1p+364 -0x1p-1072 Compiler: nan -0x0p+0 Improved: 0x1p+364 -0x1p-1072 Expected: 0x1p-1072 0x1p+20 Compiler: 0x0p+0 nan Improved: 0x1p-1072 0x1p+20 Expected: 0x1.ffffffffff8p+961 0x1.ffffffffff8p+982 Compiler: nan nan Improved: 0x1.ffffffffff8p+961 0x1.ffffffffff8p+982 Expected: 0x1.3333333333333p-1 0x1.999999999999ap-3 Compiler: nan nan Improved: 0x1.3333333333333p-1 0x1.999999999999ap-3 Expected: 0x1p-9 -0x1p-9 Compiler: nan nan Improved: 0x1p-9 -0x1p-9 Expected: 0x1p-279 0x1.f8p-729 Compiler: 0x1p-279 0x0p+0 Improved: 0x1p-279 0x1.f8p-729 Here, "Expected" is the expected result. "Compiler" is what clang produces. "Improved" implements the algorithm suggested in Michael Baudin, Robert L. Smith, "A Robust Complex Division in Scilab," http://arxiv.org/abs/1210.4539 -- Steve #include <complex.h> #include <float.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #ifdef OFF #pragma CX_LIMITED_RANGE off #else #pragma CX_LIMITED_RANGE on #endif typedef union { double complex f; double a[2]; } double_complex; #define REALPART(z) ((z).a[0]) #define IMAGPART(z) ((z).a[1]) static __inline double complex cpack(double x, double y) { double_complex z; REALPART(z) = x; IMAGPART(z) = y; return (z.f); } /* * For details of internal() and divi() see: * Michael Baudin, Robert L. Smith, "A Robust Complex Division in Scilab," * http://arxiv.org/abs/1210.4539 */ double complex internal(double a, double b, double c, double d, int l) { static const double ti = 0x1.p-1022, rti = 0x1.p1022, hu = 0x1.p1023, rhu = 0x1.p-1023; double e, f, r, t, ma, mi; r = d / c; if (r != 0) { if (fabs(c) < rhu) { t = c * hu + d * hu * r; e = (a * hu + b * hu * r) / t; f = (b * hu - a * hu * r) / t; } else if (fabs(c) > rti) { t = 1 / (c * ti + d * ti * r); e = (a * ti + b * ti * r) * t; f = (b * ti - a * ti * r) * t; } else { t = 1 / (c + d * r); ma = fmax(fabs(a), fabs(b)); mi = fmin(fabs(a), fabs(b)); if (ma < rti && mi * r > rhu) { e = (a + b * r) * t; f = (b - a * r) * t; } else { r = r * t; e = a * t + b * r; f = b * t - a * r; } } } else { t = 1 / c; e = (a + d * (b / c)) * t; f = (b - d * (a / c)) * t; } if (l == 1) f = -f; return (cpack(e, f)); } double complex divi(double complex z1, double complex z2) { double complex r; double a, b, c, d; a = creal(z1); b = cimag(z1); c = creal(z2); d = cimag(z2); if (fabs(d) <= fabs(c)) r = internal(a, b, c, d, 0); else r = internal(b, a, d, c, 1); return (r); } int main(void) { /* Numerators. */ double complex x[10] = { cpack(1, 1), cpack(1, 1), cpack(0x1.p1023, 0x1.p-1023), cpack(0x1.p1023, 0x1.p1023), cpack(0x1.p1020, 0x1.p-844), cpack(0x1.p-71, 0x1.p1021), cpack(0x1.p-347, 0x1.p-54), cpack(0x1.p-1074, 0x1.p-1074), cpack(0x1.p1015, 0x1.p-989), cpack(0x1.p-622, 0x1.p-1071) }; /* Denominators. */ double complex y[10] = { cpack(1, 0x1.p1023), cpack(0x1.p-1023, 0x1.p-1023), cpack(0x1.p677, 0x1.p-677), cpack(1, 1), cpack(0x1.p656, 0x1.p-780), cpack(0x1.p1001, 0x1.p-323), cpack(0x1.p-1037, 0x1.p-1058), cpack(0x1.p-1073, 0x1.p-1074), cpack(0x1.p1023, 0x1.p1023), cpack(0x1.p-343, 0x1.p-798) }; double complex r[10] = { cpack(0x1.p-1023, -0x1.p-1023), cpack(0x1.p1023, 0), cpack(0x1.p346, -0x1.p-1008), cpack(0x1.p1023, 0), cpack(0x1.p364, -0x1.p-1072), cpack(0x1.p-1072, 0x1.p20), cpack(3.898125604559113300E289, 8.174961907852353577E295), cpack(0.6, 0.2), cpack(0.001953125, -0.001953125), cpack(1.02951151789360578E-84, 6.97145987515076231E-220) }; double complex a, z[10]; int j; for (j = 0; j < 10; j++) { z[j] = x[j] / y[j]; a = divi(x[j], y[j]); printf("Expected: %la %la\n", creal(r[j]), cimag(r[j])); printf("Compiler: %la %la\n", creal(z[j]), cimag(z[j])); printf("Improved: %la %la\n", creal(a), cimag(a)); printf("\n"); } return 0; }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20140326002205.GA9940>