summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/text/internal/number/decimal.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/text/internal/number/decimal.go')
-rw-r--r--vendor/golang.org/x/text/internal/number/decimal.go185
1 files changed, 112 insertions, 73 deletions
diff --git a/vendor/golang.org/x/text/internal/number/decimal.go b/vendor/golang.org/x/text/internal/number/decimal.go
index 199c7e416..62074e7d7 100644
--- a/vendor/golang.org/x/text/internal/number/decimal.go
+++ b/vendor/golang.org/x/text/internal/number/decimal.go
@@ -25,22 +25,39 @@ const (
numModes
)
-// A RoundingContext indicates how a number should be converted to digits.
-type RoundingContext struct {
- Mode RoundingMode
- Increment int32 // if > 0, round to Increment * 10^-Scale
+const maxIntDigits = 20
+
+// A Decimal represents a floating point number in decimal format.
+// Digits represents a number [0, 1.0), and the absolute value represented by
+// Decimal is Digits * 10^Exp. Leading and trailing zeros may be omitted and Exp
+// may point outside a valid position in Digits.
+//
+// Examples:
+// Number Decimal
+// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5
+// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2
+// 12000 Digits: [1, 2], Exp: 5
+// 12000.00 Digits: [1, 2], Exp: 5
+// 0.00123 Digits: [1, 2, 3], Exp: -2
+// 0 Digits: [], Exp: 0
+type Decimal struct {
+ digits
- Precision int32 // maximum number of significant digits.
- Scale int32 // maximum number of decimals after the dot.
+ buf [maxIntDigits]byte
}
-const maxIntDigits = 20
+type digits struct {
+ Digits []byte // mantissa digits, big-endian
+ Exp int32 // exponent
+ Neg bool
+ Inf bool // Takes precedence over Digits and Exp.
+ NaN bool // Takes precedence over Inf.
+}
-// A Decimal represents floating point number represented in digits of the base
-// in which a number is to be displayed. Digits represents a number [0, 1.0),
-// and the absolute value represented by Decimal is Digits * 10^Exp.
-// Leading and trailing zeros may be omitted and Exp may point outside a valid
-// position in Digits.
+// Digits represents a floating point number represented in digits of the
+// base in which a number is to be displayed. It is similar to Decimal, but
+// keeps track of trailing fraction zeros and the comma placement for
+// engineering notation. Digits must have at least one digit.
//
// Examples:
// Number Decimal
@@ -51,25 +68,31 @@ const maxIntDigits = 20
// 12000.00 Digits: [1, 2], Exp: 5 End: 7
// 0.00123 Digits: [1, 2, 3], Exp: -2 End: 3
// 0 Digits: [], Exp: 0 End: 1
-// scientific:
-// 0 Digits: [], Exp: 0, End: 1, Comma: 0
+// scientific (actual exp is Exp - Comma)
+// 0e0 Digits: [0], Exp: 1, End: 1, Comma: 1
+// .0e0 Digits: [0], Exp: 0, End: 1, Comma: 0
+// 0.0e0 Digits: [0], Exp: 1, End: 2, Comma: 1
// 1.23e4 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 1
+// .123e5 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 0
// engineering
// 12.3e3 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 2
-type Decimal struct {
- Digits []byte // mantissa digits, big-endian
- Exp int32 // exponent
+type Digits struct {
+ digits
// End indicates the end position of the number.
End int32 // For decimals Exp <= End. For scientific len(Digits) <= End.
// Comma is used for the comma position for scientific (always 0 or 1) and
// engineering notation (always 0, 1, 2, or 3).
Comma uint8
+ // IsScientific indicates whether this number is to be rendered as a
+ // scientific number.
+ IsScientific bool
+}
- Neg bool
- Inf bool // Takes precedence over Digits and Exp.
- NaN bool // Takes precedence over Inf.
-
- buf [maxIntDigits]byte
+func (d *Digits) NumFracDigits() int {
+ if d.Exp >= d.End {
+ return 0
+ }
+ return int(d.End - d.Exp)
}
// normalize returns a new Decimal with leading and trailing zeros removed.
@@ -151,7 +174,7 @@ func appendZeros(buf []byte, n int) []byte {
return buf
}
-func (d *Decimal) round(mode RoundingMode, n int) {
+func (d *digits) round(mode RoundingMode, n int) {
if n >= len(d.Digits) {
return
}
@@ -227,7 +250,7 @@ func (r RoundingMode) roundFloat(x float64) float64 {
return i
}
-func (x *Decimal) roundUp(n int) {
+func (x *digits) roundUp(n int) {
if n < 0 || n >= len(x.Digits) {
return // nothing to do
}
@@ -248,7 +271,7 @@ func (x *Decimal) roundUp(n int) {
// x already trimmed
}
-func (x *Decimal) roundDown(n int) {
+func (x *digits) roundDown(n int) {
if n < 0 || n >= len(x.Digits) {
return // nothing to do
}
@@ -258,7 +281,7 @@ func (x *Decimal) roundDown(n int) {
// trim cuts off any trailing zeros from x's mantissa;
// they are meaningless for the value of x.
-func trim(x *Decimal) {
+func trim(x *digits) {
i := len(x.Digits)
for i > 0 && x.Digits[i-1] == 0 {
i--
@@ -272,7 +295,7 @@ func trim(x *Decimal) {
// A Converter converts a number into decimals according to the given rounding
// criteria.
type Converter interface {
- Convert(d *Decimal, r *RoundingContext)
+ Convert(d *Decimal, r RoundingContext)
}
const (
@@ -282,7 +305,7 @@ const (
// Convert converts the given number to the decimal representation using the
// supplied RoundingContext.
-func (d *Decimal) Convert(r *RoundingContext, number interface{}) {
+func (d *Decimal) Convert(r RoundingContext, number interface{}) {
switch f := number.(type) {
case Converter:
d.clear()
@@ -312,6 +335,8 @@ func (d *Decimal) Convert(r *RoundingContext, number interface{}) {
case uint64:
d.ConvertInt(r, unsigned, f)
+ default:
+ d.NaN = true
// TODO:
// case string: if produced by strconv, allows for easy arbitrary pos.
// case reflect.Value:
@@ -324,7 +349,7 @@ func (d *Decimal) Convert(r *RoundingContext, number interface{}) {
}
// ConvertInt converts an integer to decimals.
-func (d *Decimal) ConvertInt(r *RoundingContext, signed bool, x uint64) {
+func (d *Decimal) ConvertInt(r RoundingContext, signed bool, x uint64) {
if r.Increment > 0 {
// TODO: if uint64 is too large, fall back to float64
if signed {
@@ -344,12 +369,30 @@ func (d *Decimal) ConvertInt(r *RoundingContext, signed bool, x uint64) {
}
// ConvertFloat converts a floating point number to decimals.
-func (d *Decimal) ConvertFloat(r *RoundingContext, x float64, size int) {
+func (d *Decimal) ConvertFloat(r RoundingContext, x float64, size int) {
d.clear()
if math.IsNaN(x) {
d.NaN = true
return
}
+ // Simple case: decimal notation
+ if r.Increment > 0 {
+ scale := int(r.IncrementScale)
+ mult := 1.0
+ if scale > len(scales) {
+ mult = math.Pow(10, float64(scale))
+ } else {
+ mult = scales[scale]
+ }
+ // We multiply x instead of dividing inc as it gives less rounding
+ // issues.
+ x *= mult
+ x /= float64(r.Increment)
+ x = r.Mode.roundFloat(x)
+ x *= float64(r.Increment)
+ x /= mult
+ }
+
abs := x
if x < 0 {
d.Neg = true
@@ -359,63 +402,59 @@ func (d *Decimal) ConvertFloat(r *RoundingContext, x float64, size int) {
d.Inf = true
return
}
- // Simple case: decimal notation
- if r.Scale > 0 || r.Increment > 0 || r.Precision == 0 {
- if int(r.Scale) > len(scales) {
- x *= math.Pow(10, float64(r.Scale))
- } else {
- x *= scales[r.Scale]
- }
- if r.Increment > 0 {
- inc := float64(r.Increment)
- x /= float64(inc)
- x = r.Mode.roundFloat(x)
- x *= inc
- } else {
- x = r.Mode.roundFloat(x)
- }
- d.fillIntDigits(uint64(math.Abs(x)))
- d.Exp = int32(len(d.Digits)) - r.Scale
- return
- }
- // Nasty case (for non-decimal notation).
- // Asides from being inefficient, this result is also wrong as it will
- // apply ToNearestEven rounding regardless of the user setting.
- // TODO: expose functionality in strconv so we can avoid this hack.
+ // By default we get the exact decimal representation.
+ verb := byte('g')
+ prec := -1
+ // Determine rounding, if possible. As the strconv API does not return the
+ // rounding accuracy (exact/rounded up|down), we can only round using
+ // ToNearestEven.
// Something like this would work:
// AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)
- // TODO: This only supports the nearest even rounding mode.
-
- prec := int(r.Precision)
- if prec > 0 {
- prec--
- }
- b := strconv.AppendFloat(d.Digits, abs, 'e', prec, size)
+ //
+ // TODO: At this point strconv's rounding is imprecise to the point that it
+ // is not useable for this purpose.
+ // See https://github.com/golang/go/issues/21714
+ // if r.Mode == ToNearestEven {
+ // if n := r.RoundSignificantDigits(); n >= 0 {
+ // prec = n
+ // } else if n = r.RoundFractionDigits(); n >= 0 {
+ // prec = n
+ // verb = 'f'
+ // }
+ // }
+
+ b := strconv.AppendFloat(d.Digits[:0], abs, verb, prec, size)
i := 0
k := 0
- // No need to check i < len(b) as we always have an 'e'.
- for {
+ beforeDot := 1
+ for i < len(b) {
if c := b[i]; '0' <= c && c <= '9' {
b[k] = c - '0'
k++
- } else if c != '.' {
+ d.Exp += int32(beforeDot)
+ } else if c == '.' {
+ beforeDot = 0
+ d.Exp = int32(k)
+ } else {
break
}
i++
}
d.Digits = b[:k]
- i += len("e")
- pSign := i
- exp := 0
- for i++; i < len(b); i++ {
- exp *= 10
- exp += int(b[i] - '0')
- }
- if b[pSign] == '-' {
- exp = -exp
+ if i != len(b) {
+ i += len("e")
+ pSign := i
+ exp := 0
+ for i++; i < len(b); i++ {
+ exp *= 10
+ exp += int(b[i] - '0')
+ }
+ if b[pSign] == '-' {
+ exp = -exp
+ }
+ d.Exp = int32(exp) + 1
}
- d.Exp = int32(exp) + 1
}
func (d *Decimal) fillIntDigits(x uint64) {