@@ -13,6 +13,8 @@
the the whole number is Inf, but an operation involving NaN ought
to result in NaN, not Inf. */
+#include <math.h>
+
__complex float
__go_complex64_div (__complex float a, __complex float b)
{
@@ -29,6 +31,48 @@ __go_complex64_div (__complex float a, __complex float b)
return a / b;
}
+#ifdef FP_FAST_FMA
+
+// This implements what is sometimes called "Kahan's compensated algorithm for
+// 2 by 2 determinants". It returns a*b + c*d to a high degree of precision
+// but depends on a fused-multiply add operation that rounds once.
+//
+// The obvious algorithm has problems when a*b and c*d nearly cancel, but the
+// trick is the calculation of 'e': "a*b = w + e" is exact when the operands
+// are considered as real numbers. So if c*d nearly cancels out w, e restores
+// the result to accuracy.
+double
+Kahan(double a, double b, double c, double d)
+{
+ double w, e, f;
+ w = b * a;
+ e = fma(b, a, -w);
+ f = fma(d, c, w);
+ return f + e;
+}
+
+__complex double
+__go_complex128_div (__complex double a, __complex double b)
+{
+ double r, i, denom;
+ if (__builtin_expect (b == 0+0i, 0))
+ {
+ if (!__builtin_isinf (__real__ a)
+ && !__builtin_isinf (__imag__ a)
+ && (__builtin_isnan (__real__ a) || __builtin_isnan (__imag__ a)))
+ {
+ /* Pass "1" to nan to match math/bits.go. */
+ return __builtin_nan("1") + __builtin_nan("1")*1i;
+ }
+ }
+ r = Kahan(__real__ a, __real__ b, __imag__ a, __imag__ b);
+ i = Kahan(__imag__ a, __real__ b, - __real__ a, __imag__ b);
+ denom = (__real__ b)*(__real__ b) + (__imag__ b)*(__imag__ b);
+ return r/denom + i*1.0i/denom;
+}
+
+#else
+
__complex double
__go_complex128_div (__complex double a, __complex double b)
{
@@ -44,3 +88,5 @@ __go_complex128_div (__complex double a, __complex double b)
}
return a / b;
}
+
+#endif