Merge pull request #10 from thiagoarruda/feature/parse-scientific-notation

Add scientific notation parsing
This commit is contained in:
Vadim Graboys 2015-07-31 12:51:16 -04:00
commit 7a95614e29
2 changed files with 66 additions and 8 deletions

View file

@ -81,21 +81,32 @@ func New(value int64, exp int32) Decimal {
// //
func NewFromString(value string) (Decimal, error) { func NewFromString(value string) (Decimal, error) {
var intString string var intString string
var exp int32 var exp int64
// Check if number is using scientific notation
eIndex := strings.IndexAny(value, "Ee")
if eIndex != -1 {
expInt, err := strconv.ParseInt(value[eIndex+1:], 10, 32)
if err != nil {
rerr := err.(*strconv.NumError)
if rerr.Err == strconv.ErrRange {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", value)
}
return Decimal{}, fmt.Errorf("can't convert %s to decimal: exponent is not numeric", value)
}
value = value[:eIndex]
exp = expInt
}
parts := strings.Split(value, ".") parts := strings.Split(value, ".")
if len(parts) == 1 { if len(parts) == 1 {
// There is no decimal point, we can just parse the original string as // There is no decimal point, we can just parse the original string as
// an int // an int
intString = value intString = value
exp = 0
} else if len(parts) == 2 { } else if len(parts) == 2 {
intString = parts[0] + parts[1] intString = parts[0] + parts[1]
expInt := -len(parts[1]) expInt := -len(parts[1])
if expInt < math.MinInt32 { exp += int64(expInt)
// NOTE(vadim): I doubt a string could realistically be this long
return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", value)
}
exp = int32(expInt)
} else { } else {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value) return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value)
} }
@ -106,9 +117,14 @@ func NewFromString(value string) (Decimal, error) {
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
} }
if exp < math.MinInt32 || exp > math.MaxInt32 {
// NOTE(vadim): I doubt a string could realistically be this long
return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", value)
}
return Decimal{ return Decimal{
value: dValue, value: dValue,
exp: exp, exp: int32(exp),
}, nil }, nil
} }

View file

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"math" "math"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -32,6 +33,21 @@ var testTable = map[float64]string{
.1000000000000008: "0.1000000000000008", .1000000000000008: "0.1000000000000008",
} }
var testTableScientificNotation = map[string]string{
"1e9": "1000000000",
"2.41E-3": "0.00241",
"24.2E-4": "0.00242",
"243E-5": "0.00243",
"1e-5": "0.00001",
"245E3": "245000",
"1.2345E-1": "0.12345",
"0e5": "0",
"0e-5": "0",
"123.456e0": "123.456",
"123.456e2": "12345.6",
"123.456e10": "1234560000000",
}
func init() { func init() {
// add negatives // add negatives
for f, s := range testTable { for f, s := range testTable {
@ -39,6 +55,11 @@ func init() {
testTable[-f] = "-" + s testTable[-f] = "-" + s
} }
} }
for e, s := range testTableScientificNotation {
if string(e[0]) != "-" && s != "0" {
testTableScientificNotation["-"+e] = "-" + s
}
}
} }
func TestNewFromFloat(t *testing.T) { func TestNewFromFloat(t *testing.T) {
@ -76,6 +97,17 @@ func TestNewFromString(t *testing.T) {
d.value.String(), d.exp) d.value.String(), d.exp)
} }
} }
for e, s := range testTableScientificNotation {
d, err := NewFromString(e)
if err != nil {
t.Errorf("error while parsing %s", e)
} else if d.String() != s {
t.Errorf("expected %s, got %s (%s, %d)",
s, d.String(),
d.value.String(), d.exp)
}
}
} }
func TestNewFromStringErrs(t *testing.T) { func TestNewFromStringErrs(t *testing.T) {
@ -95,6 +127,16 @@ func TestNewFromStringErrs(t *testing.T) {
".5.2", ".5.2",
"8..2", "8..2",
"8.1.", "8.1.",
"1e",
"1-e",
"1e9e",
"1ee9",
"1ee",
"1e1.2",
"123.456e1.3",
"1e-1.2",
"123.456e-1.3",
"123.456e" + strconv.FormatInt(math.MinInt64, 10),
} }
for _, s := range tests { for _, s := range tests {