mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
math/big: fix several issues with string->Float conversion
Change-Id: I7bf7154e2d8d779fdf7f1d2bb561a06ad174f3b0 Reviewed-on: https://go-review.googlesource.com/4883 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
291bf1f03f
commit
d9859ad404
3 changed files with 128 additions and 45 deletions
|
|
@ -537,7 +537,7 @@ func (z *Float) SetFloat64(x float64) *Float {
|
|||
// fnorm normalizes mantissa m by shifting it to the left
|
||||
// such that the msb of the most-significant word (msw) is 1.
|
||||
// It returns the shift amount. It assumes that len(m) != 0.
|
||||
func fnorm(m nat) uint {
|
||||
func fnorm(m nat) int64 {
|
||||
if debugFloat && (len(m) == 0 || m[len(m)-1] == 0) {
|
||||
panic("msw of mantissa is 0")
|
||||
}
|
||||
|
|
@ -548,7 +548,7 @@ func fnorm(m nat) uint {
|
|||
panic("nlz or shlVU incorrect")
|
||||
}
|
||||
}
|
||||
return s
|
||||
return int64(s)
|
||||
}
|
||||
|
||||
// SetInt sets z to the (possibly rounded) value of x and returns z.
|
||||
|
|
@ -884,7 +884,7 @@ func (z *Float) uadd(x, y *Float) {
|
|||
}
|
||||
// len(z.mant) > 0
|
||||
|
||||
z.setExp(ex + int64(len(z.mant))*_W - int64(fnorm(z.mant)))
|
||||
z.setExp(ex + int64(len(z.mant))*_W - fnorm(z.mant))
|
||||
z.round(0)
|
||||
}
|
||||
|
||||
|
|
@ -926,7 +926,7 @@ func (z *Float) usub(x, y *Float) {
|
|||
}
|
||||
// len(z.mant) > 0
|
||||
|
||||
z.setExp(ex + int64(len(z.mant))*_W - int64(fnorm(z.mant)))
|
||||
z.setExp(ex + int64(len(z.mant))*_W - fnorm(z.mant))
|
||||
z.round(0)
|
||||
}
|
||||
|
||||
|
|
@ -947,7 +947,7 @@ func (z *Float) umul(x, y *Float) {
|
|||
z.mant = z.mant.mul(x.mant, y.mant)
|
||||
|
||||
// normalize mantissa
|
||||
z.setExp(e - int64(fnorm(z.mant)))
|
||||
z.setExp(e - fnorm(z.mant))
|
||||
z.round(0)
|
||||
}
|
||||
|
||||
|
|
@ -986,7 +986,7 @@ func (z *Float) uquo(x, y *Float) {
|
|||
e := int64(x.exp) - int64(y.exp) - int64(d-len(z.mant))*_W
|
||||
|
||||
// normalize mantissa
|
||||
z.setExp(e - int64(fnorm(z.mant)))
|
||||
z.setExp(e - fnorm(z.mant))
|
||||
|
||||
// The result is long enough to include (at least) the rounding bit.
|
||||
// If there's a non-zero remainder, the corresponding fractional part
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func (z *Float) SetString(s string) (*Float, bool) {
|
|||
}
|
||||
|
||||
// there should be no unread characters left
|
||||
if _, _, err = r.ReadRune(); err != io.EOF {
|
||||
if _, err = r.ReadByte(); err != io.EOF {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
|
|
@ -35,8 +35,10 @@ func (z *Float) SetString(s string) (*Float, bool) {
|
|||
// 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:
|
||||
// It sets z to the (possibly rounded) value of the corresponding
|
||||
// floating-point number, and returns z, the actual base b, and an
|
||||
// error err, if any. If z's precision is 0, it is changed to 64
|
||||
// before rounding takes effect. The number must be of the form:
|
||||
//
|
||||
// number = [ sign ] [ prefix ] mantissa [ exponent ] .
|
||||
// sign = "+" | "-" .
|
||||
|
|
@ -50,16 +52,23 @@ func (z *Float) SetString(s string) (*Float, bool) {
|
|||
// argument will lead to a run-time panic.
|
||||
//
|
||||
// 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
|
||||
// "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 accepted.
|
||||
// The octal prefix ``0'' is not supported.
|
||||
// The octal prefix "0" is not supported (a leading "0" is simply
|
||||
// considered a "0").
|
||||
//
|
||||
// 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.
|
||||
// A "p" exponent indicates a binary (rather then decimal) exponent;
|
||||
// for instance "0x1.fffffffffffffp1023" (using base 0) represents the
|
||||
// maximum float64 value. For hexadecimal mantissae, the exponent must
|
||||
// be binary, if present (an "e" or "E" exponent indicator cannot be
|
||||
// distinguished from a mantissa digit).
|
||||
//
|
||||
// BUG(gri) This signature conflicts with Scan(s fmt.ScanState, ch rune) error.
|
||||
// TODO(gri) What should the default precision be?
|
||||
func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
|
||||
if z.prec == 0 {
|
||||
z.prec = 64
|
||||
}
|
||||
|
||||
// sign
|
||||
z.neg, err = scanSign(r)
|
||||
if err != nil {
|
||||
|
|
@ -67,8 +76,8 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
|
|||
}
|
||||
|
||||
// mantissa
|
||||
var ecorr int // decimal exponent correction; valid if <= 0
|
||||
z.mant, b, ecorr, err = z.mant.scan(r, base, true)
|
||||
var fcount int // fractional digit count; valid if <= 0
|
||||
z.mant, b, fcount, err = z.mant.scan(r, base, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -80,48 +89,82 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// set result
|
||||
f = z
|
||||
|
||||
// special-case 0
|
||||
if len(z.mant) == 0 {
|
||||
z.acc = Exact
|
||||
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)))
|
||||
// The mantissa may have a decimal point (fcount <= 0) and there
|
||||
// may be a nonzero exponent exp. The decimal point amounts to a
|
||||
// division by b**(-fcount). An exponent means multiplication by
|
||||
// ebase**exp. Finally, mantissa normalization (shift left) requires
|
||||
// a correcting multiplication by 2**(-shiftcount). Multiplications
|
||||
// are commutative, so we can apply them in any order as long as there
|
||||
// is no loss of precision. We only have powers of 2 and 10; keep
|
||||
// track via separate exponents exp2 and exp10.
|
||||
|
||||
// normalize mantissa and get initial binary exponent
|
||||
var exp2 = int64(len(z.mant))*_W - fnorm(z.mant)
|
||||
|
||||
// determine binary or decimal exponent contribution of decimal point
|
||||
var exp10 int64
|
||||
if fcount < 0 {
|
||||
// The mantissa has a "decimal" point ddd.dddd; and
|
||||
// -fcount is the number of digits to the right of '.'.
|
||||
// Adjust relevant exponent accodingly.
|
||||
switch b {
|
||||
case 16:
|
||||
fcount *= 4 // hexadecimal digits are 4 bits each
|
||||
fallthrough
|
||||
case 2:
|
||||
exp2 += int64(fcount)
|
||||
default: // b == 10
|
||||
exp10 = int64(fcount)
|
||||
}
|
||||
// we don't need fcount anymore
|
||||
}
|
||||
|
||||
// take actual exponent into account
|
||||
if ebase == 2 {
|
||||
exp2 += exp
|
||||
exp = 0
|
||||
}
|
||||
if ecorr < 0 {
|
||||
exp += int64(ecorr)
|
||||
} else { // ebase == 10
|
||||
exp10 += exp
|
||||
}
|
||||
// we don't need exp anymore
|
||||
|
||||
// apply 2**exp2
|
||||
z.setExp(exp2)
|
||||
if exp == 0 {
|
||||
// no decimal exponent
|
||||
|
||||
if exp10 == 0 {
|
||||
// no decimal exponent to consider
|
||||
z.round(0)
|
||||
f = z
|
||||
return
|
||||
}
|
||||
// exp != 0
|
||||
// exp10 != 0
|
||||
|
||||
// compute decimal exponent power
|
||||
expabs := exp
|
||||
expabs := exp10
|
||||
if expabs < 0 {
|
||||
expabs = -expabs
|
||||
}
|
||||
powTen := new(Float).SetInt(new(Int).SetBits(nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)))
|
||||
powTen := nat(nil).expNN(natTen, nat(nil).setUint64(uint64(expabs)), nil)
|
||||
fpowTen := new(Float).SetInt(new(Int).SetBits(powTen))
|
||||
|
||||
// correct result
|
||||
if exp < 0 {
|
||||
z.uquo(z, powTen)
|
||||
// apply 10**exp10
|
||||
// (uquo and umul do the rounding)
|
||||
if exp10 < 0 {
|
||||
z.uquo(z, fpowTen)
|
||||
} else {
|
||||
z.umul(z, powTen)
|
||||
z.umul(z, fpowTen)
|
||||
}
|
||||
|
||||
f = z
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ func TestFloatSetFloat64String(t *testing.T) {
|
|||
s string
|
||||
x float64
|
||||
}{
|
||||
// basics
|
||||
{"0", 0},
|
||||
{"-0", -0},
|
||||
{"+0", 0},
|
||||
|
|
@ -28,34 +29,69 @@ func TestFloatSetFloat64String(t *testing.T) {
|
|||
{"1.", 1},
|
||||
{"+1.", 1},
|
||||
|
||||
// various zeros
|
||||
{"0e100", 0},
|
||||
{"-0e+100", 0},
|
||||
{"+0e-100", 0},
|
||||
{"0E100", 0},
|
||||
{"-0E+100", 0},
|
||||
{"+0E-100", 0},
|
||||
{"0p100", 0},
|
||||
{"-0p+100", 0},
|
||||
{"+0p-100", 0},
|
||||
|
||||
// various decimal exponent formats
|
||||
{"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)},
|
||||
|
||||
// misc decimal values
|
||||
{"3.14159265", 3.14159265},
|
||||
{"-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},
|
||||
|
||||
// decimal mantissa, binary exponent
|
||||
{"0p0", 0},
|
||||
{"-0p0", -0},
|
||||
{"1p10", 1 << 10},
|
||||
{"1p+10", 1 << 10},
|
||||
{"+1p-10", 1.0 / (1 << 10)},
|
||||
{"1024p-12", 0.25},
|
||||
{"-1p10", -1024},
|
||||
{"1.5p1", 3},
|
||||
|
||||
// binary mantissa, decimal exponent
|
||||
{"0b0", 0},
|
||||
{"-0b0", -0},
|
||||
{"0b0e+10", 0},
|
||||
{"-0b0e-10", -0},
|
||||
{"0b1010", 10},
|
||||
{"0B1010E2", 1000},
|
||||
{"0b.1", 0.5},
|
||||
{"0b.001", 0.125},
|
||||
{"0b.001e3", 125},
|
||||
|
||||
// binary mantissa, binary exponent
|
||||
{"0b0p+10", 0},
|
||||
{"-0b0p-10", -0},
|
||||
{"0b.1010p4", 10},
|
||||
{"0b1p-1", 0.5},
|
||||
{"0b001p-3", 0.125},
|
||||
{"0b.001p3", 1},
|
||||
{"0b0.01p2", 1},
|
||||
|
||||
// hexadecimal mantissa and exponent
|
||||
{"0x0", 0},
|
||||
{"-0x0", -0},
|
||||
{"0x0p+10", 0},
|
||||
{"-0x0p-10", -0},
|
||||
{"0xff", 255},
|
||||
{"0X.8p1", 1},
|
||||
{"-0X0.00008p16", -0.5},
|
||||
{"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64},
|
||||
{"0x1.fffffffffffffp1023", math.MaxFloat64},
|
||||
} {
|
||||
var x Float
|
||||
x.SetPrec(53)
|
||||
|
|
@ -341,7 +377,11 @@ func TestFloatFormat(t *testing.T) {
|
|||
// and its output for 0.0 prints a biased exponent value
|
||||
// as in 0p-1074 which makes no sense to emulate here)
|
||||
if test.prec == 53 && test.format != 'p' && f.Sign() != 0 {
|
||||
f64, _ := f.Float64()
|
||||
f64, acc := f.Float64()
|
||||
if acc != Exact {
|
||||
t.Errorf("%v: expected exact conversion to float64", test)
|
||||
continue
|
||||
}
|
||||
got := strconv.FormatFloat(f64, test.format, test.digits, 64)
|
||||
if got != test.want {
|
||||
t.Errorf("%v: got %s; want %s", test, got, test.want)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue