Source file src/runtime/softfloat64_test.go

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"math"
     9  	"math/rand"
    10  	. "runtime"
    11  	"testing"
    12  )
    13  
    14  // turn uint64 op into float64 op
    15  func fop(f func(x, y uint64) uint64) func(x, y float64) float64 {
    16  	return func(x, y float64) float64 {
    17  		bx := math.Float64bits(x)
    18  		by := math.Float64bits(y)
    19  		return math.Float64frombits(f(bx, by))
    20  	}
    21  }
    22  
    23  func add(x, y float64) float64 { return x + y }
    24  func sub(x, y float64) float64 { return x - y }
    25  func mul(x, y float64) float64 { return x * y }
    26  func div(x, y float64) float64 { return x / y }
    27  
    28  func TestFloat64(t *testing.T) {
    29  	base := []float64{
    30  		0,
    31  		math.Copysign(0, -1),
    32  		-1,
    33  		1,
    34  		math.NaN(),
    35  		math.Inf(+1),
    36  		math.Inf(-1),
    37  		0.1,
    38  		1.5,
    39  		1.9999999999999998,      // all 1s mantissa
    40  		1.3333333333333333,      // 1.010101010101...
    41  		1.1428571428571428,      // 1.001001001001...
    42  		1.112536929253601e-308,  // first normal
    43  		-2.662107816930723e-301, // #79964: two near-equal opposite-sign
    44  		2.662107858822336e-301,  // normals cancelling to a subnormal
    45  		2,
    46  		4,
    47  		8,
    48  		16,
    49  		32,
    50  		64,
    51  		128,
    52  		256,
    53  		3,
    54  		12,
    55  		1234,
    56  		123456,
    57  		-0.1,
    58  		-1.5,
    59  		-1.9999999999999998,
    60  		-1.3333333333333333,
    61  		-1.1428571428571428,
    62  		-2,
    63  		-3,
    64  		1e-200,
    65  		1e-300,
    66  		1e-310,
    67  		5e-324,
    68  		1e-105,
    69  		1e-305,
    70  		1e+200,
    71  		1e+306,
    72  		1e+307,
    73  		1e+308,
    74  	}
    75  	all := make([]float64, 200)
    76  	copy(all, base)
    77  	for i := len(base); i < len(all); i++ {
    78  		all[i] = rand.NormFloat64()
    79  	}
    80  
    81  	test(t, "+", add, fop(Fadd64), all)
    82  	test(t, "-", sub, fop(Fsub64), all)
    83  	if GOARCH != "386" { // 386 is not precise!
    84  		test(t, "*", mul, fop(Fmul64), all)
    85  		test(t, "/", div, fop(Fdiv64), all)
    86  	}
    87  }
    88  
    89  // 64 -hw-> 32 -hw-> 64
    90  func trunc32(f float64) float64 {
    91  	return float64(float32(f))
    92  }
    93  
    94  // 64 -sw->32 -hw-> 64
    95  func to32sw(f float64) float64 {
    96  	return float64(math.Float32frombits(F64to32(math.Float64bits(f))))
    97  }
    98  
    99  // 64 -hw->32 -sw-> 64
   100  func to64sw(f float64) float64 {
   101  	return math.Float64frombits(F32to64(math.Float32bits(float32(f))))
   102  }
   103  
   104  // float64 -hw-> int64 -hw-> float64
   105  func hwint64(f float64) float64 {
   106  	return float64(int64(f))
   107  }
   108  
   109  // float64 -hw-> int32 -hw-> float64
   110  func hwint32(f float64) float64 {
   111  	return float64(int32(f))
   112  }
   113  
   114  // float64 -sw-> int64 -hw-> float64
   115  func toint64sw(f float64) float64 {
   116  	i, ok := F64toint(math.Float64bits(f))
   117  	if !ok {
   118  		// There's no right answer for out of range.
   119  		// Match the hardware to pass the test.
   120  		i = int64(f)
   121  	}
   122  	return float64(i)
   123  }
   124  
   125  // float64 -hw-> int64 -sw-> float64
   126  func fromint64sw(f float64) float64 {
   127  	return math.Float64frombits(Fintto64(int64(f)))
   128  }
   129  
   130  var nerr int
   131  
   132  func err(t *testing.T, format string, args ...any) {
   133  	t.Errorf(format, args...)
   134  
   135  	// cut errors off after a while.
   136  	// otherwise we spend all our time
   137  	// allocating memory to hold the
   138  	// formatted output.
   139  	if nerr++; nerr >= 10 {
   140  		t.Fatal("too many errors")
   141  	}
   142  }
   143  
   144  func test(t *testing.T, op string, hw, sw func(float64, float64) float64, all []float64) {
   145  	for _, f := range all {
   146  		for _, g := range all {
   147  			h := hw(f, g)
   148  			s := sw(f, g)
   149  			if !same(h, s) {
   150  				err(t, "%g %s %g = sw %g, hw %g\n", f, op, g, s, h)
   151  			}
   152  			testu(t, "to32", trunc32, to32sw, h)
   153  			testu(t, "to64", trunc32, to64sw, h)
   154  			testu(t, "toint64", hwint64, toint64sw, h)
   155  			testu(t, "fromint64", hwint64, fromint64sw, h)
   156  			testcmp(t, f, h)
   157  			testcmp(t, h, f)
   158  			testcmp(t, g, h)
   159  			testcmp(t, h, g)
   160  		}
   161  	}
   162  }
   163  
   164  func testu(t *testing.T, op string, hw, sw func(float64) float64, v float64) {
   165  	h := hw(v)
   166  	s := sw(v)
   167  	if !same(h, s) {
   168  		err(t, "%s %g = sw %g, hw %g\n", op, v, s, h)
   169  	}
   170  }
   171  
   172  func hwcmp(f, g float64) (cmp int, isnan bool) {
   173  	switch {
   174  	case f < g:
   175  		return -1, false
   176  	case f > g:
   177  		return +1, false
   178  	case f == g:
   179  		return 0, false
   180  	}
   181  	return 0, true // must be NaN
   182  }
   183  
   184  func testcmp(t *testing.T, f, g float64) {
   185  	hcmp, hisnan := hwcmp(f, g)
   186  	scmp, sisnan := Fcmp64(math.Float64bits(f), math.Float64bits(g))
   187  	if int32(hcmp) != scmp || hisnan != sisnan {
   188  		err(t, "cmp(%g, %g) = sw %v, %v, hw %v, %v\n", f, g, scmp, sisnan, hcmp, hisnan)
   189  	}
   190  }
   191  
   192  func same(f, g float64) bool {
   193  	if math.IsNaN(f) && math.IsNaN(g) {
   194  		return true
   195  	}
   196  	if math.Copysign(1, f) != math.Copysign(1, g) {
   197  		return false
   198  	}
   199  	return f == g
   200  }
   201  

View as plain text