1
0
mirror of https://github.com/golang/go synced 2024-11-21 23:54:40 -07:00

strconv: handle very large inputs

Fixes #2642.

R=remyoudompheng, r, r
CC=golang-dev
https://golang.org/cl/5639052
This commit is contained in:
Russ Cox 2012-02-07 23:37:15 -05:00
parent 00134fe8ef
commit 3ee208533e
3 changed files with 50 additions and 16 deletions

View File

@ -52,10 +52,10 @@ func special(s string) (f float64, ok bool) {
return
}
// TODO(rsc): Better truncation handling.
func (b *decimal) set(s string) (ok bool) {
i := 0
b.neg = false
b.trunc = false
// optional sign
if i >= len(s) {
@ -88,8 +88,12 @@ func (b *decimal) set(s string) (ok bool) {
b.dp--
continue
}
b.d[b.nd] = s[i]
b.nd++
if b.nd < len(b.d) {
b.d[b.nd] = s[i]
b.nd++
} else if s[i] != '0' {
b.trunc = true
}
continue
}
break

View File

@ -9,6 +9,7 @@ import (
"math/rand"
"reflect"
. "strconv"
"strings"
"testing"
"time"
)
@ -117,6 +118,20 @@ var atoftests = []atofTest{
// A very large number (initially wrongly parsed by the fast algorithm).
{"4.630813248087435e+307", "4.630813248087435e+307", nil},
// A different kind of very large number.
{"22.222222222222222", "22.22222222222222", nil},
{"2." + strings.Repeat("2", 4000) + "e+1", "22.22222222222222", nil},
// Exactly halfway between 1 and math.Nextafter(1, 2).
// Round to even (down).
{"1.00000000000000011102230246251565404236316680908203125", "1", nil},
// Slightly lower; still round down.
{"1.00000000000000011102230246251565404236316680908203124", "1", nil},
// Slightly higher; round up.
{"1.00000000000000011102230246251565404236316680908203126", "1.0000000000000002", nil},
// Slightly higher, but you have to read all the way to the end.
{"1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1", "1.0000000000000002", nil},
}
type atofSimpleTest struct {

View File

@ -12,12 +12,11 @@
package strconv
type decimal struct {
// TODO(rsc): Can make d[] a bit smaller and add
// truncated bool;
d [800]byte // digits
nd int // number of digits used
dp int // decimal point
neg bool
d [800]byte // digits
nd int // number of digits used
dp int // decimal point
neg bool
trunc bool // discarded nonzero digits beyond d[:nd]
}
func (a *decimal) String() string {
@ -145,8 +144,12 @@ func rightShift(a *decimal, k uint) {
for n > 0 {
dig := n >> k
n -= dig << k
a.d[w] = byte(dig + '0')
w++
if w < len(a.d) {
a.d[w] = byte(dig + '0')
w++
} else if dig > 0 {
a.trunc = true
}
n = n * 10
}
@ -242,7 +245,11 @@ func leftShift(a *decimal, k uint) {
quo := n / 10
rem := n - 10*quo
w--
a.d[w] = byte(rem + '0')
if w < len(a.d) {
a.d[w] = byte(rem + '0')
} else if rem != 0 {
a.trunc = true
}
n = quo
}
@ -251,11 +258,18 @@ func leftShift(a *decimal, k uint) {
quo := n / 10
rem := n - 10*quo
w--
a.d[w] = byte(rem + '0')
if w < len(a.d) {
a.d[w] = byte(rem + '0')
} else if rem != 0 {
a.trunc = true
}
n = quo
}
a.nd += delta
if a.nd >= len(a.d) {
a.nd = len(a.d)
}
a.dp += delta
trim(a)
}
@ -286,6 +300,10 @@ func shouldRoundUp(a *decimal, nd int) bool {
return false
}
if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even
// if we truncated, a little higher than what's recorded - always round up
if a.trunc {
return true
}
return nd > 0 && (a.d[nd-1]-'0')%2 != 0
}
// not halfway - digit tells all
@ -293,7 +311,6 @@ func shouldRoundUp(a *decimal, nd int) bool {
}
// Round a to nd digits (or fewer).
// Returns receiver for convenience.
// If nd is zero, it means we're rounding
// just to the left of the digits, as in
// 0.09 -> 0.1.
@ -309,7 +326,6 @@ func (a *decimal) Round(nd int) {
}
// Round a down to nd digits (or fewer).
// Returns receiver for convenience.
func (a *decimal) RoundDown(nd int) {
if nd < 0 || nd >= a.nd {
return
@ -319,7 +335,6 @@ func (a *decimal) RoundDown(nd int) {
}
// Round a up to nd digits (or fewer).
// Returns receiver for convenience.
func (a *decimal) RoundUp(nd int) {
if nd < 0 || nd >= a.nd {
return