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 (a *Mpflt) SetInt(b *Mpint) { 41 if b.Ovf { 42 // sign doesn't really matter but copy anyway 43 a.Val.SetInf(b.Val.Sign() < 0) 44 return 45 } 46 a.Val.SetInt(&b.Val) 47 } 48 49 func (a *Mpflt) Set(b *Mpflt) { 50 a.Val.Set(&b.Val) 51 } 52 53 func (a *Mpflt) Add(b *Mpflt) { 54 if Mpdebug { 55 fmt.Printf("\n%v + %v", a, b) 56 } 57 58 a.Val.Add(&a.Val, &b.Val) 59 60 if Mpdebug { 61 fmt.Printf(" = %v\n\n", a) 62 } 63 } 64 65 func (a *Mpflt) AddFloat64(c float64) { 66 var b Mpflt 67 68 b.SetFloat64(c) 69 a.Add(&b) 70 } 71 72 func (a *Mpflt) Sub(b *Mpflt) { 73 if Mpdebug { 74 fmt.Printf("\n%v - %v", a, b) 75 } 76 77 a.Val.Sub(&a.Val, &b.Val) 78 79 if Mpdebug { 80 fmt.Printf(" = %v\n\n", a) 81 } 82 } 83 84 func (a *Mpflt) Mul(b *Mpflt) { 85 if Mpdebug { 86 fmt.Printf("%v\n * %v\n", a, b) 87 } 88 89 a.Val.Mul(&a.Val, &b.Val) 90 91 if Mpdebug { 92 fmt.Printf(" = %v\n\n", a) 93 } 94 } 95 96 func (a *Mpflt) MulFloat64(c float64) { 97 var b Mpflt 98 99 b.SetFloat64(c) 100 a.Mul(&b) 101 } 102 103 func (a *Mpflt) Quo(b *Mpflt) { 104 if Mpdebug { 105 fmt.Printf("%v\n / %v\n", a, b) 106 } 107 108 a.Val.Quo(&a.Val, &b.Val) 109 110 if Mpdebug { 111 fmt.Printf(" = %v\n\n", a) 112 } 113 } 114 115 func (a *Mpflt) Cmp(b *Mpflt) int { 116 return a.Val.Cmp(&b.Val) 117 } 118 119 func (a *Mpflt) CmpFloat64(c float64) int { 120 if c == 0 { 121 return a.Val.Sign() // common case shortcut 122 } 123 return a.Val.Cmp(big.NewFloat(c)) 124 } 125 126 func (a *Mpflt) Float64() float64 { 127 x, _ := a.Val.Float64() 128 129 // check for overflow 130 if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { 131 yyerror("ovf in Mpflt Float64") 132 } 133 134 return x + 0 // avoid -0 (should not be needed, but be conservative) 135 } 136 137 func (a *Mpflt) Float32() float64 { 138 x32, _ := a.Val.Float32() 139 x := float64(x32) 140 141 // check for overflow 142 if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { 143 yyerror("ovf in Mpflt Float32") 144 } 145 146 return x + 0 // avoid -0 (should not be needed, but be conservative) 147 } 148 149 func (a *Mpflt) SetFloat64(c float64) { 150 if Mpdebug { 151 fmt.Printf("\nconst %g", c) 152 } 153 154 // convert -0 to 0 155 if c == 0 { 156 c = 0 157 } 158 a.Val.SetFloat64(c) 159 160 if Mpdebug { 161 fmt.Printf(" = %v\n", a) 162 } 163 } 164 165 func (a *Mpflt) Neg() { 166 // avoid -0 167 if a.Val.Sign() != 0 { 168 a.Val.Neg(&a.Val) 169 } 170 } 171 172 // 173 // floating point input 174 // required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*] 175 // 176 func (a *Mpflt) SetString(as string) { 177 for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') { 178 as = as[1:] 179 } 180 181 f, ok := a.Val.SetString(as) 182 if !ok { 183 // At the moment we lose precise error cause; 184 // the old code additionally distinguished between: 185 // - malformed hex constant 186 // - decimal point in hex constant 187 // - constant exponent out of range 188 // - decimal point and binary point in constant 189 // TODO(gri) use different conversion function or check separately 190 yyerror("malformed constant: %s", as) 191 a.Val.SetFloat64(0) 192 return 193 } 194 195 if f.IsInf() { 196 yyerror("constant too large: %s", as) 197 a.Val.SetFloat64(0) 198 return 199 } 200 201 // -0 becomes 0 202 if f.Sign() == 0 && f.Signbit() { 203 a.Val.SetFloat64(0) 204 } 205 } 206 207 func (f *Mpflt) String() string { 208 return fconv(f, 0) 209 } 210 211 func fconv(fvp *Mpflt, flag FmtFlag) string { 212 if flag&FmtSharp == 0 { 213 return fvp.Val.Text('b', 0) 214 } 215 216 // use decimal format for error messages 217 218 // determine sign 219 f := &fvp.Val 220 var sign string 221 if f.Sign() < 0 { 222 sign = "-" 223 f = new(big.Float).Abs(f) 224 } else if flag&FmtSign != 0 { 225 sign = "+" 226 } 227 228 // Don't try to convert infinities (will not terminate). 229 if f.IsInf() { 230 return sign + "Inf" 231 } 232 233 // Use exact fmt formatting if in float64 range (common case): 234 // proceed if f doesn't underflow to 0 or overflow to inf. 235 if x, _ := f.Float64(); f.Sign() == 0 == (x == 0) && !math.IsInf(x, 0) { 236 return fmt.Sprintf("%s%.6g", sign, x) 237 } 238 239 // Out of float64 range. Do approximate manual to decimal 240 // conversion to avoid precise but possibly slow Float 241 // formatting. 242 // f = mant * 2**exp 243 var mant big.Float 244 exp := f.MantExp(&mant) // 0.5 <= mant < 1.0 245 246 // approximate float64 mantissa m and decimal exponent d 247 // f ~ m * 10**d 248 m, _ := mant.Float64() // 0.5 <= m < 1.0 249 d := float64(exp) * (math.Ln2 / math.Ln10) // log_10(2) 250 251 // adjust m for truncated (integer) decimal exponent e 252 e := int64(d) 253 m *= math.Pow(10, d-float64(e)) 254 255 // ensure 1 <= m < 10 256 switch { 257 case m < 1-0.5e-6: 258 // The %.6g format below rounds m to 5 digits after the 259 // decimal point. Make sure that m*10 < 10 even after 260 // rounding up: m*10 + 0.5e-5 < 10 => m < 1 - 0.5e6. 261 m *= 10 262 e-- 263 case m >= 10: 264 m /= 10 265 e++ 266 } 267 268 return fmt.Sprintf("%s%.6ge%+d", sign, m, e) 269 } 270