diff options
Diffstat (limited to 'src/pkg/big/rat.go')
-rw-r--r-- | src/pkg/big/rat.go | 136 |
1 files changed, 95 insertions, 41 deletions
diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go index ddd858d5c..e70673a1c 100644 --- a/src/pkg/big/rat.go +++ b/src/pkg/big/rat.go @@ -35,9 +35,8 @@ func (z *Rat) SetFrac(a, b *Int) *Rat { func (z *Rat) SetFrac64(a, b int64) *Rat { z.a.SetInt64(a) if b < 0 { - z.b.setUint64(uint64(-b)) + b = -b z.a.neg = !z.a.neg - return z.norm() } z.b = z.b.setUint64(uint64(b)) return z.norm() @@ -60,6 +59,23 @@ func (z *Rat) SetInt64(x int64) *Rat { } +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func (x *Rat) Sign() int { + return x.a.Sign() +} + + +// IsInt returns true if the denominator of x is 1. +func (x *Rat) IsInt() bool { + return len(x.b) == 1 && x.b[0] == 1 +} + + // Num returns the numerator of z; it may be <= 0. // The result is a reference to z's numerator; it // may change if a new value is assigned to z. @@ -126,6 +142,14 @@ func (x *Rat) Cmp(y *Rat) (r int) { } +// Abs sets z to |x| (the absolute value of x) and returns z. +func (z *Rat) Abs(x *Rat) *Rat { + z.a.Abs(&x.a) + z.b = z.b.set(x.b) + return z +} + + // Add sets z to the sum x+y and returns z. func (z *Rat) Add(x, y *Rat) *Rat { a1 := mulNat(&x.a, y.b) @@ -186,26 +210,17 @@ func (z *Rat) Set(x *Rat) *Rat { // SetString sets z to the value of s and returns z and a boolean indicating -// success. s can be given as a fraction "a/b" or as a decimal number "a.b". -// If the operation failed, the value of z is undefined. +// success. s can be given as a fraction "a/b" or as a floating-point number +// optionally followed by an exponent. If the operation failed, the value of z +// is undefined. func (z *Rat) SetString(s string) (*Rat, bool) { if len(s) == 0 { return z, false } - // Check for a decimal point - sep := strings.Index(s, ".") - if sep < 0 { - // Check for a quotient - sep = strings.Index(s, "/") - if sep < 0 { - // Just read in the string as an integer - if _, ok := z.a.SetString(s, 10); !ok { - return z, false - } - z.b = z.b.setWord(1) - return z, true - } + // check for a quotient + sep := strings.Index(s, "/") + if sep >= 0 { if _, ok := z.a.SetString(s[0:sep], 10); !ok { return z, false } @@ -214,59 +229,98 @@ func (z *Rat) SetString(s string) (*Rat, bool) { if z.b, _, n = z.b.scan(s, 10); n != len(s) { return z, false } - return z.norm(), true } - s = s[0:sep] + s[sep+1:] + // check for a decimal point + sep = strings.Index(s, ".") + // check for an exponent + e := strings.IndexAny(s, "eE") + var exp Int + if e >= 0 { + if e < sep { + // The E must come after the decimal point. + return z, false + } + if _, ok := exp.SetString(s[e+1:], 10); !ok { + return z, false + } + s = s[0:e] + } + if sep >= 0 { + s = s[0:sep] + s[sep+1:] + exp.Sub(&exp, NewInt(int64(len(s)-sep))) + } + if _, ok := z.a.SetString(s, 10); !ok { return z, false } - z.b = z.b.expNN(natTen, nat{Word(len(s) - sep)}, nil) + powTen := nat{}.expNN(natTen, exp.abs, nil) + if exp.neg { + z.b = powTen + z.norm() + } else { + z.a.abs = z.a.abs.mul(z.a.abs, powTen) + z.b = z.b.setWord(1) + } - return z.norm(), true + return z, true } -// String returns a string representation of z in the form "a/b". +// String returns a string representation of z in the form "a/b" (even if b == 1). func (z *Rat) String() string { - s := z.a.String() - if len(z.b) == 1 && z.b[0] == 1 { - return s + return z.a.String() + "/" + z.b.string(10) +} + + +// RatString returns a string representation of z in the form "a/b" if b != 1, +// and in the form "a" if b == 1. +func (z *Rat) RatString() string { + if z.IsInt() { + return z.a.String() } - return s + "/" + z.b.string(10) + return z.String() } // FloatString returns a string representation of z in decimal form with prec // digits of precision after the decimal point and the last digit rounded. func (z *Rat) FloatString(prec int) string { - q, r := nat{}.div(nat{}, z.a.abs, z.b) - - s := "" - if z.a.neg { - s = "-" + if z.IsInt() { + return z.a.String() } - s += q.string(10) - if len(z.b) == 1 && z.b[0] == 1 { - return s + q, r := nat{}.div(nat{}, z.a.abs, z.b) + + p := natOne + if prec > 0 { + p = nat{}.expNN(natTen, nat{}.setUint64(uint64(prec)), nil) } - p := nat{}.expNN(natTen, nat{Word(prec)}, nil) r = r.mul(r, p) r, r2 := r.div(nat{}, r, z.b) - // See if we need to round up - r2 = r2.mul(r2, natTwo) + // see if we need to round up + r2 = r2.add(r2, r2) if z.b.cmp(r2) <= 0 { r = r.add(r, natOne) + if r.cmp(p) >= 0 { + q = nat{}.add(q, natOne) + r = nat{}.sub(r, p) + } } - rs := r.string(10) - leadingZeros := prec - len(rs) - s += "." + strings.Repeat("0", leadingZeros) + rs - s = strings.TrimRight(s, "0") + s := q.string(10) + if z.a.neg { + s = "-" + s + } + + if prec > 0 { + rs := r.string(10) + leadingZeros := prec - len(rs) + s += "." + strings.Repeat("0", leadingZeros) + rs + } return s } |