taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

decimal.go (12105B)


      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 }