aboutsummaryrefslogtreecommitdiff
path: root/src/math/big/floatconv.go
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2015-01-30 14:53:53 -0800
committerRobert Griesemer <gri@golang.org>2015-01-30 23:03:06 +0000
commitf17cd880895863e2c13396296819d0dce81fa43f (patch)
tree388b07704de7a148d687a0ffcf2f53d2e4ec188d /src/math/big/floatconv.go
parent20a96a1f68cfe608b8066f3ee1b0db28d1d3e4e0 (diff)
downloadgo-f17cd880895863e2c13396296819d0dce81fa43f.tar.xz
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/3674 Reviewed-by: Alan Donovan <adonovan@google.com>
Diffstat (limited to 'src/math/big/floatconv.go')
-rw-r--r--src/math/big/floatconv.go256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go
new file mode 100644
index 0000000000..06bbdbcb02
--- /dev/null
+++ b/src/math/big/floatconv.go
@@ -0,0 +1,256 @@
+// 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()
+}