1// run
2
3// Copyright 2021 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// Excerpted from go/constant/value.go to capture a bug from there.
8
9package main
10
11import (
12	"fmt"
13	"math"
14	"math/big"
15)
16
17type (
18	unknownVal struct{}
19	intVal     struct{ val *big.Int }   // Int values not representable as an int64
20	ratVal     struct{ val *big.Rat }   // Float values representable as a fraction
21	floatVal   struct{ val *big.Float } // Float values not representable as a fraction
22	complexVal struct{ re, im Value }
23)
24
25const prec = 512
26
27func (unknownVal) String() string { return "unknown" }
28
29func (x intVal) String() string   { return x.val.String() }
30func (x ratVal) String() string   { return rtof(x).String() }
31
32func (x floatVal) String() string {
33	f := x.val
34
35	// Use exact fmt formatting if in float64 range (common case):
36	// proceed if f doesn't underflow to 0 or overflow to inf.
37	if x, _ := f.Float64(); f.Sign() == 0 == (x == 0) && !math.IsInf(x, 0) {
38		return fmt.Sprintf("%.6g", x)
39	}
40
41	return "OOPS"
42}
43
44func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
45
46func newFloat() *big.Float { return new(big.Float).SetPrec(prec) }
47
48//go:noinline
49//go:registerparams
50func itor(x intVal) ratVal       { return ratVal{nil} }
51
52//go:noinline
53//go:registerparams
54func itof(x intVal) floatVal     { return floatVal{nil} }
55func rtof(x ratVal) floatVal     { return floatVal{newFloat().SetRat(x.val)} }
56
57type Value interface {
58	String() string
59}
60
61//go:noinline
62//go:registerparams
63func ToFloat(x Value) Value {
64	switch x := x.(type) {
65	case intVal:
66		if smallInt(x.val) {
67			return itor(x)
68		}
69		return itof(x)
70	case ratVal, floatVal:
71		return x
72	case complexVal:
73		if Sign(x.im) == 0 {
74			return ToFloat(x.re)
75		}
76	}
77	return unknownVal{}
78}
79
80//go:noinline
81//go:registerparams
82func smallInt(x *big.Int) bool {
83	return false
84}
85
86//go:noinline
87//go:registerparams
88func Sign(x Value) int {
89	return 0
90}
91
92
93func main() {
94	v := ratVal{big.NewRat(22,7)}
95	s := ToFloat(v).String()
96	fmt.Printf("s=%s\n", s)
97}
98