github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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  	"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  //
   180  // floating point input
   181  // required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*]
   182  //
   183  func (a *Mpflt) SetString(as string) {
   184  	for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') {
   185  		as = as[1:]
   186  	}
   187  
   188  	f, ok := a.Val.SetString(as)
   189  	if !ok {
   190  		// At the moment we lose precise error cause;
   191  		// the old code additionally distinguished between:
   192  		// - malformed hex constant
   193  		// - decimal point in hex constant
   194  		// - constant exponent out of range
   195  		// - decimal point and binary point in constant
   196  		// TODO(gri) use different conversion function or check separately
   197  		yyerror("malformed constant: %s", as)
   198  		a.Val.SetFloat64(0)
   199  		return
   200  	}
   201  
   202  	if f.IsInf() {
   203  		yyerror("constant too large: %s", as)
   204  		a.Val.SetFloat64(0)
   205  		return
   206  	}
   207  
   208  	// -0 becomes 0
   209  	if f.Sign() == 0 && f.Signbit() {
   210  		a.Val.SetFloat64(0)
   211  	}
   212  }
   213  
   214  func (f *Mpflt) String() string {
   215  	return fconv(f, 0)
   216  }
   217  
   218  func fconv(fvp *Mpflt, flag FmtFlag) string {
   219  	if flag&FmtSharp == 0 {
   220  		return fvp.Val.Text('b', 0)
   221  	}
   222  
   223  	// use decimal format for error messages
   224  
   225  	// determine sign
   226  	f := &fvp.Val
   227  	var sign string
   228  	if f.Sign() < 0 {
   229  		sign = "-"
   230  		f = new(big.Float).Abs(f)
   231  	} else if flag&FmtSign != 0 {
   232  		sign = "+"
   233  	}
   234  
   235  	// Don't try to convert infinities (will not terminate).
   236  	if f.IsInf() {
   237  		return sign + "Inf"
   238  	}
   239  
   240  	// Use exact fmt formatting if in float64 range (common case):
   241  	// proceed if f doesn't underflow to 0 or overflow to inf.
   242  	if x, _ := f.Float64(); f.Sign() == 0 == (x == 0) && !math.IsInf(x, 0) {
   243  		return fmt.Sprintf("%s%.6g", sign, x)
   244  	}
   245  
   246  	// Out of float64 range. Do approximate manual to decimal
   247  	// conversion to avoid precise but possibly slow Float
   248  	// formatting.
   249  	// f = mant * 2**exp
   250  	var mant big.Float
   251  	exp := f.MantExp(&mant) // 0.5 <= mant < 1.0
   252  
   253  	// approximate float64 mantissa m and decimal exponent d
   254  	// f ~ m * 10**d
   255  	m, _ := mant.Float64()                     // 0.5 <= m < 1.0
   256  	d := float64(exp) * (math.Ln2 / math.Ln10) // log_10(2)
   257  
   258  	// adjust m for truncated (integer) decimal exponent e
   259  	e := int64(d)
   260  	m *= math.Pow(10, d-float64(e))
   261  
   262  	// ensure 1 <= m < 10
   263  	switch {
   264  	case m < 1-0.5e-6:
   265  		// The %.6g format below rounds m to 5 digits after the
   266  		// decimal point. Make sure that m*10 < 10 even after
   267  		// rounding up: m*10 + 0.5e-5 < 10 => m < 1 - 0.5e6.
   268  		m *= 10
   269  		e--
   270  	case m >= 10:
   271  		m /= 10
   272  		e++
   273  	}
   274  
   275  	return fmt.Sprintf("%s%.6ge%+d", sign, m, e)
   276  }