Commit 16200c73 authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Robert Griesemer

runtime: make complex division c99 compatible

- changes tests to check that the real and imaginary part of the go complex
  division result is equal to the result gcc produces for c99
- changes complex division code to satisfy new complex division test
- adds float functions isNan, isFinite, isInf, abs and copysign
  in the runtime package

Fixes #14644.

name                   old time/op  new time/op  delta
Complex128DivNormal-4  21.8ns ± 6%  13.9ns ± 6%  -36.37%  (p=0.000 n=20+20)
Complex128DivNisNaN-4  14.1ns ± 1%  15.0ns ± 1%   +5.86%  (p=0.000 n=20+19)
Complex128DivDisNaN-4  12.5ns ± 1%  16.7ns ± 1%  +33.79%  (p=0.000 n=19+20)
Complex128DivNisInf-4  10.1ns ± 1%  13.0ns ± 1%  +28.25%  (p=0.000 n=20+19)
Complex128DivDisInf-4  11.0ns ± 1%  20.9ns ± 1%  +90.69%  (p=0.000 n=16+19)
ComplexAlgMap-4        86.7ns ± 1%  86.8ns ± 2%     ~     (p=0.804 n=20+20)

Change-Id: I261f3b4a81f6cc858bc7ff48f6fd1b39c300abf0
Reviewed-on: https://go-review.googlesource.com/37441Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent 4b8f41da
...@@ -4,68 +4,58 @@ ...@@ -4,68 +4,58 @@
package runtime package runtime
func isposinf(f float64) bool { return f > maxFloat64 } // inf2one returns a signed 1 if f is an infinity and a signed 0 otherwise.
func isneginf(f float64) bool { return f < -maxFloat64 } // The sign of the result is the sign of f.
func isnan(f float64) bool { return f != f } func inf2one(f float64) float64 {
g := 0.0
func nan() float64 { if isInf(f) {
var f float64 = 0 g = 1.0
return f / f }
} return copysign(g, f)
func posinf() float64 {
var f float64 = maxFloat64
return f * f
}
func neginf() float64 {
var f float64 = maxFloat64
return -f * f
} }
func complex128div(n complex128, d complex128) complex128 { func complex128div(n complex128, m complex128) complex128 {
// Special cases as in C99. var e, f float64 // complex(e, f) = n/m
ninf := isposinf(real(n)) || isneginf(real(n)) ||
isposinf(imag(n)) || isneginf(imag(n)) // Algorithm for robust complex division as described in
dinf := isposinf(real(d)) || isneginf(real(d)) || // Robert L. Smith: Algorithm 116: Complex division. Commun. ACM 5(8): 435 (1962).
isposinf(imag(d)) || isneginf(imag(d)) if abs(real(m)) >= abs(imag(m)) {
ratio := imag(m) / real(m)
nnan := !ninf && (isnan(real(n)) || isnan(imag(n))) denom := real(m) + ratio*imag(m)
dnan := !dinf && (isnan(real(d)) || isnan(imag(d))) e = (real(n) + imag(n)*ratio) / denom
f = (imag(n) - real(n)*ratio) / denom
} else {
ratio := real(m) / imag(m)
denom := imag(m) + ratio*real(m)
e = (real(n)*ratio + imag(n)) / denom
f = (imag(n)*ratio - real(n)) / denom
}
switch { if isNaN(e) && isNaN(f) {
case nnan || dnan: // Correct final result to infinities and zeros if applicable.
return complex(nan(), nan()) // Matches C99: ISO/IEC 9899:1999 - G.5.1 Multiplicative operators.
case ninf && !dinf:
return complex(posinf(), posinf()) a, b := real(n), imag(n)
case !ninf && dinf: c, d := real(m), imag(m)
return complex(0, 0)
case real(d) == 0 && imag(d) == 0: switch {
if real(n) == 0 && imag(n) == 0 { case m == 0 && (!isNaN(a) || !isNaN(b)):
return complex(nan(), nan()) e = copysign(inf, c) * a
} else { f = copysign(inf, c) * b
return complex(posinf(), posinf())
} case (isInf(a) || isInf(b)) && isFinite(c) && isFinite(d):
default: a = inf2one(a)
// Standard complex arithmetic, factored to avoid unnecessary overflow. b = inf2one(b)
a := real(d) e = inf * (a*c + b*d)
if a < 0 { f = inf * (b*c - a*d)
a = -a
} case (isInf(c) || isInf(d)) && isFinite(a) && isFinite(b):
b := imag(d) c = inf2one(c)
if b < 0 { d = inf2one(d)
b = -b e = 0 * (a*c + b*d)
} f = 0 * (b*c - a*d)
if a <= b {
ratio := real(d) / imag(d)
denom := real(d)*ratio + imag(d)
return complex((real(n)*ratio+imag(n))/denom,
(imag(n)*ratio-real(n))/denom)
} else {
ratio := imag(d) / real(d)
denom := imag(d)*ratio + real(d)
return complex((imag(n)*ratio+real(n))/denom,
(imag(n)-real(n)*ratio)/denom)
} }
} }
return complex(e, f)
} }
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
package runtime package runtime
import "unsafe"
// fastlog2 implements a fast approximation to the base 2 log of a // fastlog2 implements a fast approximation to the base 2 log of a
// float64. This is used to compute a geometric distribution for heap // float64. This is used to compute a geometric distribution for heap
// sampling, without introducing dependencies into package math. This // sampling, without introducing dependencies into package math. This
...@@ -27,7 +25,3 @@ func fastlog2(x float64) float64 { ...@@ -27,7 +25,3 @@ func fastlog2(x float64) float64 {
low, high := fastlog2Table[xManIndex], fastlog2Table[xManIndex+1] low, high := fastlog2Table[xManIndex], fastlog2Table[xManIndex+1]
return float64(xExp) + low + (high-low)*float64(xManScale)*fastlogScaleRatio return float64(xExp) + low + (high-low)*float64(xManScale)*fastlogScaleRatio
} }
// float64bits returns the IEEE 754 binary representation of f.
// Taken from math.Float64bits to avoid dependencies into package math.
func float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import "unsafe"
var inf = float64frombits(0x7FF0000000000000)
// isNaN reports whether f is an IEEE 754 ``not-a-number'' value.
func isNaN(f float64) (is bool) {
// IEEE 754 says that only NaNs satisfy f != f.
return f != f
}
// isFinite reports whether f is neither NaN nor an infinity.
func isFinite(f float64) bool {
return !isNaN(f - f)
}
// isInf reports whether f is an infinity.
func isInf(f float64) bool {
return !isNaN(f) && !isFinite(f)
}
// Abs returns the absolute value of x.
//
// Special cases are:
// Abs(±Inf) = +Inf
// Abs(NaN) = NaN
func abs(x float64) float64 {
const sign = 1 << 63
return float64frombits(float64bits(x) &^ sign)
}
// copysign returns a value with the magnitude
// of x and the sign of y.
func copysign(x, y float64) float64 {
const sign = 1 << 63
return float64frombits(float64bits(x)&^sign | float64bits(y)&sign)
}
// Float64bits returns the IEEE 754 binary representation of f.
func float64bits(f float64) uint64 {
return *(*uint64)(unsafe.Pointer(&f))
}
// Float64frombits returns the floating point number corresponding
// the IEEE 754 binary representation b.
func float64frombits(b uint64) float64 {
return *(*float64)(unsafe.Pointer(&b))
}
...@@ -23,50 +23,63 @@ ...@@ -23,50 +23,63 @@
#define nelem(x) (sizeof(x)/sizeof((x)[0])) #define nelem(x) (sizeof(x)/sizeof((x)[0]))
double f[] = { double f[] = {
0, 0.0,
1, -0.0,
-1, 1.0,
2, -1.0,
2.0,
NAN, NAN,
INFINITY, INFINITY,
-INFINITY, -INFINITY,
}; };
char* char* fmt(double g) {
fmt(double g)
{
static char buf[10][30]; static char buf[10][30];
static int n; static int n;
char *p; char *p;
p = buf[n++]; p = buf[n++];
if(n == 10) if(n == 10) {
n = 0; n = 0;
}
sprintf(p, "%g", g); sprintf(p, "%g", g);
if(strcmp(p, "-0") == 0)
strcpy(p, "negzero");
return p;
}
int if(strcmp(p, "0") == 0) {
iscnan(double complex d) strcpy(p, "zero");
{ return p;
return !isinf(creal(d)) && !isinf(cimag(d)) && (isnan(creal(d)) || isnan(cimag(d))); }
}
if(strcmp(p, "-0") == 0) {
strcpy(p, "-zero");
return p;
}
double complex zero; // attempt to hide zero division from gcc return p;
}
int int main(void) {
main(void)
{
int i, j, k, l; int i, j, k, l;
double complex n, d, q; double complex n, d, q;
printf("// skip\n"); printf("// skip\n");
printf("// # generated by cmplxdivide.c\n"); printf("// # generated by cmplxdivide.c\n");
printf("\n"); printf("\n");
printf("package main\n"); printf("package main\n");
printf("var tests = []Test{\n"); printf("\n");
printf("import \"math\"\n");
printf("\n");
printf("var (\n");
printf("\tnan = math.NaN()\n");
printf("\tinf = math.Inf(1)\n");
printf("\tzero = 0.0\n");
printf(")\n");
printf("\n");
printf("var tests = []struct {\n");
printf("\tf, g complex128\n");
printf("\tout complex128\n");
printf("}{\n");
for(i=0; i<nelem(f); i++) for(i=0; i<nelem(f); i++)
for(j=0; j<nelem(f); j++) for(j=0; j<nelem(f); j++)
for(k=0; k<nelem(f); k++) for(k=0; k<nelem(f); k++)
...@@ -74,17 +87,8 @@ main(void) ...@@ -74,17 +87,8 @@ main(void)
n = f[i] + f[j]*I; n = f[i] + f[j]*I;
d = f[k] + f[l]*I; d = f[k] + f[l]*I;
q = n/d; q = n/d;
// BUG FIX.
// Gcc gets the wrong answer for NaN/0 unless both sides are NaN.
// That is, it treats (NaN+NaN*I)/0 = NaN+NaN*I (a complex NaN)
// but it then computes (1+NaN*I)/0 = Inf+NaN*I (a complex infinity).
// Since both numerators are complex NaNs, it seems that the
// results should agree in kind. Override the gcc computation in this case.
if(iscnan(n) && d == 0)
q = (NAN+NAN*I) / zero;
printf("\tTest{complex(%s, %s), complex(%s, %s), complex(%s, %s)},\n", printf("\t{complex(%s, %s), complex(%s, %s), complex(%s, %s)},\n",
fmt(creal(n)), fmt(cimag(n)), fmt(creal(n)), fmt(cimag(n)),
fmt(creal(d)), fmt(cimag(d)), fmt(creal(d)), fmt(cimag(d)),
fmt(creal(q)), fmt(cimag(q))); fmt(creal(q)), fmt(cimag(q)));
......
...@@ -5,33 +5,25 @@ ...@@ -5,33 +5,25 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Driver for complex division table defined in cmplxdivide1.go // Driver for complex division table defined in cmplxdivide1.go
// For details, see the comment at the top of in cmplxdivide.c. // For details, see the comment at the top of cmplxdivide.c.
package main package main
import ( import (
"fmt" "fmt"
"math" "math"
"math/cmplx"
) )
type Test struct {
f, g complex128
out complex128
}
var nan = math.NaN()
var inf = math.Inf(1)
var negzero = math.Copysign(0, -1)
func calike(a, b complex128) bool { func calike(a, b complex128) bool {
switch { if imag(a) != imag(b) && !(math.IsNaN(imag(a)) && math.IsNaN(imag(b))) {
case cmplx.IsInf(a) && cmplx.IsInf(b): return false
return true
case cmplx.IsNaN(a) && cmplx.IsNaN(b):
return true
} }
return a == b
if real(a) != real(b) && !(math.IsNaN(real(a)) && math.IsNaN(real(b))) {
return false
}
return true
} }
func main() { func main() {
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment