#!/usr/bin/env perl use strict; use warnings; my @fct = qw(x_add_zero x_sub_zero zero_sub_x zero_mul_x zero_div_x x_mul_pone x_mul_mone x_div_pone x_div_mone); my %ops = ( add => '+', sub => '-', mul => '*', div => '/' ); my %val = ( x => 'x', zero => '0.0', pone => '1.0', mone => '-1.0' ); print <<'EOF'; /* Test some common transformations. See: * https://gcc.gnu.org/wiki/FloatingPointMath * * Note: the NaN tests may not be valid on all machines. * Tested only on x86_64. */ #include #include #include #include #include #include typedef union { double d; uint64_t i; } ieee_double_t; EOF foreach my $f (@fct) { $f =~ /^([a-z]+)_([a-z]+)_([a-z]+)$/ or die; my $ret = "return $val{$1} $ops{$2} $val{$3};"; print "double ${f}_0 (double x) { $ret }\n"; $ret =~ s/(-?[.\d]+)/c/ or die; print "double ${f}_1 (double x) { volatile double c = $1; $ret }\n\n"; } print <<'EOF'; /* NaN canonicalization */ uint64_t canon (uint64_t i) { return ((i & 0x7ff0000000000000) != 0x7ff0000000000000 || (i & 0x000fffffffffffff) == 0) ? i /* not NaN */ : 0x7ff0000000000001 | (i & 0x0008000000000000); } void vout (char *s, double d) { ieee_double_t v; v.d = d; printf ("%s = [0x%016" PRIx64 "] = %g%s\n", s, v.i, d, canon (v.i) != 0x7ff0000000000001 ? "" : " (signaling)"); } int cmp (char *s, char *r, double y0, double y1) { volatile double z0 = y0, z1 = y1; volatile ieee_double_t v0, v1; v0.d = y0; v1.d = y1; if (canon (v0.i) == canon (v1.i)) return 0; #if 0 if ((isnan (z0) && isnan (z1)) || (z0 == z1 && !signbit (z0) == !signbit (z1))) return 0; #endif printf ("Error for %s in %s:\n", s, r); vout (" y0", z0); vout (" y1", z1); return 1; } int test (char *r, double x) { volatile double y0, y1; int err = 0; EOF foreach my $f (@fct) { print map " y$_ = ${f}_$_ (x);\n", (0..1); print " err |= cmp (\"${f}\", r, y0, y1);\n\n"; } print <<'EOF'; return err; } int main (int argc, char *argv[]) { char *end; double x; int err = 0; if (argc != 2) { exit (1); } if (strncmp (argv[1], "0x", 2) == 0) { ieee_double_t v; v.i = strtoull (argv[1], &end, 0); if (*end != '\0') exit (1); x = v.d; } else { x = strtod (argv[1], &end); if (*end != '\0') exit (1); } vout ("x", x); EOF foreach (qw/TONEAREST TOWARDZERO DOWNWARD UPWARD/) { my $r = "FE_$_"; print <