github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/gc/mpfloat.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/compile/internal/big"
     9  	"fmt"
    10  	"math"
    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("mpgetflt ovf")
   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("mpgetflt32 ovf")
   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  }