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 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 }
    266