Home | History | Annotate | Download | only in gc
      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