1
0
mirror of https://github.com/golang/go synced 2024-11-23 17:00:07 -07:00

math/big: implemented Float.Int (truncation of Floats to Ints)

Change-Id: Id98f7333fe6ae1b64e0469c6d01f02360c1f8f55
Reviewed-on: https://go-review.googlesource.com/4481
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Robert Griesemer 2015-02-10 14:28:25 -08:00
parent 950aa9f1bc
commit 64e7038291
2 changed files with 95 additions and 14 deletions

View File

@ -22,7 +22,7 @@ const debugFloat = true // enable for debugging
// A Float represents a multi-precision floating point number of the form
//
// sign * mantissa * 2**exponent
// sign × mantissa × 2**exponent
//
// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp (with the
// exception of 0 and Inf which have a 0 mantissa and special exponents).
@ -231,21 +231,18 @@ func (z *Float) SetMantExp(mant *Float, exp int) *Float {
// IsInt reports whether x is an integer.
// ±Inf are not considered integers.
func (x *Float) IsInt() bool {
// pick off easy cases
if len(x.mant) == 0 {
return x.exp != infExp // x == 0
if debugFloat {
x.validate()
}
// x != 0
// pick off easy cases
if x.exp <= 0 {
return false // 0 < |x| <= 0.5
// |x| < 1 || |x| == Inf
return len(x.mant) == 0 && x.exp != infExp
}
// x.exp > 0
if uint(x.exp) >= x.prec {
return true // not enough precision for fractional mantissa
}
if debugFloat {
x.validate()
}
// x.mant[len(x.mant)-1] != 0
// determine minimum required precision for x
minPrec := uint(len(x.mant))*_W - x.mant.trailingZeroBits()
@ -264,6 +261,9 @@ func (x *Float) IsInf(sign int) bool {
// If the exponent's magnitude is too large, z becomes ±Inf.
func (z *Float) setExp(e int64) {
if -MaxExp <= e && e <= MaxExp {
if len(z.mant) == 0 {
e = 0
}
z.exp = int32(e)
return
}
@ -706,9 +706,50 @@ func (x *Float) Float64() (float64, Accuracy) {
return math.Float64frombits(s | e<<52 | m), r.acc
}
// BUG(gri) Int is not yet implemented
func (x *Float) Int() (*Int, Accuracy) {
panic("unimplemented")
// Int returns the result of truncating x towards zero; or nil
// if x is an infinity. The result is Exact if x.IsInt();
// otherwise it is Below for x > 0, and Above for x < 0.
func (x *Float) Int() (res *Int, acc Accuracy) {
if debugFloat {
x.validate()
}
// accuracy for inexact results
acc = Below // truncation
if x.neg {
acc = Above
}
// pick off easy cases
if x.exp <= 0 {
// |x| < 1 || |x| == Inf
if x.exp == infExp {
return nil, acc // ±Inf
}
if len(x.mant) == 0 {
acc = Exact // ±0
}
return new(Int), acc // ±0.xxx
}
// x.exp > 0
// x.mant[len(x.mant)-1] != 0
// determine minimum required precision for x
allBits := uint(len(x.mant)) * _W
minPrec := allBits - x.mant.trailingZeroBits()
exp := uint(x.exp)
if exp >= minPrec {
acc = Exact
}
// shift mantissa as needed
res = &Int{neg: x.neg}
// TODO(gri) should have a shift that takes positive and negative shift counts
switch {
case exp > allBits:
res.abs = res.abs.shl(x.mant, exp-allBits)
default:
res.abs = res.abs.set(x.mant)
case exp < allBits:
res.abs = res.abs.shr(x.mant, allBits-exp)
}
return
}
// BUG(gri) Rat is not yet implemented
@ -1042,7 +1083,6 @@ func (z *Float) Mul(x, y *Float) *Float {
}
// Quo sets z to the rounded quotient x/y and returns z.
// If y == 0, a division-by-zero run-time panic occurs. TODO(gri) this should become Inf
// Precision, rounding, and accuracy reporting are as for Add.
func (z *Float) Quo(x, y *Float) *Float {
if z.prec == 0 {

View File

@ -75,7 +75,7 @@ func makeFloat(s string) *Float {
return NewInf(-1)
}
var x Float
x.prec = 100 // TODO(gri) find a better way to do this
x.prec = 1000 // TODO(gri) find a better way to do this
if _, ok := x.SetString(s); !ok {
panic(fmt.Sprintf("%q is not a valid float", s))
}
@ -553,6 +553,46 @@ func TestFloatSetRat(t *testing.T) {
}
}
func TestFloatInt(t *testing.T) {
for _, test := range []struct {
x string
out string
acc Accuracy
}{
{"0", "0", Exact},
{"+0", "0", Exact},
{"-0", "0", Exact},
{"Inf", "nil", Below},
{"+Inf", "nil", Below},
{"-Inf", "nil", Above},
{"1", "1", Exact},
{"-1", "-1", Exact},
{"1.23", "1", Below},
{"-1.23", "-1", Above},
{"123e-2", "1", Below},
{"123e-3", "0", Below},
{"123e-4", "0", Below},
{"1e-1000", "0", Below},
{"-1e-1000", "0", Above},
{"1e+10", "10000000000", Exact},
{"1e+100", "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Exact},
} {
x := makeFloat(test.x)
out, acc := x.Int()
got := "nil"
if out != nil {
got = out.String()
}
if got != test.out || acc != test.acc {
t.Errorf("%s: got %s (%s); want %s (%s)", test.x, got, acc, test.out, test.acc)
}
}
}
func TestFloatRat(t *testing.T) {
// TODO(gri) implement this
}
// Selected precisions with which to run various tests.
var precList = [...]uint{1, 2, 5, 8, 10, 16, 23, 24, 32, 50, 53, 64, 100, 128, 500, 511, 512, 513, 1000, 10000}
@ -678,6 +718,7 @@ func TestFloatAdd64(t *testing.T) {
}
func TestFloatMul(t *testing.T) {
// TODO(gri) implement this
}
// TestFloatMul64 tests that Float.Mul/Quo of numbers with