summaryrefslogtreecommitdiff
path: root/src/pkg/strconv/decimal.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/strconv/decimal.go')
-rw-r--r--src/pkg/strconv/decimal.go392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go
new file mode 100644
index 000000000..38d9c47fb
--- /dev/null
+++ b/src/pkg/strconv/decimal.go
@@ -0,0 +1,392 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Multiprecision decimal numbers.
+// For floating-point formatting only; not general purpose.
+// Only operations are assign and (binary) left/right shift.
+// Can do binary floating point in multiprecision decimal precisely
+// because 2 divides 10; cannot do decimal floating point
+// in multiprecision binary precisely.
+
+package strconv
+
+import "bytes"
+
+type decimal struct {
+ // TODO(rsc): Can make d[] a bit smaller and add
+ // truncated bool;
+ d [2000] byte; // digits
+ nd int; // number of digits used
+ dp int; // decimal point
+};
+func (a *decimal) String() string;
+func (a *decimal) Assign(v uint64);
+func (a *decimal) Shift(k int) *decimal;
+func (a *decimal) Round(nd int) *decimal;
+func (a *decimal) RoundUp(nd int) *decimal;
+func (a *decimal) RoundDown(nd int) *decimal;
+func (a *decimal) RoundedInteger() uint64;
+
+
+func digitZero(dst []byte) int;
+
+func (a *decimal) String() string {
+ n := 10 + a.nd;
+ if a.dp > 0 {
+ n += a.dp;
+ }
+ if a.dp < 0 {
+ n += -a.dp;
+ }
+
+ buf := make([]byte, n);
+ w := 0;
+ switch {
+ case a.nd == 0:
+ return "0";
+
+ case a.dp <= 0:
+ // zeros fill space between decimal point and digits
+ buf[w] = '0';
+ w++;
+ buf[w] = '.';
+ w++;
+ w += digitZero(buf[w:w+-a.dp]);
+ w += bytes.Copy(buf[w:w+a.nd], a.d[0:a.nd]);
+
+ case a.dp < a.nd:
+ // decimal point in middle of digits
+ w += bytes.Copy(buf[w:w+a.dp], a.d[0:a.dp]);
+ buf[w] = '.';
+ w++;
+ w += bytes.Copy(buf[w:w+a.nd-a.dp], a.d[a.dp:a.nd]);
+
+ default:
+ // zeros fill space between digits and decimal point
+ w += bytes.Copy(buf[w:w+a.nd], a.d[0:a.nd]);
+ w += digitZero(buf[w:w+a.dp-a.nd]);
+ }
+ return string(buf[0:w]);
+}
+
+func copy(dst []byte, src []byte) int {
+ for i := 0; i < len(dst); i++ {
+ dst[i] = src[i];
+ }
+ return len(dst);
+}
+
+func digitZero(dst []byte) int {
+ for i := 0; i < len(dst); i++ {
+ dst[i] = '0';
+ }
+ return len(dst);
+}
+
+// trim trailing zeros from number.
+// (They are meaningless; the decimal point is tracked
+// independent of the number of digits.)
+func trim(a *decimal) {
+ for a.nd > 0 && a.d[a.nd-1] == '0' {
+ a.nd--;
+ }
+ if a.nd == 0 {
+ a.dp = 0;
+ }
+}
+
+// Assign v to a.
+func (a *decimal) Assign(v uint64) {
+ var buf [50]byte;
+
+ // Write reversed decimal in buf.
+ n := 0;
+ for v > 0 {
+ v1 := v/10;
+ v -= 10*v1;
+ buf[n] = byte(v + '0');
+ n++;
+ v = v1;
+ }
+
+ // Reverse again to produce forward decimal in a.d.
+ a.nd = 0;
+ for n--; n>=0; n-- {
+ a.d[a.nd] = buf[n];
+ a.nd++;
+ }
+ a.dp = a.nd;
+ trim(a);
+}
+
+func newDecimal(i uint64) *decimal {
+ a := new(decimal);
+ a.Assign(i);
+ return a;
+}
+
+// Maximum shift that we can do in one pass without overflow.
+// Signed int has 31 bits, and we have to be able to accomodate 9<<k.
+const maxShift = 27
+
+// Binary shift right (* 2) by k bits. k <= maxShift to avoid overflow.
+func rightShift(a *decimal, k uint) {
+ r := 0; // read pointer
+ w := 0; // write pointer
+
+ // Pick up enough leading digits to cover first shift.
+ n := 0;
+ for ; n>>k == 0; r++ {
+ if r >= a.nd {
+ if n == 0 {
+ // a == 0; shouldn't get here, but handle anyway.
+ a.nd = 0;
+ return;
+ }
+ for n>>k == 0 {
+ n = n*10;
+ r++;
+ }
+ break;
+ }
+ c := int(a.d[r]);
+ n = n*10 + c-'0';
+ }
+ a.dp -= r-1;
+
+ // Pick up a digit, put down a digit.
+ for ; r < a.nd; r++ {
+ c := int(a.d[r]);
+ dig := n>>k;
+ n -= dig<<k;
+ a.d[w] = byte(dig+'0');
+ w++;
+ n = n*10 + c-'0';
+ }
+
+ // Put down extra digits.
+ for n > 0 {
+ dig := n>>k;
+ n -= dig<<k;
+ a.d[w] = byte(dig+'0');
+ w++;
+ n = n*10;
+ }
+
+ a.nd = w;
+ trim(a);
+}
+
+// Cheat sheet for left shift: table indexed by shift count giving
+// number of new digits that will be introduced by that shift.
+//
+// For example, leftcheats[4] = {2, "625"}. That means that
+// if we are shifting by 4 (multiplying by 16), it will add 2 digits
+// when the string prefix is "625" through "999", and one fewer digit
+// if the string prefix is "000" through "624".
+//
+// Credit for this trick goes to Ken.
+
+type leftCheat struct {
+ delta int; // number of new digits
+ cutoff string; // minus one digit if original < a.
+}
+
+var leftcheats = []leftCheat {
+ // Leading digits of 1/2^i = 5^i.
+ // 5^23 is not an exact 64-bit floating point number,
+ // so have to use bc for the math.
+ /*
+ seq 27 | sed 's/^/5^/' | bc |
+ awk 'BEGIN{ print "\tleftCheat{ 0, \"\" }," }
+ {
+ log2 = log(2)/log(10)
+ printf("\tleftCheat{ %d, \"%s\" },\t// * %d\n",
+ int(log2*NR+1), $0, 2**NR)
+ }'
+ */
+ leftCheat{ 0, "" },
+ leftCheat{ 1, "5" }, // * 2
+ leftCheat{ 1, "25" }, // * 4
+ leftCheat{ 1, "125" }, // * 8
+ leftCheat{ 2, "625" }, // * 16
+ leftCheat{ 2, "3125" }, // * 32
+ leftCheat{ 2, "15625" }, // * 64
+ leftCheat{ 3, "78125" }, // * 128
+ leftCheat{ 3, "390625" }, // * 256
+ leftCheat{ 3, "1953125" }, // * 512
+ leftCheat{ 4, "9765625" }, // * 1024
+ leftCheat{ 4, "48828125" }, // * 2048
+ leftCheat{ 4, "244140625" }, // * 4096
+ leftCheat{ 4, "1220703125" }, // * 8192
+ leftCheat{ 5, "6103515625" }, // * 16384
+ leftCheat{ 5, "30517578125" }, // * 32768
+ leftCheat{ 5, "152587890625" }, // * 65536
+ leftCheat{ 6, "762939453125" }, // * 131072
+ leftCheat{ 6, "3814697265625" }, // * 262144
+ leftCheat{ 6, "19073486328125" }, // * 524288
+ leftCheat{ 7, "95367431640625" }, // * 1048576
+ leftCheat{ 7, "476837158203125" }, // * 2097152
+ leftCheat{ 7, "2384185791015625" }, // * 4194304
+ leftCheat{ 7, "11920928955078125" }, // * 8388608
+ leftCheat{ 8, "59604644775390625" }, // * 16777216
+ leftCheat{ 8, "298023223876953125" }, // * 33554432
+ leftCheat{ 8, "1490116119384765625" }, // * 67108864
+ leftCheat{ 9, "7450580596923828125" }, // * 134217728
+}
+
+// Is the leading prefix of b lexicographically less than s?
+func prefixIsLessThan(b []byte, s string) bool {
+ for i := 0; i < len(s); i++ {
+ if i >= len(b) {
+ return true;
+ }
+ if b[i] != s[i] {
+ return b[i] < s[i];
+ }
+ }
+ return false;
+}
+
+// Binary shift left (/ 2) by k bits. k <= maxShift to avoid overflow.
+func leftShift(a *decimal, k uint) {
+ delta := leftcheats[k].delta;
+ if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) {
+ delta--;
+ }
+
+ r := a.nd; // read index
+ w := a.nd + delta; // write index
+ n := 0;
+
+ // Pick up a digit, put down a digit.
+ for r--; r >= 0; r-- {
+ n += (int(a.d[r])-'0') << k;
+ quo := n/10;
+ rem := n - 10*quo;
+ w--;
+ a.d[w] = byte(rem+'0');
+ n = quo;
+ }
+
+ // Put down extra digits.
+ for n > 0 {
+ quo := n/10;
+ rem := n - 10*quo;
+ w--;
+ a.d[w] = byte(rem+'0');
+ n = quo;
+ }
+
+ if w != 0 {
+ // TODO: Remove - has no business panicking.
+ panicln("strconv: bad leftShift", w);
+ }
+ a.nd += delta;
+ a.dp += delta;
+ trim(a);
+}
+
+// Binary shift left (k > 0) or right (k < 0).
+// Returns receiver for convenience.
+func (a *decimal) Shift(k int) *decimal {
+ switch {
+ case a.nd == 0:
+ // nothing to do: a == 0
+ case k > 0:
+ for k > maxShift {
+ leftShift(a, maxShift);
+ k -= maxShift;
+ }
+ leftShift(a, uint(k));
+ case k < 0:
+ for k < -maxShift {
+ rightShift(a, maxShift);
+ k += maxShift;
+ }
+ rightShift(a, uint(-k));
+ }
+ return a;
+}
+
+// If we chop a at nd digits, should we round up?
+func shouldRoundUp(a *decimal, nd int) bool {
+ if nd <= 0 || nd >= a.nd {
+ return false;
+ }
+ if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even
+ return (a.d[nd-1] - '0') % 2 != 0;
+ }
+ // not halfway - digit tells all
+ return a.d[nd] >= '5';
+}
+
+// Round a to nd digits (or fewer).
+// Returns receiver for convenience.
+func (a *decimal) Round(nd int) *decimal {
+ if nd <= 0 || nd >= a.nd {
+ return a;
+ }
+ if(shouldRoundUp(a, nd)) {
+ return a.RoundUp(nd);
+ }
+ return a.RoundDown(nd);
+}
+
+// Round a down to nd digits (or fewer).
+// Returns receiver for convenience.
+func (a *decimal) RoundDown(nd int) *decimal {
+ if nd <= 0 || nd >= a.nd {
+ return a;
+ }
+ a.nd = nd;
+ trim(a);
+ return a;
+}
+
+// Round a up to nd digits (or fewer).
+// Returns receiver for convenience.
+func (a *decimal) RoundUp(nd int) *decimal {
+ if nd <= 0 || nd >= a.nd {
+ return a;
+ }
+
+ // round up
+ for i := nd-1; i >= 0; i-- {
+ c := a.d[i];
+ if c < '9' { // can stop after this digit
+ a.d[i]++;
+ a.nd = i+1;
+ return a;
+ }
+ }
+
+ // Number is all 9s.
+ // Change to single 1 with adjusted decimal point.
+ a.d[0] = '1';
+ a.nd = 1;
+ a.dp++;
+ return a;
+}
+
+// Extract integer part, rounded appropriately.
+// No guarantees about overflow.
+func (a *decimal) RoundedInteger() uint64 {
+ if a.dp > 20 {
+ return 0xFFFFFFFFFFFFFFFF;
+ }
+ var i int;
+ n := uint64(0);
+ for i = 0; i < a.dp && i < a.nd; i++ {
+ n = n*10 + uint64(a.d[i] - '0');
+ }
+ for ; i < a.dp; i++ {
+ n *= 10;
+ }
+ if shouldRoundUp(a, a.dp) {
+ n++;
+ }
+ return n;
+}
+