Commit f17cd880 authored by Robert Griesemer's avatar Robert Griesemer

math/big: split float conversion routines and tests into separate files

No other functional changes.

Change-Id: I7e0bb7452c6a265535297ec7ce6a629f1aff695c
Reviewed-on: https://go-review.googlesource.com/3674Reviewed-by: 's avatarAlan Donovan <adonovan@google.com>
parent 20a96a1f
......@@ -14,11 +14,8 @@
package big
import (
"bytes"
"fmt"
"io"
"math"
"strings"
)
// TODO(gri): Determine if there's a more natural way to set the precision.
......@@ -958,245 +955,3 @@ func (x *Float) Sign() int {
}
return 1
}
// SetString sets z to the value of s and returns z and a boolean indicating
// success. s must be a floating-point number of the same format as accepted
// by Scan, with number prefixes permitted.
func (z *Float) SetString(s string) (*Float, bool) {
r := strings.NewReader(s)
f, _, err := z.Scan(r, 0)
if err != nil {
return nil, false
}
// there should be no unread characters left
if _, _, err = r.ReadRune(); err != io.EOF {
return nil, false
}
return f, true
}
// 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 = [ sign ] [ prefix ] mantissa [ exponent ] .
// sign = "+" | "-" .
// prefix = "0" ( "x" | "X" | "b" | "B" ) .
// mantissa = digits | digits "." [ digits ] | "." digits .
// exponent = ( "E" | "e" | "p" ) [ sign ] digits .
// digits = digit { digit } .
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
//
// The base argument must be 0 or a value between 2 through MaxBase.
//
// 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.
// 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) This signature conflicts with Scan(s fmt.ScanState, ch rune) error.
func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
// sign
z.neg, err = scanSign(r)
if err != nil {
return
}
// mantissa
var ecorr int // decimal exponent correction; valid if <= 0
z.mant, b, ecorr, err = z.mant.scan(r, base, true)
if err != nil {
return
}
// exponent
var exp int64
var ebase int
exp, ebase, err = scanExponent(r)
if err != nil {
return
}
// special-case 0
if len(z.mant) == 0 {
z.exp = 0
f = z
return
}
// len(z.mant) > 0
// determine binary (exp2) and decimal (exp) exponent
exp2 := int64(len(z.mant)*_W - int(fnorm(z.mant)))
if ebase == 2 {
exp2 += exp
exp = 0
}
if ecorr < 0 {
exp += int64(ecorr)
}
z.setExp(exp2)
if exp == 0 {
// no decimal exponent
z.round(0)
f = z
return
}
// exp != 0
// compute decimal exponent power
expabs := exp
if expabs < 0 {
expabs = -expabs
}
powTen := new(Float).SetInt(new(Int).SetBits(nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)))
// correct result
if exp < 0 {
z.uquo(z, powTen)
} else {
z.umul(z, powTen)
}
f = z
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' like 'e' for large exponents, like 'f' otherwise
// 'G' like 'E' for large exponents, like 'f' otherwise
// 'b' -ddddddp±dd, binary exponent
// 'p' -0x.dddp±dd, binary exponent, hexadecimal mantissa
//
// For the binary exponent formats, the mantissa is printed in normalized form:
//
// 'b' decimal integer mantissa using x.Precision() bits, or -0
// 'p' hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
//
// 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 'b' and 'p' format.
func (x *Float) Format(format byte, prec int) string {
switch format {
case 'b':
return x.bstring()
case 'p':
return x.pstring()
}
return fmt.Sprintf(`%%!c(%s)`, format, x.pstring())
}
// BUG(gri): Currently, String uses the 'p' (rather than 'g') format.
func (x *Float) String() string {
return x.Format('p', 0)
}
// TODO(gri) The 'b' and 'p' formats have different meanings here than
// in strconv: in strconv, the printed exponent is the biased (hardware)
// exponent; here it is the unbiased exponent. Decide what to do.
// (a strconv 'p' formatted float value can only be interpreted correctly
// if the bias is known; i.e., we must know if it's a 32bit or 64bit number).
// bstring returns x as a string in the format ["-"] mantissa "p" exponent
// with a decimal mantissa and a binary exponent, or ["-"] "0" if x is zero.
// The mantissa is normalized such that is uses x.Precision() bits in binary
// representation.
func (x *Float) bstring() string {
// TODO(gri) handle Inf
if len(x.mant) == 0 {
if x.neg {
return "-0"
}
return "0"
}
// x != 0
// normalize mantissa
m := x.mant
t := uint(len(x.mant)*_W) - x.prec // 0 <= t < _W
if t > 0 {
m = nat(nil).shr(m, t)
}
var buf bytes.Buffer
if x.neg {
buf.WriteByte('-')
}
buf.WriteString(m.decimalString())
fmt.Fprintf(&buf, "p%d", x.exp)
return buf.String()
}
// pstring returns x as a string in the format ["-"] "0x." mantissa "p" exponent
// with a hexadecimal mantissa and a binary exponent, or ["-"] "0" if x is zero.
// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
func (x *Float) pstring() string {
// TODO(gri) handle Inf
if len(x.mant) == 0 {
if x.neg {
return "-0"
}
return "0"
}
// x != 0
// mantissa is stored in normalized form
var buf bytes.Buffer
if x.neg {
buf.WriteByte('-')
}
buf.WriteString("0x.")
buf.WriteString(strings.TrimRight(x.mant.hexString(), "0"))
fmt.Fprintf(&buf, "p%d", x.exp)
return buf.String()
}
......@@ -682,104 +682,3 @@ func TestFromBits(t *testing.T) {
}
}
}
var floatSetFloat64StringTests = []struct {
s string
x float64
}{
{"0", 0},
{"-0", -0},
{"+0", 0},
{"1", 1},
{"-1", -1},
{"+1", 1},
{"1.234", 1.234},
{"-1.234", -1.234},
{"+1.234", 1.234},
{".1", 0.1},
{"1.", 1},
{"+1.", 1},
{"0e100", 0},
{"-0e+100", 0},
{"+0e-100", 0},
{"0E100", 0},
{"-0E+100", 0},
{"+0E-100", 0},
{"0p100", 0},
{"-0p+100", 0},
{"+0p-100", 0},
{"1.e10", 1e10},
{"1e+10", 1e10},
{"+1e-10", 1e-10},
{"1E10", 1e10},
{"1.E+10", 1e10},
{"+1E-10", 1e-10},
{"1p10", 1 << 10},
{"1p+10", 1 << 10},
{"+1.p-10", 1.0 / (1 << 10)},
{"-687436.79457e-245", -687436.79457e-245},
{"-687436.79457E245", -687436.79457e245},
{"1024.p-12", 0.25},
{"-1.p10", -1024},
{"0.25p2", 1},
{".0000000000000000000000000000000000000001", 1e-40},
{"+10000000000000000000000000000000000000000e-0", 1e40},
}
func TestFloatSetFloat64String(t *testing.T) {
for _, test := range floatSetFloat64StringTests {
var x Float
x.prec = 53 // TODO(gri) find better solution
_, ok := x.SetString(test.s)
if !ok {
t.Errorf("%s: parse error", test.s)
continue
}
f, _ := x.Float64()
want := new(Float).SetFloat64(test.x)
if x.Cmp(want) != 0 {
t.Errorf("%s: got %s (%v); want %v", test.s, &x, f, test.x)
}
}
}
func TestFloatFormat(t *testing.T) {
for _, test := range []struct {
x string
format byte
prec int
want string
}{
{"0", 'b', 0, "0"},
{"-0", 'b', 0, "-0"},
{"1.0", 'b', 0, "4503599627370496p1"},
{"-1.0", 'b', 0, "-4503599627370496p1"},
{"0", 'p', 0, "0"},
{"-0", 'p', 0, "-0"},
{"1024.0", 'p', 0, "0x.8p11"},
{"-1024.0", 'p', 0, "-0x.8p11"},
} {
f64, err := strconv.ParseFloat(test.x, 64)
if err != nil {
t.Error(err)
continue
}
f := new(Float).SetFloat64(f64)
got := f.Format(test.format, test.prec)
if got != test.want {
t.Errorf("%v: got %s", test, got)
}
if test.format == 'b' || test.format == 'p' {
continue // 'b', 'p' format not supported or different in strconv.Format
}
want := strconv.FormatFloat(f64, test.format, test.prec, 64)
if got != want {
t.Errorf("%v: got %s; want %s", test, got, want)
}
}
}
// Copyright 2015 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.
// This file implements float-to-string conversion functions.
package big
import (
"bytes"
"fmt"
"io"
"strings"
)
// SetString sets z to the value of s and returns z and a boolean indicating
// success. s must be a floating-point number of the same format as accepted
// by Scan, with number prefixes permitted.
func (z *Float) SetString(s string) (*Float, bool) {
r := strings.NewReader(s)
f, _, err := z.Scan(r, 0)
if err != nil {
return nil, false
}
// there should be no unread characters left
if _, _, err = r.ReadRune(); err != io.EOF {
return nil, false
}
return f, true
}
// 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 = [ sign ] [ prefix ] mantissa [ exponent ] .
// sign = "+" | "-" .
// prefix = "0" ( "x" | "X" | "b" | "B" ) .
// mantissa = digits | digits "." [ digits ] | "." digits .
// exponent = ( "E" | "e" | "p" ) [ sign ] digits .
// digits = digit { digit } .
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
//
// The base argument must be 0 or a value between 2 through MaxBase.
//
// 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.
// 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) This signature conflicts with Scan(s fmt.ScanState, ch rune) error.
func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
// sign
z.neg, err = scanSign(r)
if err != nil {
return
}
// mantissa
var ecorr int // decimal exponent correction; valid if <= 0
z.mant, b, ecorr, err = z.mant.scan(r, base, true)
if err != nil {
return
}
// exponent
var exp int64
var ebase int
exp, ebase, err = scanExponent(r)
if err != nil {
return
}
// special-case 0
if len(z.mant) == 0 {
z.exp = 0
f = z
return
}
// len(z.mant) > 0
// determine binary (exp2) and decimal (exp) exponent
exp2 := int64(len(z.mant)*_W - int(fnorm(z.mant)))
if ebase == 2 {
exp2 += exp
exp = 0
}
if ecorr < 0 {
exp += int64(ecorr)
}
z.setExp(exp2)
if exp == 0 {
// no decimal exponent
z.round(0)
f = z
return
}
// exp != 0
// compute decimal exponent power
expabs := exp
if expabs < 0 {
expabs = -expabs
}
powTen := new(Float).SetInt(new(Int).SetBits(nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)))
// correct result
if exp < 0 {
z.uquo(z, powTen)
} else {
z.umul(z, powTen)
}
f = z
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' like 'e' for large exponents, like 'f' otherwise
// 'G' like 'E' for large exponents, like 'f' otherwise
// 'b' -ddddddp±dd, binary exponent
// 'p' -0x.dddp±dd, binary exponent, hexadecimal mantissa
//
// For the binary exponent formats, the mantissa is printed in normalized form:
//
// 'b' decimal integer mantissa using x.Precision() bits, or -0
// 'p' hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
//
// 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 'b' and 'p' format.
func (x *Float) Format(format byte, prec int) string {
switch format {
case 'b':
return x.bstring()
case 'p':
return x.pstring()
}
return fmt.Sprintf(`%%!c(%s)`, format, x.pstring())
}
// BUG(gri): Currently, String uses the 'p' (rather than 'g') format.
func (x *Float) String() string {
return x.Format('p', 0)
}
// TODO(gri) The 'b' and 'p' formats have different meanings here than
// in strconv: in strconv, the printed exponent is the biased (hardware)
// exponent; here it is the unbiased exponent. Decide what to do.
// (a strconv 'p' formatted float value can only be interpreted correctly
// if the bias is known; i.e., we must know if it's a 32bit or 64bit number).
// bstring returns x as a string in the format ["-"] mantissa "p" exponent
// with a decimal mantissa and a binary exponent, or ["-"] "0" if x is zero.
// The mantissa is normalized such that is uses x.Precision() bits in binary
// representation.
func (x *Float) bstring() string {
// TODO(gri) handle Inf
if len(x.mant) == 0 {
if x.neg {
return "-0"
}
return "0"
}
// x != 0
// normalize mantissa
m := x.mant
t := uint(len(x.mant)*_W) - x.prec // 0 <= t < _W
if t > 0 {
m = nat(nil).shr(m, t)
}
var buf bytes.Buffer
if x.neg {
buf.WriteByte('-')
}
buf.WriteString(m.decimalString())
fmt.Fprintf(&buf, "p%d", x.exp)
return buf.String()
}
// pstring returns x as a string in the format ["-"] "0x." mantissa "p" exponent
// with a hexadecimal mantissa and a binary exponent, or ["-"] "0" if x is zero.
// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
func (x *Float) pstring() string {
// TODO(gri) handle Inf
if len(x.mant) == 0 {
if x.neg {
return "-0"
}
return "0"
}
// x != 0
// mantissa is stored in normalized form
var buf bytes.Buffer
if x.neg {
buf.WriteByte('-')
}
buf.WriteString("0x.")
buf.WriteString(strings.TrimRight(x.mant.hexString(), "0"))
fmt.Fprintf(&buf, "p%d", x.exp)
return buf.String()
}
// Copyright 2015 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.
package big
import (
"strconv"
"testing"
)
var floatSetFloat64StringTests = []struct {
s string
x float64
}{
{"0", 0},
{"-0", -0},
{"+0", 0},
{"1", 1},
{"-1", -1},
{"+1", 1},
{"1.234", 1.234},
{"-1.234", -1.234},
{"+1.234", 1.234},
{".1", 0.1},
{"1.", 1},
{"+1.", 1},
{"0e100", 0},
{"-0e+100", 0},
{"+0e-100", 0},
{"0E100", 0},
{"-0E+100", 0},
{"+0E-100", 0},
{"0p100", 0},
{"-0p+100", 0},
{"+0p-100", 0},
{"1.e10", 1e10},
{"1e+10", 1e10},
{"+1e-10", 1e-10},
{"1E10", 1e10},
{"1.E+10", 1e10},
{"+1E-10", 1e-10},
{"1p10", 1 << 10},
{"1p+10", 1 << 10},
{"+1.p-10", 1.0 / (1 << 10)},
{"-687436.79457e-245", -687436.79457e-245},
{"-687436.79457E245", -687436.79457e245},
{"1024.p-12", 0.25},
{"-1.p10", -1024},
{"0.25p2", 1},
{".0000000000000000000000000000000000000001", 1e-40},
{"+10000000000000000000000000000000000000000e-0", 1e40},
}
func TestFloatSetFloat64String(t *testing.T) {
for _, test := range floatSetFloat64StringTests {
var x Float
x.prec = 53 // TODO(gri) find better solution
_, ok := x.SetString(test.s)
if !ok {
t.Errorf("%s: parse error", test.s)
continue
}
f, _ := x.Float64()
want := new(Float).SetFloat64(test.x)
if x.Cmp(want) != 0 {
t.Errorf("%s: got %s (%v); want %v", test.s, &x, f, test.x)
}
}
}
func TestFloatFormat(t *testing.T) {
for _, test := range []struct {
x string
format byte
prec int
want string
}{
{"0", 'b', 0, "0"},
{"-0", 'b', 0, "-0"},
{"1.0", 'b', 0, "4503599627370496p1"},
{"-1.0", 'b', 0, "-4503599627370496p1"},
{"0", 'p', 0, "0"},
{"-0", 'p', 0, "-0"},
{"1024.0", 'p', 0, "0x.8p11"},
{"-1024.0", 'p', 0, "-0x.8p11"},
} {
f64, err := strconv.ParseFloat(test.x, 64)
if err != nil {
t.Error(err)
continue
}
f := new(Float).SetFloat64(f64)
got := f.Format(test.format, test.prec)
if got != test.want {
t.Errorf("%v: got %s", test, got)
}
if test.format == 'b' || test.format == 'p' {
continue // 'b', 'p' format not supported or different in strconv.Format
}
want := strconv.FormatFloat(f64, test.format, test.prec, 64)
if got != want {
t.Errorf("%v: got %s; want %s", test, got, want)
}
}
}
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