Source file test/convert5.go

     1  // run
     2  
     3  // Copyright 2020 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package main
     8  
     9  import (
    10  	"fmt"
    11  	"runtime"
    12  )
    13  
    14  // This test checks that conversion from floats to (unsigned) 32 and 64-bit
    15  // integers has the same sensible behavior for corner cases, and that the
    16  // conversions to smaller integers agree.  Because outliers are platform-
    17  // independent, the "golden test" for smaller integers is more like of
    18  // a "gold-ish test" and subject to change.
    19  
    20  //go:noinline
    21  func id[T any](x T) T {
    22  	return x
    23  }
    24  
    25  //go:noinline
    26  func want[T comparable](name string, x, y T) {
    27  	if x != y {
    28  		_, _, line, _ := runtime.Caller(1)
    29  		fmt.Println("FAIL at line", line, "var =", name, "got =", x, "want =", y)
    30  	}
    31  }
    32  
    33  //go:noinline
    34  func log[T comparable](name string, x T) {
    35  	fmt.Println(name, x)
    36  }
    37  
    38  const (
    39  	// pX = max positive signed X bit
    40  	// nX = min negative signed X bit
    41  	// uX = max unsigned X bit
    42  	// tX = two to the X
    43  	p32 = 2147483647
    44  	n32 = -2147483648
    45  	u32 = 4294967295
    46  	p64 = 9223372036854775807
    47  	n64 = -9223372036854775808
    48  	u64 = 18446744073709551615
    49  	t44 = 1 << 44
    50  )
    51  
    52  func main() {
    53  	one := 1.0
    54  	minus1_32 := id(float32(-1.0))
    55  	minus1_64 := id(float64(-1.0))
    56  	p32_plus4k_plus1 := id(float32(p32 + 4096 + 1)) // want this to be precise and fit in 24 bits mantissa
    57  	p64_plus4k_plus1 := id(float64(p64 + 4096 + 1)) // want this to be precise and fit in 53 bits mantissa
    58  	n32_minus4k := id(float32(n32 - 4096))
    59  	n64_minus4k := id(float64(n64 - 4096))
    60  	n32_plus4k := id(float32(n32 + 4096))
    61  	n64_plus4k := id(float64(n64 + 4096))
    62  	inf_32 := id(float32(one / 0))
    63  	inf_64 := id(float64(one / 0))
    64  	ninf_32 := id(float32(-one / 0))
    65  	ninf_64 := id(float64(-one / 0))
    66  
    67  	// int32 conversions
    68  	int32Tests := []struct {
    69  		name     string
    70  		input    any // Use any to handle both float32 and float64
    71  		expected int32
    72  	}{
    73  		{"minus1_32", minus1_32, -1},
    74  		{"minus1_64", minus1_64, -1},
    75  		{"p32_plus4k_plus1", p32_plus4k_plus1, p32},
    76  		{"p64_plus4k_plus1", p64_plus4k_plus1, p32},
    77  		{"n32_minus4k", n32_minus4k, n32},
    78  		{"n64_minus4k", n64_minus4k, n32},
    79  		{"n32_plus4k", n32_plus4k, n32 + 4096},
    80  		{"inf_32", inf_32, p32},
    81  		{"inf_64", inf_64, p32},
    82  		{"ninf_32", ninf_32, n32},
    83  		{"ninf_64", ninf_64, n32},
    84  	}
    85  
    86  	for _, test := range int32Tests {
    87  		var converted int32
    88  		switch v := test.input.(type) {
    89  		case float32:
    90  			converted = int32(v)
    91  		case float64:
    92  			converted = int32(v)
    93  		}
    94  		want(test.name, converted, test.expected)
    95  	}
    96  
    97  	// int64 conversions
    98  	int64Tests := []struct {
    99  		name     string
   100  		input    any
   101  		expected int64
   102  	}{
   103  		{"minus1_32", minus1_32, -1},
   104  		{"minus1_64", minus1_64, -1},
   105  		{"p32_plus4k_plus1", p32_plus4k_plus1, p32 + 4096 + 1},
   106  		{"p64_plus4k_plus1", p64_plus4k_plus1, p64},
   107  		{"n32_minus4k", n32_minus4k, n32 - 4096},
   108  		{"n64_minus4k", n64_minus4k, n64},
   109  		{"n32_plus4k", n32_plus4k, n32 + 4096},
   110  		{"n64_plus4k", n64_plus4k, n64 + 4096},
   111  		{"inf_32", inf_32, p64},
   112  		{"inf_64", inf_64, p64},
   113  		{"ninf_32", ninf_32, n64},
   114  		{"ninf_64", ninf_64, n64},
   115  	}
   116  
   117  	for _, test := range int64Tests {
   118  		var converted int64
   119  		switch v := test.input.(type) {
   120  		case float32:
   121  			converted = int64(v)
   122  		case float64:
   123  			converted = int64(v)
   124  		}
   125  		want(test.name, converted, test.expected)
   126  	}
   127  
   128  	// uint32 conversions
   129  	uint32Tests := []struct {
   130  		name     string
   131  		input    any
   132  		expected uint32
   133  	}{
   134  		{"minus1_32", minus1_32, 0},
   135  		{"minus1_64", minus1_64, 0},
   136  		{"p32_plus4k_plus1", p32_plus4k_plus1, p32 + 4096 + 1},
   137  		{"p64_plus4k_plus1", p64_plus4k_plus1, u32},
   138  		{"n32_minus4k", n32_minus4k, 0},
   139  		{"n64_minus4k", n64_minus4k, 0},
   140  		{"inf_32", inf_32, u32},
   141  		{"inf_64", inf_64, u32},
   142  		{"ninf_32", ninf_32, 0},
   143  		{"ninf_64", ninf_64, 0},
   144  	}
   145  
   146  	for _, test := range uint32Tests {
   147  		var converted uint32
   148  		switch v := test.input.(type) {
   149  		case float32:
   150  			converted = uint32(v)
   151  		case float64:
   152  			converted = uint32(v)
   153  		}
   154  		want(test.name, converted, test.expected)
   155  	}
   156  
   157  	u64_plus4k_plus1_64 := id(float64(u64 + 4096 + 1))
   158  	u64_plust44_plus1_32 := id(float32(u64 + t44 + 1))
   159  
   160  	// uint64 conversions
   161  	uint64Tests := []struct {
   162  		name     string
   163  		input    any
   164  		expected uint64
   165  	}{
   166  		{"minus1_32", minus1_32, 0},
   167  		{"minus1_64", minus1_64, 0},
   168  		{"p32_plus4k_plus1", p32_plus4k_plus1, p32 + 4096 + 1},
   169  		{"p64_plus4k_plus1", p64_plus4k_plus1, p64 + 4096 + 1},
   170  		{"n32_minus4k", n32_minus4k, 0},
   171  		{"n64_minus4k", n64_minus4k, 0},
   172  		{"inf_32", inf_32, u64},
   173  		{"inf_64", inf_64, u64},
   174  		{"ninf_32", ninf_32, 0},
   175  		{"ninf_64", ninf_64, 0},
   176  		{"u64_plus4k_plus1_64", u64_plus4k_plus1_64, u64},
   177  		{"u64_plust44_plus1_32", u64_plust44_plus1_32, u64},
   178  	}
   179  
   180  	for _, test := range uint64Tests {
   181  		var converted uint64
   182  		switch v := test.input.(type) {
   183  		case float32:
   184  			converted = uint64(v)
   185  		case float64:
   186  			converted = uint64(v)
   187  		}
   188  		want(test.name, converted, test.expected)
   189  	}
   190  
   191  	// for smaller integer types
   192  	// TODO the overflow behavior is dubious, maybe we should fix it to be more sensible, e.g. saturating.
   193  	fmt.Println("Below this are 'golden' results to check for consistency across platforms.  Overflow behavior is not necessarily what we want")
   194  
   195  	u8plus2 := id(float64(257))
   196  	p8minus1 := id(float32(126))
   197  	n8plus2 := id(float64(-126))
   198  	n8minusone := id(float32(-129))
   199  
   200  	fmt.Println("\nuint8 conversions")
   201  	uint8Tests := []struct {
   202  		name  string
   203  		input any
   204  	}{
   205  		{"minus1_32", minus1_32},
   206  		{"minus1_64", minus1_64},
   207  		{"p32_plus4k_plus1", p32_plus4k_plus1},
   208  		{"p64_plus4k_plus1", p64_plus4k_plus1},
   209  		{"n32_minus4k", n32_minus4k},
   210  		{"n64_minus4k", n64_minus4k},
   211  		{"inf_32", inf_32},
   212  		{"inf_64", inf_64},
   213  		{"ninf_32", ninf_32},
   214  		{"ninf_64", ninf_64},
   215  		{"u64_plus4k_plus1_64", u64_plus4k_plus1_64},
   216  		{"u64_plust44_plus1_32", u64_plust44_plus1_32},
   217  		{"u8plus2", u8plus2},
   218  		{"p8minus1", p8minus1},
   219  		{"n8plus2", n8plus2},
   220  		{"n8minusone", n8minusone},
   221  	}
   222  
   223  	for _, test := range uint8Tests {
   224  		var converted uint8
   225  		switch v := test.input.(type) {
   226  		case float32:
   227  			converted = uint8(v)
   228  		case float64:
   229  			converted = uint8(v)
   230  		}
   231  		log(test.name, converted)
   232  	}
   233  
   234  	fmt.Println("\nint8 conversions")
   235  	int8Tests := []struct {
   236  		name  string
   237  		input any
   238  	}{
   239  		{"minus1_32", minus1_32},
   240  		{"minus1_64", minus1_64},
   241  		{"p32_plus4k_plus1", p32_plus4k_plus1},
   242  		{"p64_plus4k_plus1", p64_plus4k_plus1},
   243  		{"n32_minus4k", n32_minus4k},
   244  		{"n64_minus4k", n64_minus4k},
   245  		{"inf_32", inf_32},
   246  		{"inf_64", inf_64},
   247  		{"ninf_32", ninf_32},
   248  		{"ninf_64", ninf_64},
   249  		{"u64_plus4k_plus1_64", u64_plus4k_plus1_64},
   250  		{"u64_plust44_plus1_32", u64_plust44_plus1_32},
   251  		{"u8plus2", u8plus2},
   252  		{"p8minus1", p8minus1},
   253  		{"n8plus2", n8plus2},
   254  		{"n8minusone", n8minusone},
   255  	}
   256  
   257  	for _, test := range int8Tests {
   258  		var converted int8
   259  		switch v := test.input.(type) {
   260  		case float32:
   261  			converted = int8(v)
   262  		case float64:
   263  			converted = int8(v)
   264  		}
   265  		log(test.name, converted)
   266  	}
   267  
   268  }
   269  

View as plain text