github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/internal/gc/mparith3.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  	"cmd/internal/gc/big"
     9  	"cmd/internal/obj"
    10  	"fmt"
    11  	"math"
    12  )
    13  
    14  /// implements float arihmetic
    15  
    16  func newMpflt() *Mpflt {
    17  	var a Mpflt
    18  	a.Val.SetPrec(Mpprec)
    19  	return &a
    20  }
    21  
    22  func Mpmovefixflt(a *Mpflt, b *Mpint) {
    23  	if b.Ovf {
    24  		// sign doesn't really matter but copy anyway
    25  		a.Val.SetInf(b.Val.Sign() < 0)
    26  		return
    27  	}
    28  	a.Val.SetInt(&b.Val)
    29  }
    30  
    31  func mpmovefltflt(a *Mpflt, b *Mpflt) {
    32  	a.Val.Set(&b.Val)
    33  }
    34  
    35  func mpaddfltflt(a *Mpflt, b *Mpflt) {
    36  	if Mpdebug {
    37  		fmt.Printf("\n%v + %v", Fconv(a, 0), Fconv(b, 0))
    38  	}
    39  
    40  	a.Val.Add(&a.Val, &b.Val)
    41  
    42  	if Mpdebug {
    43  		fmt.Printf(" = %v\n\n", Fconv(a, 0))
    44  	}
    45  }
    46  
    47  func mpaddcflt(a *Mpflt, c float64) {
    48  	var b Mpflt
    49  
    50  	Mpmovecflt(&b, c)
    51  	mpaddfltflt(a, &b)
    52  }
    53  
    54  func mpsubfltflt(a *Mpflt, b *Mpflt) {
    55  	if Mpdebug {
    56  		fmt.Printf("\n%v - %v", Fconv(a, 0), Fconv(b, 0))
    57  	}
    58  
    59  	a.Val.Sub(&a.Val, &b.Val)
    60  
    61  	if Mpdebug {
    62  		fmt.Printf(" = %v\n\n", Fconv(a, 0))
    63  	}
    64  }
    65  
    66  func mpmulfltflt(a *Mpflt, b *Mpflt) {
    67  	if Mpdebug {
    68  		fmt.Printf("%v\n * %v\n", Fconv(a, 0), Fconv(b, 0))
    69  	}
    70  
    71  	a.Val.Mul(&a.Val, &b.Val)
    72  
    73  	if Mpdebug {
    74  		fmt.Printf(" = %v\n\n", Fconv(a, 0))
    75  	}
    76  }
    77  
    78  func mpmulcflt(a *Mpflt, c float64) {
    79  	var b Mpflt
    80  
    81  	Mpmovecflt(&b, c)
    82  	mpmulfltflt(a, &b)
    83  }
    84  
    85  func mpdivfltflt(a *Mpflt, b *Mpflt) {
    86  	if Mpdebug {
    87  		fmt.Printf("%v\n / %v\n", Fconv(a, 0), Fconv(b, 0))
    88  	}
    89  
    90  	a.Val.Quo(&a.Val, &b.Val)
    91  
    92  	if Mpdebug {
    93  		fmt.Printf(" = %v\n\n", Fconv(a, 0))
    94  	}
    95  }
    96  
    97  func mpcmpfltflt(a *Mpflt, b *Mpflt) int {
    98  	return a.Val.Cmp(&b.Val)
    99  }
   100  
   101  func mpcmpfltc(b *Mpflt, c float64) int {
   102  	var a Mpflt
   103  
   104  	Mpmovecflt(&a, c)
   105  	return mpcmpfltflt(b, &a)
   106  }
   107  
   108  func mpgetfltN(a *Mpflt, prec int, bias int) float64 {
   109  	var x float64
   110  	switch prec {
   111  	case 53:
   112  		x, _ = a.Val.Float64()
   113  	case 24:
   114  		// We should be using a.Val.Float32() here but that seems incorrect
   115  		// for certain denormal values (all.bash fails). The current code
   116  		// appears to work for all existing test cases, though there ought
   117  		// to be issues with denormal numbers that are incorrectly rounded.
   118  		// TODO(gri) replace with a.Val.Float32() once correctly working
   119  		// See also: https://github.com/golang/go/issues/10321
   120  		var t Mpflt
   121  		t.Val.SetPrec(24).Set(&a.Val)
   122  		x, _ = t.Val.Float64()
   123  	default:
   124  		panic("unreachable")
   125  	}
   126  
   127  	// check for overflow
   128  	if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
   129  		Yyerror("mpgetflt ovf")
   130  	}
   131  
   132  	return x
   133  }
   134  
   135  func mpgetflt(a *Mpflt) float64 {
   136  	return mpgetfltN(a, 53, -1023)
   137  }
   138  
   139  func mpgetflt32(a *Mpflt) float64 {
   140  	return mpgetfltN(a, 24, -127)
   141  }
   142  
   143  func Mpmovecflt(a *Mpflt, c float64) {
   144  	if Mpdebug {
   145  		fmt.Printf("\nconst %g", c)
   146  	}
   147  
   148  	a.Val.SetFloat64(c)
   149  
   150  	if Mpdebug {
   151  		fmt.Printf(" = %v\n", Fconv(a, 0))
   152  	}
   153  }
   154  
   155  func mpnegflt(a *Mpflt) {
   156  	a.Val.Neg(&a.Val)
   157  }
   158  
   159  //
   160  // floating point input
   161  // required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*]
   162  //
   163  func mpatoflt(a *Mpflt, as string) {
   164  	for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') {
   165  		as = as[1:]
   166  	}
   167  
   168  	f, ok := a.Val.SetString(as)
   169  	if !ok {
   170  		// At the moment we lose precise error cause;
   171  		// the old code additionally distinguished between:
   172  		// - malformed hex constant
   173  		// - decimal point in hex constant
   174  		// - constant exponent out of range
   175  		// - decimal point and binary point in constant
   176  		// TODO(gri) use different conversion function or check separately
   177  		Yyerror("malformed constant: %s", as)
   178  		a.Val.SetUint64(0)
   179  	}
   180  
   181  	if f.IsInf() {
   182  		Yyerror("constant too large: %s", as)
   183  		a.Val.SetUint64(0)
   184  	}
   185  }
   186  
   187  func Fconv(fvp *Mpflt, flag int) string {
   188  	if flag&obj.FmtSharp == 0 {
   189  		return fvp.Val.Format('b', 0)
   190  	}
   191  
   192  	// use decimal format for error messages
   193  
   194  	// determine sign
   195  	f := &fvp.Val
   196  	var sign string
   197  	if fvp.Val.Signbit() {
   198  		sign = "-"
   199  		f = new(big.Float).Abs(f)
   200  	} else if flag&obj.FmtSign != 0 {
   201  		sign = "+"
   202  	}
   203  
   204  	// Use fmt formatting if in float64 range (common case).
   205  	if x, _ := f.Float64(); !math.IsInf(x, 0) {
   206  		return fmt.Sprintf("%s%.6g", sign, x)
   207  	}
   208  
   209  	// Out of float64 range. Do approximate manual to decimal
   210  	// conversion to avoid precise but possibly slow Float
   211  	// formatting. The exponent is > 0 since a negative out-
   212  	// of-range exponent would have underflowed and led to 0.
   213  	// f = mant * 2**exp
   214  	var mant big.Float
   215  	exp := float64(f.MantExp(&mant)) // 0.5 <= mant < 1.0, exp > 0
   216  
   217  	// approximate float64 mantissa m and decimal exponent d
   218  	// f ~ m * 10**d
   219  	m, _ := mant.Float64()            // 0.5 <= m < 1.0
   220  	d := exp * (math.Ln2 / math.Ln10) // log_10(2)
   221  
   222  	// adjust m for truncated (integer) decimal exponent e
   223  	e := int64(d)
   224  	m *= math.Pow(10, d-float64(e))
   225  	for m >= 10 {
   226  		m /= 10
   227  		e++
   228  	}
   229  
   230  	return fmt.Sprintf("%s%.5fe+%d", sign, m, e)
   231  }