diff options
| author | Robert Griesemer <gri@golang.org> | 2015-01-30 14:53:53 -0800 |
|---|---|---|
| committer | Robert Griesemer <gri@golang.org> | 2015-01-30 23:03:06 +0000 |
| commit | f17cd880895863e2c13396296819d0dce81fa43f (patch) | |
| tree | 388b07704de7a148d687a0ffcf2f53d2e4ec188d /src/math/big/floatconv.go | |
| parent | 20a96a1f68cfe608b8066f3ee1b0db28d1d3e4e0 (diff) | |
| download | go-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.go | 256 |
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() +} |
