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