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,
    44  		4,
    45  		8,
    46  		16,
    47  		32,
    48  		64,
    49  		128,
    50  		256,
    51  		3,
    52  		12,
    53  		1234,
    54  		123456,
    55  		-0.1,
    56  		-1.5,
    57  		-1.9999999999999998,
    58  		-1.3333333333333333,
    59  		-1.1428571428571428,
    60  		-2,
    61  		-3,
    62  		1e-200,
    63  		1e-300,
    64  		1e-310,
    65  		5e-324,
    66  		1e-105,
    67  		1e-305,
    68  		1e+200,
    69  		1e+306,
    70  		1e+307,
    71  		1e+308,
    72  	}
    73  	all := make([]float64, 200)
    74  	copy(all, base)
    75  	for i := len(base); i < len(all); i++ {
    76  		all[i] = rand.NormFloat64()
    77  	}
    78  
    79  	test(t, "+", add, fop(Fadd64), all)
    80  	test(t, "-", sub, fop(Fsub64), all)
    81  	if GOARCH != "386" { // 386 is not precise!
    82  		test(t, "*", mul, fop(Fmul64), all)
    83  		test(t, "/", div, fop(Fdiv64), all)
    84  	}
    85  }
    86  
    87  // 64 -hw-> 32 -hw-> 64
    88  func trunc32(f float64) float64 {
    89  	return float64(float32(f))
    90  }
    91  
    92  // 64 -sw->32 -hw-> 64
    93  func to32sw(f float64) float64 {
    94  	return float64(math.Float32frombits(F64to32(math.Float64bits(f))))
    95  }
    96  
    97  // 64 -hw->32 -sw-> 64
    98  func to64sw(f float64) float64 {
    99  	return math.Float64frombits(F32to64(math.Float32bits(float32(f))))
   100  }
   101  
   102  // float64 -hw-> int64 -hw-> float64
   103  func hwint64(f float64) float64 {
   104  	return float64(int64(f))
   105  }
   106  
   107  // float64 -hw-> int32 -hw-> float64
   108  func hwint32(f float64) float64 {
   109  	return float64(int32(f))
   110  }
   111  
   112  // float64 -sw-> int64 -hw-> float64
   113  func toint64sw(f float64) float64 {
   114  	i, ok := F64toint(math.Float64bits(f))
   115  	if !ok {
   116  		// There's no right answer for out of range.
   117  		// Match the hardware to pass the test.
   118  		i = int64(f)
   119  	}
   120  	return float64(i)
   121  }
   122  
   123  // float64 -hw-> int64 -sw-> float64
   124  func fromint64sw(f float64) float64 {
   125  	return math.Float64frombits(Fintto64(int64(f)))
   126  }
   127  
   128  var nerr int
   129  
   130  func err(t *testing.T, format string, args ...any) {
   131  	t.Errorf(format, args...)
   132  
   133  	// cut errors off after a while.
   134  	// otherwise we spend all our time
   135  	// allocating memory to hold the
   136  	// formatted output.
   137  	if nerr++; nerr >= 10 {
   138  		t.Fatal("too many errors")
   139  	}
   140  }
   141  
   142  func test(t *testing.T, op string, hw, sw func(float64, float64) float64, all []float64) {
   143  	for _, f := range all {
   144  		for _, g := range all {
   145  			h := hw(f, g)
   146  			s := sw(f, g)
   147  			if !same(h, s) {
   148  				err(t, "%g %s %g = sw %g, hw %g\n", f, op, g, s, h)
   149  			}
   150  			testu(t, "to32", trunc32, to32sw, h)
   151  			testu(t, "to64", trunc32, to64sw, h)
   152  			testu(t, "toint64", hwint64, toint64sw, h)
   153  			testu(t, "fromint64", hwint64, fromint64sw, h)
   154  			testcmp(t, f, h)
   155  			testcmp(t, h, f)
   156  			testcmp(t, g, h)
   157  			testcmp(t, h, g)
   158  		}
   159  	}
   160  }
   161  
   162  func testu(t *testing.T, op string, hw, sw func(float64) float64, v float64) {
   163  	h := hw(v)
   164  	s := sw(v)
   165  	if !same(h, s) {
   166  		err(t, "%s %g = sw %g, hw %g\n", op, v, s, h)
   167  	}
   168  }
   169  
   170  func hwcmp(f, g float64) (cmp int, isnan bool) {
   171  	switch {
   172  	case f < g:
   173  		return -1, false
   174  	case f > g:
   175  		return +1, false
   176  	case f == g:
   177  		return 0, false
   178  	}
   179  	return 0, true // must be NaN
   180  }
   181  
   182  func testcmp(t *testing.T, f, g float64) {
   183  	hcmp, hisnan := hwcmp(f, g)
   184  	scmp, sisnan := Fcmp64(math.Float64bits(f), math.Float64bits(g))
   185  	if int32(hcmp) != scmp || hisnan != sisnan {
   186  		err(t, "cmp(%g, %g) = sw %v, %v, hw %v, %v\n", f, g, scmp, sisnan, hcmp, hisnan)
   187  	}
   188  }
   189  
   190  func same(f, g float64) bool {
   191  	if math.IsNaN(f) && math.IsNaN(g) {
   192  		return true
   193  	}
   194  	if math.Copysign(1, f) != math.Copysign(1, g) {
   195  		return false
   196  	}
   197  	return f == g
   198  }
   199  

View as plain text