github.com/go-xe2/third@v1.0.3/golang.org/x/text/internal/number/decimal.go (about)

     1  // Copyright 2017 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  //go:generate stringer -type RoundingMode
     6  
     7  package number
     8  
     9  import (
    10  	"math"
    11  	"strconv"
    12  )
    13  
    14  // RoundingMode determines how a number is rounded to the desired precision.
    15  type RoundingMode byte
    16  
    17  const (
    18  	ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant.
    19  	ToNearestZero                     // towards the nearest integer, or towards zero if equidistant.
    20  	ToNearestAway                     // towards the nearest integer, or away from zero if equidistant.
    21  	ToPositiveInf                     // towards infinity
    22  	ToNegativeInf                     // towards negative infinity
    23  	ToZero                            // towards zero
    24  	AwayFromZero                      // away from zero
    25  	numModes
    26  )
    27  
    28  const maxIntDigits = 20
    29  
    30  // A Decimal represents a floating point number in decimal format.
    31  // Digits represents a number [0, 1.0), and the absolute value represented by
    32  // Decimal is Digits * 10^Exp. Leading and trailing zeros may be omitted and Exp
    33  // may point outside a valid position in Digits.
    34  //
    35  // Examples:
    36  //      Number     Decimal
    37  //      12345      Digits: [1, 2, 3, 4, 5], Exp: 5
    38  //      12.345     Digits: [1, 2, 3, 4, 5], Exp: 2
    39  //      12000      Digits: [1, 2],          Exp: 5
    40  //      12000.00   Digits: [1, 2],          Exp: 5
    41  //      0.00123    Digits: [1, 2, 3],       Exp: -2
    42  //      0          Digits: [],              Exp: 0
    43  type Decimal struct {
    44  	digits
    45  
    46  	buf [maxIntDigits]byte
    47  }
    48  
    49  type digits struct {
    50  	Digits []byte // mantissa digits, big-endian
    51  	Exp    int32  // exponent
    52  	Neg    bool
    53  	Inf    bool // Takes precedence over Digits and Exp.
    54  	NaN    bool // Takes precedence over Inf.
    55  }
    56  
    57  // Digits represents a floating point number represented in digits of the
    58  // base in which a number is to be displayed. It is similar to Decimal, but
    59  // keeps track of trailing fraction zeros and the comma placement for
    60  // engineering notation. Digits must have at least one digit.
    61  //
    62  // Examples:
    63  //      Number     Decimal
    64  //    decimal
    65  //      12345      Digits: [1, 2, 3, 4, 5], Exp: 5  End: 5
    66  //      12.345     Digits: [1, 2, 3, 4, 5], Exp: 2  End: 5
    67  //      12000      Digits: [1, 2],          Exp: 5  End: 5
    68  //      12000.00   Digits: [1, 2],          Exp: 5  End: 7
    69  //      0.00123    Digits: [1, 2, 3],       Exp: -2 End: 3
    70  //      0          Digits: [],              Exp: 0  End: 1
    71  //    scientific (actual exp is Exp - Comma)
    72  //      0e0        Digits: [0],             Exp: 1, End: 1, Comma: 1
    73  //      .0e0       Digits: [0],             Exp: 0, End: 1, Comma: 0
    74  //      0.0e0      Digits: [0],             Exp: 1, End: 2, Comma: 1
    75  //      1.23e4     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 1
    76  //      .123e5     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 0
    77  //    engineering
    78  //      12.3e3     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 2
    79  type Digits struct {
    80  	digits
    81  	// End indicates the end position of the number.
    82  	End int32 // For decimals Exp <= End. For scientific len(Digits) <= End.
    83  	// Comma is used for the comma position for scientific (always 0 or 1) and
    84  	// engineering notation (always 0, 1, 2, or 3).
    85  	Comma uint8
    86  	// IsScientific indicates whether this number is to be rendered as a
    87  	// scientific number.
    88  	IsScientific bool
    89  }
    90  
    91  func (d *Digits) NumFracDigits() int {
    92  	if d.Exp >= d.End {
    93  		return 0
    94  	}
    95  	return int(d.End - d.Exp)
    96  }
    97  
    98  // normalize returns a new Decimal with leading and trailing zeros removed.
    99  func (d *Decimal) normalize() (n Decimal) {
   100  	n = *d
   101  	b := n.Digits
   102  	// Strip leading zeros. Resulting number of digits is significant digits.
   103  	for len(b) > 0 && b[0] == 0 {
   104  		b = b[1:]
   105  		n.Exp--
   106  	}
   107  	// Strip trailing zeros
   108  	for len(b) > 0 && b[len(b)-1] == 0 {
   109  		b = b[:len(b)-1]
   110  	}
   111  	if len(b) == 0 {
   112  		n.Exp = 0
   113  	}
   114  	n.Digits = b
   115  	return n
   116  }
   117  
   118  func (d *Decimal) clear() {
   119  	b := d.Digits
   120  	if b == nil {
   121  		b = d.buf[:0]
   122  	}
   123  	*d = Decimal{}
   124  	d.Digits = b[:0]
   125  }
   126  
   127  func (x *Decimal) String() string {
   128  	if x.NaN {
   129  		return "NaN"
   130  	}
   131  	var buf []byte
   132  	if x.Neg {
   133  		buf = append(buf, '-')
   134  	}
   135  	if x.Inf {
   136  		buf = append(buf, "Inf"...)
   137  		return string(buf)
   138  	}
   139  	switch {
   140  	case len(x.Digits) == 0:
   141  		buf = append(buf, '0')
   142  	case x.Exp <= 0:
   143  		// 0.00ddd
   144  		buf = append(buf, "0."...)
   145  		buf = appendZeros(buf, -int(x.Exp))
   146  		buf = appendDigits(buf, x.Digits)
   147  
   148  	case /* 0 < */ int(x.Exp) < len(x.Digits):
   149  		// dd.ddd
   150  		buf = appendDigits(buf, x.Digits[:x.Exp])
   151  		buf = append(buf, '.')
   152  		buf = appendDigits(buf, x.Digits[x.Exp:])
   153  
   154  	default: // len(x.Digits) <= x.Exp
   155  		// ddd00
   156  		buf = appendDigits(buf, x.Digits)
   157  		buf = appendZeros(buf, int(x.Exp)-len(x.Digits))
   158  	}
   159  	return string(buf)
   160  }
   161  
   162  func appendDigits(buf []byte, digits []byte) []byte {
   163  	for _, c := range digits {
   164  		buf = append(buf, c+'0')
   165  	}
   166  	return buf
   167  }
   168  
   169  // appendZeros appends n 0 digits to buf and returns buf.
   170  func appendZeros(buf []byte, n int) []byte {
   171  	for ; n > 0; n-- {
   172  		buf = append(buf, '0')
   173  	}
   174  	return buf
   175  }
   176  
   177  func (d *digits) round(mode RoundingMode, n int) {
   178  	if n >= len(d.Digits) {
   179  		return
   180  	}
   181  	// Make rounding decision: The result mantissa is truncated ("rounded down")
   182  	// by default. Decide if we need to increment, or "round up", the (unsigned)
   183  	// mantissa.
   184  	inc := false
   185  	switch mode {
   186  	case ToNegativeInf:
   187  		inc = d.Neg
   188  	case ToPositiveInf:
   189  		inc = !d.Neg
   190  	case ToZero:
   191  		// nothing to do
   192  	case AwayFromZero:
   193  		inc = true
   194  	case ToNearestEven:
   195  		inc = d.Digits[n] > 5 || d.Digits[n] == 5 &&
   196  			(len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0)
   197  	case ToNearestAway:
   198  		inc = d.Digits[n] >= 5
   199  	case ToNearestZero:
   200  		inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1
   201  	default:
   202  		panic("unreachable")
   203  	}
   204  	if inc {
   205  		d.roundUp(n)
   206  	} else {
   207  		d.roundDown(n)
   208  	}
   209  }
   210  
   211  // roundFloat rounds a floating point number.
   212  func (r RoundingMode) roundFloat(x float64) float64 {
   213  	// Make rounding decision: The result mantissa is truncated ("rounded down")
   214  	// by default. Decide if we need to increment, or "round up", the (unsigned)
   215  	// mantissa.
   216  	abs := x
   217  	if x < 0 {
   218  		abs = -x
   219  	}
   220  	i, f := math.Modf(abs)
   221  	if f == 0.0 {
   222  		return x
   223  	}
   224  	inc := false
   225  	switch r {
   226  	case ToNegativeInf:
   227  		inc = x < 0
   228  	case ToPositiveInf:
   229  		inc = x >= 0
   230  	case ToZero:
   231  		// nothing to do
   232  	case AwayFromZero:
   233  		inc = true
   234  	case ToNearestEven:
   235  		// TODO: check overflow
   236  		inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0
   237  	case ToNearestAway:
   238  		inc = f >= 0.5
   239  	case ToNearestZero:
   240  		inc = f > 0.5
   241  	default:
   242  		panic("unreachable")
   243  	}
   244  	if inc {
   245  		i += 1
   246  	}
   247  	if abs != x {
   248  		i = -i
   249  	}
   250  	return i
   251  }
   252  
   253  func (x *digits) roundUp(n int) {
   254  	if n < 0 || n >= len(x.Digits) {
   255  		return // nothing to do
   256  	}
   257  	// find first digit < 9
   258  	for n > 0 && x.Digits[n-1] >= 9 {
   259  		n--
   260  	}
   261  
   262  	if n == 0 {
   263  		// all digits are 9s => round up to 1 and update exponent
   264  		x.Digits[0] = 1 // ok since len(x.Digits) > n
   265  		x.Digits = x.Digits[:1]
   266  		x.Exp++
   267  		return
   268  	}
   269  	x.Digits[n-1]++
   270  	x.Digits = x.Digits[:n]
   271  	// x already trimmed
   272  }
   273  
   274  func (x *digits) roundDown(n int) {
   275  	if n < 0 || n >= len(x.Digits) {
   276  		return // nothing to do
   277  	}
   278  	x.Digits = x.Digits[:n]
   279  	trim(x)
   280  }
   281  
   282  // trim cuts off any trailing zeros from x's mantissa;
   283  // they are meaningless for the value of x.
   284  func trim(x *digits) {
   285  	i := len(x.Digits)
   286  	for i > 0 && x.Digits[i-1] == 0 {
   287  		i--
   288  	}
   289  	x.Digits = x.Digits[:i]
   290  	if i == 0 {
   291  		x.Exp = 0
   292  	}
   293  }
   294  
   295  // A Converter converts a number into decimals according to the given rounding
   296  // criteria.
   297  type Converter interface {
   298  	Convert(d *Decimal, r RoundingContext)
   299  }
   300  
   301  const (
   302  	signed   = true
   303  	unsigned = false
   304  )
   305  
   306  // Convert converts the given number to the decimal representation using the
   307  // supplied RoundingContext.
   308  func (d *Decimal) Convert(r RoundingContext, number interface{}) {
   309  	switch f := number.(type) {
   310  	case Converter:
   311  		d.clear()
   312  		f.Convert(d, r)
   313  	case float32:
   314  		d.ConvertFloat(r, float64(f), 32)
   315  	case float64:
   316  		d.ConvertFloat(r, f, 64)
   317  	case int:
   318  		d.ConvertInt(r, signed, uint64(f))
   319  	case int8:
   320  		d.ConvertInt(r, signed, uint64(f))
   321  	case int16:
   322  		d.ConvertInt(r, signed, uint64(f))
   323  	case int32:
   324  		d.ConvertInt(r, signed, uint64(f))
   325  	case int64:
   326  		d.ConvertInt(r, signed, uint64(f))
   327  	case uint:
   328  		d.ConvertInt(r, unsigned, uint64(f))
   329  	case uint8:
   330  		d.ConvertInt(r, unsigned, uint64(f))
   331  	case uint16:
   332  		d.ConvertInt(r, unsigned, uint64(f))
   333  	case uint32:
   334  		d.ConvertInt(r, unsigned, uint64(f))
   335  	case uint64:
   336  		d.ConvertInt(r, unsigned, f)
   337  
   338  	default:
   339  		d.NaN = true
   340  		// TODO:
   341  		// case string: if produced by strconv, allows for easy arbitrary pos.
   342  		// case reflect.Value:
   343  		// case big.Float
   344  		// case big.Int
   345  		// case big.Rat?
   346  		// catch underlyings using reflect or will this already be done by the
   347  		//    message package?
   348  	}
   349  }
   350  
   351  // ConvertInt converts an integer to decimals.
   352  func (d *Decimal) ConvertInt(r RoundingContext, signed bool, x uint64) {
   353  	if r.Increment > 0 {
   354  		// TODO: if uint64 is too large, fall back to float64
   355  		if signed {
   356  			d.ConvertFloat(r, float64(int64(x)), 64)
   357  		} else {
   358  			d.ConvertFloat(r, float64(x), 64)
   359  		}
   360  		return
   361  	}
   362  	d.clear()
   363  	if signed && int64(x) < 0 {
   364  		x = uint64(-int64(x))
   365  		d.Neg = true
   366  	}
   367  	d.fillIntDigits(x)
   368  	d.Exp = int32(len(d.Digits))
   369  }
   370  
   371  // ConvertFloat converts a floating point number to decimals.
   372  func (d *Decimal) ConvertFloat(r RoundingContext, x float64, size int) {
   373  	d.clear()
   374  	if math.IsNaN(x) {
   375  		d.NaN = true
   376  		return
   377  	}
   378  	// Simple case: decimal notation
   379  	if r.Increment > 0 {
   380  		scale := int(r.IncrementScale)
   381  		mult := 1.0
   382  		if scale > len(scales) {
   383  			mult = math.Pow(10, float64(scale))
   384  		} else {
   385  			mult = scales[scale]
   386  		}
   387  		// We multiply x instead of dividing inc as it gives less rounding
   388  		// issues.
   389  		x *= mult
   390  		x /= float64(r.Increment)
   391  		x = r.Mode.roundFloat(x)
   392  		x *= float64(r.Increment)
   393  		x /= mult
   394  	}
   395  
   396  	abs := x
   397  	if x < 0 {
   398  		d.Neg = true
   399  		abs = -x
   400  	}
   401  	if math.IsInf(abs, 1) {
   402  		d.Inf = true
   403  		return
   404  	}
   405  
   406  	// By default we get the exact decimal representation.
   407  	verb := byte('g')
   408  	prec := -1
   409  	// As the strconv API does not return the rounding accuracy, we can only
   410  	// round using ToNearestEven.
   411  	if r.Mode == ToNearestEven {
   412  		if n := r.RoundSignificantDigits(); n >= 0 {
   413  			prec = n
   414  		} else if n = r.RoundFractionDigits(); n >= 0 {
   415  			prec = n
   416  			verb = 'f'
   417  		}
   418  	} else {
   419  		// TODO: At this point strconv's rounding is imprecise to the point that
   420  		// it is not useable for this purpose.
   421  		// See https://github.com/golang/go/issues/21714
   422  		// If rounding is requested, we ask for a large number of digits and
   423  		// round from there to simulate rounding only once.
   424  		// Ideally we would have strconv export an AppendDigits that would take
   425  		// a rounding mode and/or return an accuracy. Something like this would
   426  		// work:
   427  		// AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)
   428  		hasPrec := r.RoundSignificantDigits() >= 0
   429  		hasScale := r.RoundFractionDigits() >= 0
   430  		if hasPrec || hasScale {
   431  			// prec is the number of mantissa bits plus some extra for safety.
   432  			// We need at least the number of mantissa bits as decimals to
   433  			// accurately represent the floating point without rounding, as each
   434  			// bit requires one more decimal to represent: 0.5, 0.25, 0.125, ...
   435  			prec = 60
   436  		}
   437  	}
   438  
   439  	b := strconv.AppendFloat(d.Digits[:0], abs, verb, prec, size)
   440  	i := 0
   441  	k := 0
   442  	beforeDot := 1
   443  	for i < len(b) {
   444  		if c := b[i]; '0' <= c && c <= '9' {
   445  			b[k] = c - '0'
   446  			k++
   447  			d.Exp += int32(beforeDot)
   448  		} else if c == '.' {
   449  			beforeDot = 0
   450  			d.Exp = int32(k)
   451  		} else {
   452  			break
   453  		}
   454  		i++
   455  	}
   456  	d.Digits = b[:k]
   457  	if i != len(b) {
   458  		i += len("e")
   459  		pSign := i
   460  		exp := 0
   461  		for i++; i < len(b); i++ {
   462  			exp *= 10
   463  			exp += int(b[i] - '0')
   464  		}
   465  		if b[pSign] == '-' {
   466  			exp = -exp
   467  		}
   468  		d.Exp = int32(exp) + 1
   469  	}
   470  }
   471  
   472  func (d *Decimal) fillIntDigits(x uint64) {
   473  	if cap(d.Digits) < maxIntDigits {
   474  		d.Digits = d.buf[:]
   475  	} else {
   476  		d.Digits = d.buf[:maxIntDigits]
   477  	}
   478  	i := 0
   479  	for ; x > 0; x /= 10 {
   480  		d.Digits[i] = byte(x % 10)
   481  		i++
   482  	}
   483  	d.Digits = d.Digits[:i]
   484  	for p := 0; p < i; p++ {
   485  		i--
   486  		d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p]
   487  	}
   488  }
   489  
   490  var scales [70]float64
   491  
   492  func init() {
   493  	x := 1.0
   494  	for i := range scales {
   495  		scales[i] = x
   496  		x *= 10
   497  	}
   498  }