Commit 0a24b8c7 authored by Robert Griesemer's avatar Robert Griesemer

math/big: sketched out complete set of Float/string conversion functions

Also:
- use io.ByteScanner rather than io.RuneScanner internally
- minor simplifications in Float.Add/Sub

Change-Id: Iae0e99384128dba9eccf68592c4fd389e2bd3b4f
Reviewed-on: https://go-review.googlesource.com/3380Reviewed-by: default avatarRob Pike <r@golang.org>
parent 813e97b7
...@@ -179,7 +179,7 @@ func (x *Float) validate() { ...@@ -179,7 +179,7 @@ func (x *Float) validate() {
const msb = 1 << (_W - 1) const msb = 1 << (_W - 1)
m := len(x.mant) m := len(x.mant)
if x.mant[m-1]&msb == 0 { if x.mant[m-1]&msb == 0 {
panic(fmt.Sprintf("msb not set in last word %#x of %s", x.mant[m-1], x.PString())) panic(fmt.Sprintf("msb not set in last word %#x of %s", x.mant[m-1], x.pstring()))
} }
if x.prec <= 0 { if x.prec <= 0 {
panic(fmt.Sprintf("invalid precision %d", x.prec)) panic(fmt.Sprintf("invalid precision %d", x.prec))
...@@ -566,10 +566,6 @@ func (z *Float) Neg(x *Float) *Float { ...@@ -566,10 +566,6 @@ func (z *Float) Neg(x *Float) *Float {
// z = x + y, ignoring signs of x and y. // z = x + y, ignoring signs of x and y.
// x and y must not be 0. // x and y must not be 0.
func (z *Float) uadd(x, y *Float) { func (z *Float) uadd(x, y *Float) {
if debugFloat && (len(x.mant) == 0 || len(y.mant) == 0) {
panic("uadd called with 0 argument")
}
// Note: This implementation requires 2 shifts most of the // Note: This implementation requires 2 shifts most of the
// time. It is also inefficient if exponents or precisions // time. It is also inefficient if exponents or precisions
// differ by wide margins. The following article describes // differ by wide margins. The following article describes
...@@ -580,60 +576,61 @@ func (z *Float) uadd(x, y *Float) { ...@@ -580,60 +576,61 @@ func (z *Float) uadd(x, y *Float) {
// Point Addition With Exact Rounding (as in the MPFR Library)" // Point Addition With Exact Rounding (as in the MPFR Library)"
// http://www.vinc17.net/research/papers/rnc6.pdf // http://www.vinc17.net/research/papers/rnc6.pdf
if debugFloat && (len(x.mant) == 0 || len(y.mant) == 0) {
panic("uadd called with 0 argument")
}
// compute exponents ex, ey for mantissa with "binary point"
// on the right (mantissa.0) - use int64 to avoid overflow
ex := int64(x.exp) - int64(len(x.mant))*_W ex := int64(x.exp) - int64(len(x.mant))*_W
ey := int64(y.exp) - int64(len(y.mant))*_W ey := int64(y.exp) - int64(len(y.mant))*_W
var e int64 // TODO(gri) having a combined add-and-shift primitive
// could make this code significantly faster
switch { switch {
case ex < ey: case ex < ey:
t := z.mant.shl(y.mant, uint(ey-ex)) t := z.mant.shl(y.mant, uint(ey-ex))
z.mant = t.add(x.mant, t) z.mant = t.add(x.mant, t)
e = ex
default: default:
// ex == ey // ex == ey, no shift needed
z.mant = z.mant.add(x.mant, y.mant) z.mant = z.mant.add(x.mant, y.mant)
e = ex
case ex > ey: case ex > ey:
t := z.mant.shl(x.mant, uint(ex-ey)) t := z.mant.shl(x.mant, uint(ex-ey))
z.mant = t.add(t, y.mant) z.mant = t.add(t, y.mant)
e = ey ex = ey
} }
// len(z.mant) > 0 // len(z.mant) > 0
z.setExp(e + int64(len(z.mant))*_W - int64(fnorm(z.mant))) z.setExp(ex + int64(len(z.mant))*_W - int64(fnorm(z.mant)))
z.round(0) z.round(0)
} }
// z = x - y for x >= y, ignoring signs of x and y. // z = x - y for x >= y, ignoring signs of x and y.
// x and y must not be zero. // x and y must not be zero.
func (z *Float) usub(x, y *Float) { func (z *Float) usub(x, y *Float) {
// This code is symmetric to uadd.
// We have not factored the common code out because
// eventually uadd (and usub) should be optimized
// by special-casing, and the code will diverge.
if debugFloat && (len(x.mant) == 0 || len(y.mant) == 0) { if debugFloat && (len(x.mant) == 0 || len(y.mant) == 0) {
panic("usub called with 0 argument") panic("usub called with 0 argument")
} }
if x.exp < y.exp {
panic("underflow")
}
// This code is symmetric to uadd.
ex := int64(x.exp) - int64(len(x.mant))*_W ex := int64(x.exp) - int64(len(x.mant))*_W
ey := int64(y.exp) - int64(len(y.mant))*_W ey := int64(y.exp) - int64(len(y.mant))*_W
var e int64
switch { switch {
case ex < ey: case ex < ey:
t := z.mant.shl(y.mant, uint(ey-ex)) t := z.mant.shl(y.mant, uint(ey-ex))
z.mant = t.sub(x.mant, t) z.mant = t.sub(x.mant, t)
e = ex
default: default:
// ex == ey // ex == ey, no shift needed
z.mant = z.mant.sub(x.mant, y.mant) z.mant = z.mant.sub(x.mant, y.mant)
e = ex
case ex > ey: case ex > ey:
t := z.mant.shl(x.mant, uint(ex-ey)) t := z.mant.shl(x.mant, uint(ex-ey))
z.mant = t.sub(t, y.mant) z.mant = t.sub(t, y.mant)
e = ey ex = ey
} }
// operands may have cancelled each other out // operands may have cancelled each other out
...@@ -644,7 +641,7 @@ func (z *Float) usub(x, y *Float) { ...@@ -644,7 +641,7 @@ func (z *Float) usub(x, y *Float) {
} }
// len(z.mant) > 0 // len(z.mant) > 0
z.setExp(e + int64(len(z.mant))*_W - int64(fnorm(z.mant))) z.setExp(ex + int64(len(z.mant))*_W - int64(fnorm(z.mant)))
z.round(0) z.round(0)
} }
...@@ -962,13 +959,9 @@ func (x *Float) Sign() int { ...@@ -962,13 +959,9 @@ func (x *Float) Sign() int {
return 1 return 1
} }
func (x *Float) String() string { // pstring returns x as a string in the format ["-"] "0." mantissa "p" exponent
return x.PString() // TODO(gri) fix this
}
// PString returns x as a string in the format ["-"] "0." mantissa "p" exponent
// with a hexadecimal mantissa and a decimal exponent, or ["-"] "0" if x is zero. // with a hexadecimal mantissa and a decimal exponent, or ["-"] "0" if x is zero.
func (x *Float) PString() string { func (x *Float) pstring() string {
// TODO(gri) handle Inf // TODO(gri) handle Inf
var buf bytes.Buffer var buf bytes.Buffer
if x.neg { if x.neg {
...@@ -1029,7 +1022,7 @@ func (z *Float) SetString(s string) (*Float, bool) { ...@@ -1029,7 +1022,7 @@ func (z *Float) SetString(s string) (*Float, bool) {
// is 1.2 * 2**3. If the operation failed, the value of z is undefined but // is 1.2 * 2**3. If the operation failed, the value of z is undefined but
// the returned value is nil. // the returned value is nil.
// //
func (z *Float) scan(r io.RuneScanner) (f *Float, err error) { func (z *Float) scan(r io.ByteScanner) (f *Float, err error) {
// sign // sign
z.neg, err = scanSign(r) z.neg, err = scanSign(r)
if err != nil { if err != nil {
...@@ -1091,3 +1084,106 @@ func (z *Float) scan(r io.RuneScanner) (f *Float, err error) { ...@@ -1091,3 +1084,106 @@ func (z *Float) scan(r io.RuneScanner) (f *Float, err error) {
return z, nil return z, nil
} }
// Scan scans the number corresponding to the longest possible prefix
// of r representing a floating-point number with a mantissa in the
// given conversion base (the exponent is always a decimal number).
// It returns the corresponding Float f, the actual base b, and an
// error err, if any. The number must be of the form:
//
// number = [ prefix ] [ sign ] mantissa [ exponent ] .
// mantissa = digits | digits "." [ digits ] | "." digits .
// prefix = prefix = "0" ( "x" | "X" | "b" | "B" ) .
// sign = "+" | "-" .
// exponent = ( "E" | "e" | "p" ) [ sign ] digits .
// digits = digit { digit } .
// digit = digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
//
// The base argument must be 0 or a value between 2 and MaxBase, inclusive.
//
// For base 0, the number prefix determines the actual base: A prefix of
// ``0x'' or ``0X'' selects base 16, and a ``0b'' or ``0B'' prefix selects
// base 2; otherwise, the actual base is 10 and no prefix is permitted.
// Note that the octal prefix ``0'' is not supported.
//
// A "p" exponent indicates power of 2 for the exponent; for instance "1.2p3"
// with base 0 or 10 corresponds to the value 1.2 * 2**3.
//
// BUG(gri) Currently, Scan only accepts base 10.
func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
if base != 10 {
err = fmt.Errorf("base %d not supported yet", base)
return
}
b = 10
f, err = z.scan(r)
return
}
// Parse is like z.Scan(r, base), but instead of reading from an
// io.ByteScanner, it parses the string s. An error is returned if the
// string contains invalid or trailing characters not belonging to the
// number.
//
// TODO(gri) define possible errors more precisely
func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
r := strings.NewReader(s)
if f, b, err = z.Scan(r, base); err != nil {
return
}
// entire string must have been consumed
var ch byte
if ch, err = r.ReadByte(); err != io.EOF {
if err == nil {
err = fmt.Errorf("expected end of string, found %q", ch)
}
}
return
}
// ScanFloat is like f.Scan(r, base) with f set to the given precision
// and rounding mode.
func ScanFloat(r io.ByteScanner, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
return NewFloat(0, prec, mode).Scan(r, base)
}
// ParseFloat is like f.Parse(s, base) with f set to the given precision
// and rounding mode.
func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
return NewFloat(0, prec, mode).Parse(s, base)
}
// Format converts the floating-point number x to a string according
// to the given format and precision prec.
//
// The format is one of
// 'e' (-d.dddde±dd, decimal exponent),
// 'E' (-d.ddddE±dd, decimal exponent),
// 'f' (-ddddd.dddd, no exponent),
// 'g' ('e' for large exponents, 'f' otherwise),
// 'G' ('E' for large exponents, 'f' otherwise),
// 'b' (-ddddddp±dd, binary exponent), or
// 'p' (-0.ddddp±dd, hexadecimal mantissa, binary exponent).
//
// The precision prec controls the number of digits (excluding the exponent)
// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f'
// it is the number of digits after the decimal point. For 'g' and 'G' it is
// the total number of digits. A negative precision selects the smallest
// number of digits necessary such that ParseFloat will return f exactly.
// The prec value is ignored for the 'b' or 'p' format.
//
// BUG(gri) Currently, Format only accepts the 'p' format.
func (x *Float) Format(format byte, prec int) string {
if format != 'p' {
return fmt.Sprintf(`%c`, format)
}
return x.pstring()
}
// BUG(gri): Currently, String uses the 'p' (rather than 'g') format.
func (x *Float) String() string {
return x.Format('p', 0)
}
...@@ -208,7 +208,7 @@ func TestFloatSetUint64(t *testing.T) { ...@@ -208,7 +208,7 @@ func TestFloatSetUint64(t *testing.T) {
for _, want := range tests { for _, want := range tests {
f := new(Float).SetUint64(want) f := new(Float).SetUint64(want)
if got := f.Uint64(); got != want { if got := f.Uint64(); got != want {
t.Errorf("got %d (%s); want %d", got, f.PString(), want) t.Errorf("got %d (%s); want %d", got, f.pstring(), want)
} }
} }
} }
...@@ -231,7 +231,7 @@ func TestFloatSetInt64(t *testing.T) { ...@@ -231,7 +231,7 @@ func TestFloatSetInt64(t *testing.T) {
} }
f := new(Float).SetInt64(want) f := new(Float).SetInt64(want)
if got := f.Int64(); got != want { if got := f.Int64(); got != want {
t.Errorf("got %d (%s); want %d", got, f.PString(), want) t.Errorf("got %d (%s); want %d", got, f.pstring(), want)
} }
} }
} }
...@@ -256,7 +256,7 @@ func TestFloatSetFloat64(t *testing.T) { ...@@ -256,7 +256,7 @@ func TestFloatSetFloat64(t *testing.T) {
} }
f := new(Float).SetFloat64(want) f := new(Float).SetFloat64(want)
if got, _ := f.Float64(); got != want { if got, _ := f.Float64(); got != want {
t.Errorf("got %g (%s); want %g", got, f.PString(), want) t.Errorf("got %g (%s); want %g", got, f.pstring(), want)
} }
} }
} }
...@@ -687,7 +687,7 @@ func TestFromBits(t *testing.T) { ...@@ -687,7 +687,7 @@ func TestFromBits(t *testing.T) {
for _, test := range tests { for _, test := range tests {
f := fromBits(test.bits...) f := fromBits(test.bits...)
if got := f.PString(); got != test.want { if got := f.pstring(); got != test.want {
t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want) t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want)
} }
} }
...@@ -757,7 +757,7 @@ func TestFloatSetFloat64String(t *testing.T) { ...@@ -757,7 +757,7 @@ func TestFloatSetFloat64String(t *testing.T) {
} }
} }
func TestFloatPString(t *testing.T) { func TestFloatpstring(t *testing.T) {
var tests = []struct { var tests = []struct {
x Float x Float
want string want string
...@@ -768,7 +768,7 @@ func TestFloatPString(t *testing.T) { ...@@ -768,7 +768,7 @@ func TestFloatPString(t *testing.T) {
{Float{mant: nat{0x87654321}, exp: -10}, "0.87654321p-10"}, {Float{mant: nat{0x87654321}, exp: -10}, "0.87654321p-10"},
} }
for _, test := range tests { for _, test := range tests {
if got := test.x.PString(); got != test.want { if got := test.x.pstring(); got != test.want {
t.Errorf("%v: got %s; want %s", test.x, got, test.want) t.Errorf("%v: got %s; want %s", test.x, got, test.want)
} }
} }
......
...@@ -461,7 +461,7 @@ func (x *Int) Format(s fmt.State, ch rune) { ...@@ -461,7 +461,7 @@ func (x *Int) Format(s fmt.State, ch rune) {
// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a // ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a
// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. // ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10.
// //
func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) { func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) {
// determine sign // determine sign
neg, err := scanSign(r) neg, err := scanSign(r)
if err != nil { if err != nil {
...@@ -478,9 +478,9 @@ func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) { ...@@ -478,9 +478,9 @@ func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) {
return z, base, nil return z, base, nil
} }
func scanSign(r io.RuneScanner) (neg bool, err error) { func scanSign(r io.ByteScanner) (neg bool, err error) {
var ch rune var ch byte
if ch, _, err = r.ReadRune(); err != nil { if ch, err = r.ReadByte(); err != nil {
return false, err return false, err
} }
switch ch { switch ch {
...@@ -489,11 +489,29 @@ func scanSign(r io.RuneScanner) (neg bool, err error) { ...@@ -489,11 +489,29 @@ func scanSign(r io.RuneScanner) (neg bool, err error) {
case '+': case '+':
// nothing to do // nothing to do
default: default:
r.UnreadRune() r.UnreadByte()
} }
return return
} }
// byteReader is a local wrapper around fmt.ScanState;
// it implements the ByteReader interface.
type byteReader struct {
fmt.ScanState
}
func (r byteReader) ReadByte() (byte, error) {
ch, size, err := r.ReadRune()
if size != 1 && err == nil {
err = fmt.Errorf("invalid rune %#U", ch)
}
return byte(ch), err
}
func (r byteReader) UnreadByte() error {
return r.UnreadRune()
}
// Scan is a support routine for fmt.Scanner; it sets z to the value of // Scan is a support routine for fmt.Scanner; it sets z to the value of
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), // the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). // 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
...@@ -514,7 +532,7 @@ func (z *Int) Scan(s fmt.ScanState, ch rune) error { ...@@ -514,7 +532,7 @@ func (z *Int) Scan(s fmt.ScanState, ch rune) error {
default: default:
return errors.New("Int.Scan: invalid verb") return errors.New("Int.Scan: invalid verb")
} }
_, _, err := z.scan(s, base) _, _, err := z.scan(byteReader{s}, base)
return err return err
} }
...@@ -569,7 +587,7 @@ func (z *Int) SetString(s string, base int) (*Int, bool) { ...@@ -569,7 +587,7 @@ func (z *Int) SetString(s string, base int) (*Int, bool) {
if err != nil { if err != nil {
return nil, false return nil, false
} }
_, _, err = r.ReadRune() _, err = r.ReadByte()
if err != io.EOF { if err != io.EOF {
return nil, false return nil, false
} }
......
...@@ -668,7 +668,7 @@ func pow(x Word, n int) (p Word) { ...@@ -668,7 +668,7 @@ func pow(x Word, n int) (p Word) {
// base == 1, only), and the number of fractional digits is -count. In this // base == 1, only), and the number of fractional digits is -count. In this
// case, the value of the scanned number is res * 10**count. // case, the value of the scanned number is res * 10**count.
// //
func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error) { func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error) {
// reject illegal bases // reject illegal bases
if base < 0 || base > MaxBase { if base < 0 || base > MaxBase {
err = errors.New("illegal number base") err = errors.New("illegal number base")
...@@ -676,7 +676,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error) ...@@ -676,7 +676,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error)
} }
// one char look-ahead // one char look-ahead
ch, _, err := r.ReadRune() ch, err := r.ReadByte()
if err != nil { if err != nil {
return return
} }
...@@ -687,7 +687,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error) ...@@ -687,7 +687,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error)
// actual base is 10 unless there's a base prefix // actual base is 10 unless there's a base prefix
b = 10 b = 10
if ch == '0' { if ch == '0' {
switch ch, _, err = r.ReadRune(); err { switch ch, err = r.ReadByte(); err {
case nil: case nil:
// possibly one of 0x, 0X, 0b, 0B // possibly one of 0x, 0X, 0b, 0B
b = 8 b = 8
...@@ -698,7 +698,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error) ...@@ -698,7 +698,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error)
b = 2 b = 2
} }
if b == 2 || b == 16 { if b == 2 || b == 16 {
if ch, _, err = r.ReadRune(); err != nil { if ch, err = r.ReadByte(); err != nil {
// io.EOF is also an error in this case // io.EOF is also an error in this case
return return
} }
...@@ -736,7 +736,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error) ...@@ -736,7 +736,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error)
base = 10 // no 2nd decimal point permitted base = 10 // no 2nd decimal point permitted
dp = count dp = count
// advance // advance
if ch, _, err = r.ReadRune(); err != nil { if ch, err = r.ReadByte(); err != nil {
if err == io.EOF { if err == io.EOF {
err = nil err = nil
break break
...@@ -758,7 +758,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error) ...@@ -758,7 +758,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error)
d1 = MaxBase + 1 d1 = MaxBase + 1
} }
if d1 >= b1 { if d1 >= b1 {
r.UnreadRune() // ch does not belong to number anymore r.UnreadByte() // ch does not belong to number anymore
break break
} }
count++ count++
...@@ -775,7 +775,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error) ...@@ -775,7 +775,7 @@ func (z nat) scan(r io.RuneScanner, base int) (res nat, b, count int, err error)
} }
// advance // advance
if ch, _, err = r.ReadRune(); err != nil { if ch, err = r.ReadByte(); err != nil {
if err == io.EOF { if err == io.EOF {
err = nil err = nil
break break
......
...@@ -585,7 +585,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -585,7 +585,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
} }
// there should be no unread characters left // there should be no unread characters left
if _, _, err = r.ReadRune(); err != io.EOF { if _, err = r.ReadByte(); err != io.EOF {
return nil, false return nil, false
} }
...@@ -615,11 +615,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -615,11 +615,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
return z, true return z, true
} }
func scanExponent(r io.RuneScanner) (exp int64, base int, err error) { func scanExponent(r io.ByteScanner) (exp int64, base int, err error) {
base = 10 base = 10
var ch rune var ch byte
if ch, _, err = r.ReadRune(); err != nil { if ch, err = r.ReadByte(); err != nil {
if err == io.EOF { if err == io.EOF {
err = nil // no exponent; same as e0 err = nil // no exponent; same as e0
} }
...@@ -632,7 +632,7 @@ func scanExponent(r io.RuneScanner) (exp int64, base int, err error) { ...@@ -632,7 +632,7 @@ func scanExponent(r io.RuneScanner) (exp int64, base int, err error) {
case 'p': case 'p':
base = 2 base = 2
default: default:
r.UnreadRune() r.UnreadByte()
return // no exponent; same as e0 return // no exponent; same as e0
} }
...@@ -650,7 +650,7 @@ func scanExponent(r io.RuneScanner) (exp int64, base int, err error) { ...@@ -650,7 +650,7 @@ func scanExponent(r io.RuneScanner) (exp int64, base int, err error) {
// since we only care about int64 values - the // since we only care about int64 values - the
// from-scratch scan is easy enough and faster // from-scratch scan is easy enough and faster
for i := 0; ; i++ { for i := 0; ; i++ {
if ch, _, err = r.ReadRune(); err != nil { if ch, err = r.ReadByte(); err != nil {
if err != io.EOF || i == 0 { if err != io.EOF || i == 0 {
return return
} }
...@@ -659,7 +659,7 @@ func scanExponent(r io.RuneScanner) (exp int64, base int, err error) { ...@@ -659,7 +659,7 @@ func scanExponent(r io.RuneScanner) (exp int64, base int, err error) {
} }
if ch < '0' || '9' < ch { if ch < '0' || '9' < ch {
if i == 0 { if i == 0 {
r.UnreadRune() r.UnreadByte()
err = fmt.Errorf("invalid exponent (missing digits)") err = fmt.Errorf("invalid exponent (missing digits)")
return return
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment