github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/math/big/decimal.go (about)

     1  // Copyright 2015 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  // This file implements multi-precision decimal numbers.
     6  // The implementation is for float to decimal conversion only;
     7  // not general purpose use.
     8  // The only operations are precise conversion from binary to
     9  // decimal and rounding.
    10  //
    11  // The key observation and some code (shr) is borrowed from
    12  // strconv/decimal.go: conversion of binary fractional values can be done
    13  // precisely in multi-precision decimal because 2 divides 10 (required for
    14  // >> of mantissa); but conversion of decimal floating-point values cannot
    15  // be done precisely in binary representation.
    16  //
    17  // In contrast to strconv/decimal.go, only right shift is implemented in
    18  // decimal format - left shift can be done precisely in binary format.
    19  
    20  package big
    21  
    22  // A decimal represents an unsigned floating-point number in decimal representation.
    23  // The value of a non-zero decimal d is d.mant * 10**d.exp with 0.5 <= d.mant < 1,
    24  // with the most-significant mantissa digit at index 0. For the zero decimal, the
    25  // mantissa length and exponent are 0.
    26  // The zero value for decimal represents a ready-to-use 0.0.
    27  type decimal struct {
    28  	mant []byte // mantissa ASCII digits, big-endian
    29  	exp  int    // exponent
    30  }
    31  
    32  // at returns the i'th mantissa digit, starting with the most significant digit at 0.
    33  func (d *decimal) at(i int) byte {
    34  	if 0 <= i && i < len(d.mant) {
    35  		return d.mant[i]
    36  	}
    37  	return '0'
    38  }
    39  
    40  // Maximum shift amount that can be done in one pass without overflow.
    41  // A Word has _W bits and (1<<maxShift - 1)*10 + 9 must fit into Word.
    42  const maxShift = _W - 4
    43  
    44  // TODO(gri) Since we know the desired decimal precision when converting
    45  // a floating-point number, we may be able to limit the number of decimal
    46  // digits that need to be computed by init by providing an additional
    47  // precision argument and keeping track of when a number was truncated early
    48  // (equivalent of "sticky bit" in binary rounding).
    49  
    50  // TODO(gri) Along the same lines, enforce some limit to shift magnitudes
    51  // to avoid "infinitely" long running conversions (until we run out of space).
    52  
    53  // Init initializes x to the decimal representation of m << shift (for
    54  // shift >= 0), or m >> -shift (for shift < 0).
    55  func (x *decimal) init(m nat, shift int) {
    56  	// special case 0
    57  	if len(m) == 0 {
    58  		x.mant = x.mant[:0]
    59  		x.exp = 0
    60  		return
    61  	}
    62  
    63  	// Optimization: If we need to shift right, first remove any trailing
    64  	// zero bits from m to reduce shift amount that needs to be done in
    65  	// decimal format (since that is likely slower).
    66  	if shift < 0 {
    67  		ntz := m.trailingZeroBits()
    68  		s := uint(-shift)
    69  		if s >= ntz {
    70  			s = ntz // shift at most ntz bits
    71  		}
    72  		m = nat(nil).shr(m, s)
    73  		shift += int(s)
    74  	}
    75  
    76  	// Do any shift left in binary representation.
    77  	if shift > 0 {
    78  		m = nat(nil).shl(m, uint(shift))
    79  		shift = 0
    80  	}
    81  
    82  	// Convert mantissa into decimal representation.
    83  	s := m.utoa(10)
    84  	n := len(s)
    85  	x.exp = n
    86  	// Trim trailing zeros; instead the exponent is tracking
    87  	// the decimal point independent of the number of digits.
    88  	for n > 0 && s[n-1] == '0' {
    89  		n--
    90  	}
    91  	x.mant = append(x.mant[:0], s[:n]...)
    92  
    93  	// Do any (remaining) shift right in decimal representation.
    94  	if shift < 0 {
    95  		for shift < -maxShift {
    96  			shr(x, maxShift)
    97  			shift += maxShift
    98  		}
    99  		shr(x, uint(-shift))
   100  	}
   101  }
   102  
   103  // shr implements x >> s, for s <= maxShift.
   104  func shr(x *decimal, s uint) {
   105  	// Division by 1<<s using shift-and-subtract algorithm.
   106  
   107  	// pick up enough leading digits to cover first shift
   108  	r := 0 // read index
   109  	var n Word
   110  	for n>>s == 0 && r < len(x.mant) {
   111  		ch := Word(x.mant[r])
   112  		r++
   113  		n = n*10 + ch - '0'
   114  	}
   115  	if n == 0 {
   116  		// x == 0; shouldn't get here, but handle anyway
   117  		x.mant = x.mant[:0]
   118  		return
   119  	}
   120  	for n>>s == 0 {
   121  		r++
   122  		n *= 10
   123  	}
   124  	x.exp += 1 - r
   125  
   126  	// read a digit, write a digit
   127  	w := 0 // write index
   128  	for r < len(x.mant) {
   129  		ch := Word(x.mant[r])
   130  		r++
   131  		d := n >> s
   132  		n -= d << s
   133  		x.mant[w] = byte(d + '0')
   134  		w++
   135  		n = n*10 + ch - '0'
   136  	}
   137  
   138  	// write extra digits that still fit
   139  	for n > 0 && w < len(x.mant) {
   140  		d := n >> s
   141  		n -= d << s
   142  		x.mant[w] = byte(d + '0')
   143  		w++
   144  		n = n * 10
   145  	}
   146  	x.mant = x.mant[:w] // the number may be shorter (e.g. 1024 >> 10)
   147  
   148  	// append additional digits that didn't fit
   149  	for n > 0 {
   150  		d := n >> s
   151  		n -= d << s
   152  		x.mant = append(x.mant, byte(d+'0'))
   153  		n = n * 10
   154  	}
   155  
   156  	trim(x)
   157  }
   158  
   159  func (x *decimal) String() string {
   160  	if len(x.mant) == 0 {
   161  		return "0"
   162  	}
   163  
   164  	var buf []byte
   165  	switch {
   166  	case x.exp <= 0:
   167  		// 0.00ddd
   168  		buf = append(buf, "0."...)
   169  		buf = appendZeros(buf, -x.exp)
   170  		buf = append(buf, x.mant...)
   171  
   172  	case /* 0 < */ x.exp < len(x.mant):
   173  		// dd.ddd
   174  		buf = append(buf, x.mant[:x.exp]...)
   175  		buf = append(buf, '.')
   176  		buf = append(buf, x.mant[x.exp:]...)
   177  
   178  	default: // len(x.mant) <= x.exp
   179  		// ddd00
   180  		buf = append(buf, x.mant...)
   181  		buf = appendZeros(buf, x.exp-len(x.mant))
   182  	}
   183  
   184  	return string(buf)
   185  }
   186  
   187  // appendZeros appends n 0 digits to buf and returns buf.
   188  func appendZeros(buf []byte, n int) []byte {
   189  	for ; n > 0; n-- {
   190  		buf = append(buf, '0')
   191  	}
   192  	return buf
   193  }
   194  
   195  // shouldRoundUp reports if x should be rounded up
   196  // if shortened to n digits. n must be a valid index
   197  // for x.mant.
   198  func shouldRoundUp(x *decimal, n int) bool {
   199  	if x.mant[n] == '5' && n+1 == len(x.mant) {
   200  		// exactly halfway - round to even
   201  		return n > 0 && (x.mant[n-1]-'0')&1 != 0
   202  	}
   203  	// not halfway - digit tells all (x.mant has no trailing zeros)
   204  	return x.mant[n] >= '5'
   205  }
   206  
   207  // round sets x to (at most) n mantissa digits by rounding it
   208  // to the nearest even value with n (or fever) mantissa digits.
   209  // If n < 0, x remains unchanged.
   210  func (x *decimal) round(n int) {
   211  	if n < 0 || n >= len(x.mant) {
   212  		return // nothing to do
   213  	}
   214  
   215  	if shouldRoundUp(x, n) {
   216  		x.roundUp(n)
   217  	} else {
   218  		x.roundDown(n)
   219  	}
   220  }
   221  
   222  func (x *decimal) roundUp(n int) {
   223  	if n < 0 || n >= len(x.mant) {
   224  		return // nothing to do
   225  	}
   226  	// 0 <= n < len(x.mant)
   227  
   228  	// find first digit < '9'
   229  	for n > 0 && x.mant[n-1] >= '9' {
   230  		n--
   231  	}
   232  
   233  	if n == 0 {
   234  		// all digits are '9's => round up to '1' and update exponent
   235  		x.mant[0] = '1' // ok since len(x.mant) > n
   236  		x.mant = x.mant[:1]
   237  		x.exp++
   238  		return
   239  	}
   240  
   241  	// n > 0 && x.mant[n-1] < '9'
   242  	x.mant[n-1]++
   243  	x.mant = x.mant[:n]
   244  	// x already trimmed
   245  }
   246  
   247  func (x *decimal) roundDown(n int) {
   248  	if n < 0 || n >= len(x.mant) {
   249  		return // nothing to do
   250  	}
   251  	x.mant = x.mant[:n]
   252  	trim(x)
   253  }
   254  
   255  // trim cuts off any trailing zeros from x's mantissa;
   256  // they are meaningless for the value of x.
   257  func trim(x *decimal) {
   258  	i := len(x.mant)
   259  	for i > 0 && x.mant[i-1] == '0' {
   260  		i--
   261  	}
   262  	x.mant = x.mant[:i]
   263  	if i == 0 {
   264  		x.exp = 0
   265  	}
   266  }