github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/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 // 180 // floating point input 181 // required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*] 182 // 183 func (a *Mpflt) SetString(as string) { 184 for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') { 185 as = as[1:] 186 } 187 188 f, ok := a.Val.SetString(as) 189 if !ok { 190 // At the moment we lose precise error cause; 191 // the old code additionally distinguished between: 192 // - malformed hex constant 193 // - decimal point in hex constant 194 // - constant exponent out of range 195 // - decimal point and binary point in constant 196 // TODO(gri) use different conversion function or check separately 197 yyerror("malformed constant: %s", as) 198 a.Val.SetFloat64(0) 199 return 200 } 201 202 if f.IsInf() { 203 yyerror("constant too large: %s", as) 204 a.Val.SetFloat64(0) 205 return 206 } 207 208 // -0 becomes 0 209 if f.Sign() == 0 && f.Signbit() { 210 a.Val.SetFloat64(0) 211 } 212 } 213 214 func (f *Mpflt) String() string { 215 return fconv(f, 0) 216 } 217 218 func fconv(fvp *Mpflt, flag FmtFlag) string { 219 if flag&FmtSharp == 0 { 220 return fvp.Val.Text('b', 0) 221 } 222 223 // use decimal format for error messages 224 225 // determine sign 226 f := &fvp.Val 227 var sign string 228 if f.Sign() < 0 { 229 sign = "-" 230 f = new(big.Float).Abs(f) 231 } else if flag&FmtSign != 0 { 232 sign = "+" 233 } 234 235 // Don't try to convert infinities (will not terminate). 236 if f.IsInf() { 237 return sign + "Inf" 238 } 239 240 // Use exact fmt formatting if in float64 range (common case): 241 // proceed if f doesn't underflow to 0 or overflow to inf. 242 if x, _ := f.Float64(); f.Sign() == 0 == (x == 0) && !math.IsInf(x, 0) { 243 return fmt.Sprintf("%s%.6g", sign, x) 244 } 245 246 // Out of float64 range. Do approximate manual to decimal 247 // conversion to avoid precise but possibly slow Float 248 // formatting. 249 // f = mant * 2**exp 250 var mant big.Float 251 exp := f.MantExp(&mant) // 0.5 <= mant < 1.0 252 253 // approximate float64 mantissa m and decimal exponent d 254 // f ~ m * 10**d 255 m, _ := mant.Float64() // 0.5 <= m < 1.0 256 d := float64(exp) * (math.Ln2 / math.Ln10) // log_10(2) 257 258 // adjust m for truncated (integer) decimal exponent e 259 e := int64(d) 260 m *= math.Pow(10, d-float64(e)) 261 262 // ensure 1 <= m < 10 263 switch { 264 case m < 1-0.5e-6: 265 // The %.6g format below rounds m to 5 digits after the 266 // decimal point. Make sure that m*10 < 10 even after 267 // rounding up: m*10 + 0.5e-5 < 10 => m < 1 - 0.5e6. 268 m *= 10 269 e-- 270 case m >= 10: 271 m /= 10 272 e++ 273 } 274 275 return fmt.Sprintf("%s%.6ge%+d", sign, m, e) 276 }