github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/cmd/compile/internal/gc/mpfloat.go (about) 1 // Copyright 2009 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 gc 6 7 import ( 8 "fmt" 9 "math" 10 "math/big" 11 ) 12 13 // implements float arithmetic 14 15 const ( 16 // Maximum size in bits for Mpints before signalling 17 // overflow and also mantissa precision for Mpflts. 18 Mpprec = 512 19 // Turn on for constant arithmetic debugging output. 20 Mpdebug = false 21 ) 22 23 // Mpflt represents a floating-point constant. 24 type Mpflt struct { 25 Val big.Float 26 } 27 28 // Mpcplx represents a complex constant. 29 type Mpcplx struct { 30 Real Mpflt 31 Imag Mpflt 32 } 33 34 func newMpflt() *Mpflt { 35 var a Mpflt 36 a.Val.SetPrec(Mpprec) 37 return &a 38 } 39 40 func newMpcmplx() *Mpcplx { 41 var a Mpcplx 42 a.Real = *newMpflt() 43 a.Imag = *newMpflt() 44 return &a 45 } 46 47 func (a *Mpflt) SetInt(b *Mpint) { 48 if b.checkOverflow(0) { 49 // sign doesn't really matter but copy anyway 50 a.Val.SetInf(b.Val.Sign() < 0) 51 return 52 } 53 a.Val.SetInt(&b.Val) 54 } 55 56 func (a *Mpflt) Set(b *Mpflt) { 57 a.Val.Set(&b.Val) 58 } 59 60 func (a *Mpflt) Add(b *Mpflt) { 61 if Mpdebug { 62 fmt.Printf("\n%v + %v", a, b) 63 } 64 65 a.Val.Add(&a.Val, &b.Val) 66 67 if Mpdebug { 68 fmt.Printf(" = %v\n\n", a) 69 } 70 } 71 72 func (a *Mpflt) AddFloat64(c float64) { 73 var b Mpflt 74 75 b.SetFloat64(c) 76 a.Add(&b) 77 } 78 79 func (a *Mpflt) Sub(b *Mpflt) { 80 if Mpdebug { 81 fmt.Printf("\n%v - %v", a, b) 82 } 83 84 a.Val.Sub(&a.Val, &b.Val) 85 86 if Mpdebug { 87 fmt.Printf(" = %v\n\n", a) 88 } 89 } 90 91 func (a *Mpflt) Mul(b *Mpflt) { 92 if Mpdebug { 93 fmt.Printf("%v\n * %v\n", a, b) 94 } 95 96 a.Val.Mul(&a.Val, &b.Val) 97 98 if Mpdebug { 99 fmt.Printf(" = %v\n\n", a) 100 } 101 } 102 103 func (a *Mpflt) MulFloat64(c float64) { 104 var b Mpflt 105 106 b.SetFloat64(c) 107 a.Mul(&b) 108 } 109 110 func (a *Mpflt) Quo(b *Mpflt) { 111 if Mpdebug { 112 fmt.Printf("%v\n / %v\n", a, b) 113 } 114 115 a.Val.Quo(&a.Val, &b.Val) 116 117 if Mpdebug { 118 fmt.Printf(" = %v\n\n", a) 119 } 120 } 121 122 func (a *Mpflt) Cmp(b *Mpflt) int { 123 return a.Val.Cmp(&b.Val) 124 } 125 126 func (a *Mpflt) CmpFloat64(c float64) int { 127 if c == 0 { 128 return a.Val.Sign() // common case shortcut 129 } 130 return a.Val.Cmp(big.NewFloat(c)) 131 } 132 133 func (a *Mpflt) Float64() float64 { 134 x, _ := a.Val.Float64() 135 136 // check for overflow 137 if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { 138 Fatalf("ovf in Mpflt Float64") 139 } 140 141 return x + 0 // avoid -0 (should not be needed, but be conservative) 142 } 143 144 func (a *Mpflt) Float32() float64 { 145 x32, _ := a.Val.Float32() 146 x := float64(x32) 147 148 // check for overflow 149 if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { 150 Fatalf("ovf in Mpflt Float32") 151 } 152 153 return x + 0 // avoid -0 (should not be needed, but be conservative) 154 } 155 156 func (a *Mpflt) SetFloat64(c float64) { 157 if Mpdebug { 158 fmt.Printf("\nconst %g", c) 159 } 160 161 // convert -0 to 0 162 if c == 0 { 163 c = 0 164 } 165 a.Val.SetFloat64(c) 166 167 if Mpdebug { 168 fmt.Printf(" = %v\n", a) 169 } 170 } 171 172 func (a *Mpflt) Neg() { 173 // avoid -0 174 if a.Val.Sign() != 0 { 175 a.Val.Neg(&a.Val) 176 } 177 } 178 179 func (a *Mpflt) SetString(as string) { 180 for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') { 181 as = as[1:] 182 } 183 184 f, _, err := a.Val.Parse(as, 10) 185 if err != nil { 186 yyerror("malformed constant: %s (%v)", as, err) 187 a.Val.SetFloat64(0) 188 return 189 } 190 191 if f.IsInf() { 192 yyerror("constant too large: %s", as) 193 a.Val.SetFloat64(0) 194 return 195 } 196 197 // -0 becomes 0 198 if f.Sign() == 0 && f.Signbit() { 199 a.Val.SetFloat64(0) 200 } 201 } 202 203 func (f *Mpflt) String() string { 204 return fconv(f, 0) 205 } 206 207 func fconv(fvp *Mpflt, flag FmtFlag) string { 208 if flag&FmtSharp == 0 { 209 return fvp.Val.Text('b', 0) 210 } 211 212 // use decimal format for error messages 213 214 // determine sign 215 f := &fvp.Val 216 var sign string 217 if f.Sign() < 0 { 218 sign = "-" 219 f = new(big.Float).Abs(f) 220 } else if flag&FmtSign != 0 { 221 sign = "+" 222 } 223 224 // Don't try to convert infinities (will not terminate). 225 if f.IsInf() { 226 return sign + "Inf" 227 } 228 229 // Use exact fmt formatting if in float64 range (common case): 230 // proceed if f doesn't underflow to 0 or overflow to inf. 231 if x, _ := f.Float64(); f.Sign() == 0 == (x == 0) && !math.IsInf(x, 0) { 232 return fmt.Sprintf("%s%.6g", sign, x) 233 } 234 235 // Out of float64 range. Do approximate manual to decimal 236 // conversion to avoid precise but possibly slow Float 237 // formatting. 238 // f = mant * 2**exp 239 var mant big.Float 240 exp := f.MantExp(&mant) // 0.5 <= mant < 1.0 241 242 // approximate float64 mantissa m and decimal exponent d 243 // f ~ m * 10**d 244 m, _ := mant.Float64() // 0.5 <= m < 1.0 245 d := float64(exp) * (math.Ln2 / math.Ln10) // log_10(2) 246 247 // adjust m for truncated (integer) decimal exponent e 248 e := int64(d) 249 m *= math.Pow(10, d-float64(e)) 250 251 // ensure 1 <= m < 10 252 switch { 253 case m < 1-0.5e-6: 254 // The %.6g format below rounds m to 5 digits after the 255 // decimal point. Make sure that m*10 < 10 even after 256 // rounding up: m*10 + 0.5e-5 < 10 => m < 1 - 0.5e6. 257 m *= 10 258 e-- 259 case m >= 10: 260 m /= 10 261 e++ 262 } 263 264 return fmt.Sprintf("%s%.6ge%+d", sign, m, e) 265 }