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 	"cmd/compile/internal/big"
      9 	"cmd/internal/obj"
     10 	"fmt"
     11 	"math"
     12 	"strings"
     13 )
     14 
     15 /// implements float arihmetic
     16 
     17 func newMpflt() *Mpflt {
     18 	var a Mpflt
     19 	a.Val.SetPrec(Mpprec)
     20 	return &a
     21 }
     22 
     23 func Mpmovefixflt(a *Mpflt, b *Mpint) {
     24 	if b.Ovf {
     25 		// sign doesn't really matter but copy anyway
     26 		a.Val.SetInf(b.Val.Sign() < 0)
     27 		return
     28 	}
     29 	a.Val.SetInt(&b.Val)
     30 }
     31 
     32 func mpmovefltflt(a *Mpflt, b *Mpflt) {
     33 	a.Val.Set(&b.Val)
     34 }
     35 
     36 func mpaddfltflt(a *Mpflt, b *Mpflt) {
     37 	if Mpdebug {
     38 		fmt.Printf("\n%v + %v", a, b)
     39 	}
     40 
     41 	a.Val.Add(&a.Val, &b.Val)
     42 
     43 	if Mpdebug {
     44 		fmt.Printf(" = %v\n\n", a)
     45 	}
     46 }
     47 
     48 func mpaddcflt(a *Mpflt, c float64) {
     49 	var b Mpflt
     50 
     51 	Mpmovecflt(&b, c)
     52 	mpaddfltflt(a, &b)
     53 }
     54 
     55 func mpsubfltflt(a *Mpflt, b *Mpflt) {
     56 	if Mpdebug {
     57 		fmt.Printf("\n%v - %v", a, b)
     58 	}
     59 
     60 	a.Val.Sub(&a.Val, &b.Val)
     61 
     62 	if Mpdebug {
     63 		fmt.Printf(" = %v\n\n", a)
     64 	}
     65 }
     66 
     67 func mpmulfltflt(a *Mpflt, b *Mpflt) {
     68 	if Mpdebug {
     69 		fmt.Printf("%v\n * %v\n", a, b)
     70 	}
     71 
     72 	a.Val.Mul(&a.Val, &b.Val)
     73 
     74 	if Mpdebug {
     75 		fmt.Printf(" = %v\n\n", a)
     76 	}
     77 }
     78 
     79 func mpmulcflt(a *Mpflt, c float64) {
     80 	var b Mpflt
     81 
     82 	Mpmovecflt(&b, c)
     83 	mpmulfltflt(a, &b)
     84 }
     85 
     86 func mpdivfltflt(a *Mpflt, b *Mpflt) {
     87 	if Mpdebug {
     88 		fmt.Printf("%v\n / %v\n", a, b)
     89 	}
     90 
     91 	a.Val.Quo(&a.Val, &b.Val)
     92 
     93 	if Mpdebug {
     94 		fmt.Printf(" = %v\n\n", a)
     95 	}
     96 }
     97 
     98 func mpcmpfltflt(a *Mpflt, b *Mpflt) int {
     99 	return a.Val.Cmp(&b.Val)
    100 }
    101 
    102 func mpcmpfltc(b *Mpflt, c float64) int {
    103 	var a Mpflt
    104 
    105 	Mpmovecflt(&a, c)
    106 	return mpcmpfltflt(b, &a)
    107 }
    108 
    109 func mpgetflt(a *Mpflt) float64 {
    110 	x, _ := a.Val.Float64()
    111 
    112 	// check for overflow
    113 	if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
    114 		Yyerror("mpgetflt ovf")
    115 	}
    116 
    117 	return x
    118 }
    119 
    120 func mpgetflt32(a *Mpflt) float64 {
    121 	x32, _ := a.Val.Float32()
    122 	x := float64(x32)
    123 
    124 	// check for overflow
    125 	if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
    126 		Yyerror("mpgetflt32 ovf")
    127 	}
    128 
    129 	return x
    130 }
    131 
    132 func Mpmovecflt(a *Mpflt, c float64) {
    133 	if Mpdebug {
    134 		fmt.Printf("\nconst %g", c)
    135 	}
    136 
    137 	a.Val.SetFloat64(c)
    138 
    139 	if Mpdebug {
    140 		fmt.Printf(" = %v\n", a)
    141 	}
    142 }
    143 
    144 func mpnegflt(a *Mpflt) {
    145 	a.Val.Neg(&a.Val)
    146 }
    147 
    148 //
    149 // floating point input
    150 // required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*]
    151 //
    152 func mpatoflt(a *Mpflt, as string) {
    153 	for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') {
    154 		as = as[1:]
    155 	}
    156 
    157 	// The spec requires accepting exponents that fit in int32.
    158 	// Don't accept much more than that.
    159 	// Count digits in exponent and stop early if there are too many.
    160 	if i := strings.Index(as, "e"); i >= 0 {
    161 		i++
    162 		if i < len(as) && (as[i] == '-' || as[i] == '+') {
    163 			i++
    164 		}
    165 		for i < len(as) && as[i] == '0' {
    166 			i++
    167 		}
    168 		// TODO(rsc): This should be > 10, because we're supposed
    169 		// to accept any signed 32-bit int as an exponent.
    170 		// But that's not working terribly well, so we deviate from the
    171 		// spec in order to make sure that what we accept works.
    172 		// We can remove this restriction once those larger exponents work.
    173 		// See golang.org/issue/11326 and test/fixedbugs/issue11326*.go.
    174 		if len(as)-i > 8 {
    175 			Yyerror("malformed constant: %s (exponent too large)", as)
    176 			a.Val.SetUint64(0)
    177 			return
    178 		}
    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.SetUint64(0)
    192 		return
    193 	}
    194 
    195 	if f.IsInf() {
    196 		Yyerror("constant too large: %s", as)
    197 		a.Val.SetUint64(0)
    198 		return
    199 	}
    200 }
    201 
    202 func (f *Mpflt) String() string {
    203 	return Fconv(f, 0)
    204 }
    205 
    206 func Fconv(fvp *Mpflt, flag int) string {
    207 	if flag&obj.FmtSharp == 0 {
    208 		return fvp.Val.Text('b', 0)
    209 	}
    210 
    211 	// use decimal format for error messages
    212 
    213 	// determine sign
    214 	f := &fvp.Val
    215 	var sign string
    216 	if fvp.Val.Signbit() {
    217 		sign = "-"
    218 		f = new(big.Float).Abs(f)
    219 	} else if flag&obj.FmtSign != 0 {
    220 		sign = "+"
    221 	}
    222 
    223 	// Use fmt formatting if in float64 range (common case).
    224 	if x, _ := f.Float64(); !math.IsInf(x, 0) {
    225 		return fmt.Sprintf("%s%.6g", sign, x)
    226 	}
    227 
    228 	// Out of float64 range. Do approximate manual to decimal
    229 	// conversion to avoid precise but possibly slow Float
    230 	// formatting. The exponent is > 0 since a negative out-
    231 	// of-range exponent would have underflowed and led to 0.
    232 	// f = mant * 2**exp
    233 	var mant big.Float
    234 	exp := float64(f.MantExp(&mant)) // 0.5 <= mant < 1.0, exp > 0
    235 
    236 	// approximate float64 mantissa m and decimal exponent d
    237 	// f ~ m * 10**d
    238 	m, _ := mant.Float64()            // 0.5 <= m < 1.0
    239 	d := exp * (math.Ln2 / math.Ln10) // log_10(2)
    240 
    241 	// adjust m for truncated (integer) decimal exponent e
    242 	e := int64(d)
    243 	m *= math.Pow(10, d-float64(e))
    244 	for m >= 10 {
    245 		m /= 10
    246 		e++
    247 	}
    248 
    249 	return fmt.Sprintf("%s%.5fe+%d", sign, m, e)
    250 }
    251