If the code cannot use hypot() or the wider precision types, the slow method checks the performance using frexp() and scales the @greggo arguments.
#include <math.h> double nibot_norm(double w, double x, double y, double z) { // Sort the values by some means if (fabs(x) < fabs(w)) return nibot_norm(x, w, y, z); if (fabs(y) < fabs(x)) return nibot_norm(w, y, x, z); if (fabs(z) < fabs(y)) return nibot_norm(w, x, z, y); if (z == 0.0) return 0.0; // all zero case // Scale z to exponent half-way 1.0 to MAX_DOUBLE/4 // and w,x,y the same amount int maxi; frexp(DBL_MAX, &maxi); int zi; frexp(z, &zi); int pow2scale = (maxi / 2 - 2) - zi; // NO precision loss expected so far. // except w,x,y may become 0.0 if _far_ less than z w = ldexp(w, pow2scale); x = ldexp(x, pow2scale); y = ldexp(y, pow2scale); z = ldexp(z, pow2scale); // All finite values in range of squaring except for values // greatly insignificant to z (eg |z| > |x|*1e300) double norm = sqrt(((w * w + x * x) + y * y) + z * z); // Restore scale return ldexp(norm, -pow2scale); }
Test code
#include <float.h> #include <stdio.h> #ifndef DBL_TRUE_MIN #define DBL_TRUE_MIN DBL_MIN*DBL_EPSILON #endif void nibot_norm_test(double w, double x, double y, double z, double expect) { static int dig = DBL_DECIMAL_DIG - 1; printf(" w:%.*ex:%.*ey:%.*ez:%.*e\n", dig, w, dig, x, dig, y, dig, z); double norm = nibot_norm(w, x, y, z); printf("expect:%.*e\n", dig, expect); printf("actual:%.*e\n", dig, norm); if (expect != norm) puts("Different"); } int main(void) { nibot_norm_test(0, 0, 0, 0, 0); nibot_norm_test(10 / 7., 4 / 7., 2 / 7., 1 / 7., 11 / 7.); nibot_norm_test(DBL_MAX, 0, 0, 0, DBL_MAX); nibot_norm_test(DBL_MAX / 2, DBL_MAX / 2, DBL_MAX / 2, DBL_MAX / 2, DBL_MAX); nibot_norm_test(DBL_TRUE_MIN, 0, 0, 0, DBL_TRUE_MIN); nibot_norm_test(DBL_TRUE_MIN, DBL_TRUE_MIN, DBL_TRUE_MIN, DBL_TRUE_MIN, DBL_TRUE_MIN * 2); return 0; }
results
w:0.00000000000000000e+00 x:0.00000000000000000e+00 y:0.00000000000000000e+00 z:0.00000000000000000e+00 expect:0.00000000000000000e+00 actual:0.00000000000000000e+00 w:1.42857142857142860e+00 x:5.71428571428571397e-01 y:2.85714285714285698e-01 z:1.42857142857142849e-01 expect:1.57142857142857140e+00 actual:1.57142857142857140e+00 w:1.79769313486231571e+308 x:0.00000000000000000e+00 y:0.00000000000000000e+00 z:0.00000000000000000e+00 expect:1.79769313486231571e+308 actual:1.79769313486231571e+308 w:8.98846567431157854e+307 x:8.98846567431157854e+307 y:8.98846567431157854e+307 z:8.98846567431157854e+307 expect:1.79769313486231571e+308 actual:1.79769313486231571e+308 w:4.94065645841246544e-324 x:0.00000000000000000e+00 y:0.00000000000000000e+00 z:0.00000000000000000e+00 expect:4.94065645841246544e-324 actual:4.94065645841246544e-324 w:4.94065645841246544e-324 x:4.94065645841246544e-324 y:4.94065645841246544e-324 z:4.94065645841246544e-324 expect:9.88131291682493088e-324 actual:9.88131291682493088e-324