gcc/libgo/go/math/big/rat_test.go
Ian Lance Taylor f8d9fa9e80 libgo, compiler: Upgrade libgo to Go 1.4, except for runtime.
This upgrades all of libgo other than the runtime package to
the Go 1.4 release.  In Go 1.4 much of the runtime was
rewritten into Go.  Merging that code will take more time and
will not change the API, so I'm putting it off for now.

There are a few runtime changes anyhow, to accomodate other
packages that rely on minor modifications to the runtime
support.

The compiler changes slightly to add a one-bit flag to each
type descriptor kind that is stored directly in an interface,
which for gccgo is currently only pointer types.  Another
one-bit flag (gcprog) is reserved because it is used by the gc
compiler, but gccgo does not currently use it.

There is another error check in the compiler since I ran
across it during testing.

gotools/:
	* Makefile.am (go_cmd_go_files): Sort entries.  Add generate.go.
	* Makefile.in: Rebuild.

From-SVN: r219627
2015-01-15 00:27:56 +00:00

1161 lines
29 KiB
Go

// Copyright 2010 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.
package big
import (
"bytes"
"encoding/gob"
"encoding/json"
"encoding/xml"
"fmt"
"math"
"strconv"
"strings"
"testing"
)
func TestZeroRat(t *testing.T) {
var x, y, z Rat
y.SetFrac64(0, 42)
if x.Cmp(&y) != 0 {
t.Errorf("x and y should be both equal and zero")
}
if s := x.String(); s != "0/1" {
t.Errorf("got x = %s, want 0/1", s)
}
if s := x.RatString(); s != "0" {
t.Errorf("got x = %s, want 0", s)
}
z.Add(&x, &y)
if s := z.RatString(); s != "0" {
t.Errorf("got x+y = %s, want 0", s)
}
z.Sub(&x, &y)
if s := z.RatString(); s != "0" {
t.Errorf("got x-y = %s, want 0", s)
}
z.Mul(&x, &y)
if s := z.RatString(); s != "0" {
t.Errorf("got x*y = %s, want 0", s)
}
// check for division by zero
defer func() {
if s := recover(); s == nil || s.(string) != "division by zero" {
panic(s)
}
}()
z.Quo(&x, &y)
}
var setStringTests = []struct {
in, out string
ok bool
}{
{"0", "0", true},
{"-0", "0", true},
{"1", "1", true},
{"-1", "-1", true},
{"1.", "1", true},
{"1e0", "1", true},
{"1.e1", "10", true},
{in: "1e", ok: false},
{in: "1.e", ok: false},
{in: "1e+14e-5", ok: false},
{in: "1e4.5", ok: false},
{in: "r", ok: false},
{in: "a/b", ok: false},
{in: "a.b", ok: false},
{"-0.1", "-1/10", true},
{"-.1", "-1/10", true},
{"2/4", "1/2", true},
{".25", "1/4", true},
{"-1/5", "-1/5", true},
{"8129567.7690E14", "812956776900000000000", true},
{"78189e+4", "781890000", true},
{"553019.8935e+8", "55301989350000", true},
{"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true},
{"9877861857500000E-7", "3951144743/4", true},
{"2169378.417e-3", "2169378417/1000000", true},
{"884243222337379604041632732738665534", "884243222337379604041632732738665534", true},
{"53/70893980658822810696", "53/70893980658822810696", true},
{"106/141787961317645621392", "53/70893980658822810696", true},
{"204211327800791583.81095", "4084226556015831676219/20000", true},
{in: "1/0", ok: false},
}
func TestRatSetString(t *testing.T) {
for i, test := range setStringTests {
x, ok := new(Rat).SetString(test.in)
if ok {
if !test.ok {
t.Errorf("#%d SetString(%q) expected failure", i, test.in)
} else if x.RatString() != test.out {
t.Errorf("#%d SetString(%q) got %s want %s", i, test.in, x.RatString(), test.out)
}
} else if x != nil {
t.Errorf("#%d SetString(%q) got %p want nil", i, test.in, x)
}
}
}
func TestRatScan(t *testing.T) {
var buf bytes.Buffer
for i, test := range setStringTests {
x := new(Rat)
buf.Reset()
buf.WriteString(test.in)
_, err := fmt.Fscanf(&buf, "%v", x)
if err == nil != test.ok {
if test.ok {
t.Errorf("#%d error: %s", i, err)
} else {
t.Errorf("#%d expected error", i)
}
continue
}
if err == nil && x.RatString() != test.out {
t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
}
}
}
var floatStringTests = []struct {
in string
prec int
out string
}{
{"0", 0, "0"},
{"0", 4, "0.0000"},
{"1", 0, "1"},
{"1", 2, "1.00"},
{"-1", 0, "-1"},
{".25", 2, "0.25"},
{".25", 1, "0.3"},
{".25", 3, "0.250"},
{"-1/3", 3, "-0.333"},
{"-2/3", 4, "-0.6667"},
{"0.96", 1, "1.0"},
{"0.999", 2, "1.00"},
{"0.9", 0, "1"},
{".25", -1, "0"},
{".55", -1, "1"},
}
func TestFloatString(t *testing.T) {
for i, test := range floatStringTests {
x, _ := new(Rat).SetString(test.in)
if x.FloatString(test.prec) != test.out {
t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out)
}
}
}
func TestRatSign(t *testing.T) {
zero := NewRat(0, 1)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
s := x.Sign()
e := x.Cmp(zero)
if s != e {
t.Errorf("got %d; want %d for z = %v", s, e, &x)
}
}
}
var ratCmpTests = []struct {
rat1, rat2 string
out int
}{
{"0", "0/1", 0},
{"1/1", "1", 0},
{"-1", "-2/2", 0},
{"1", "0", 1},
{"0/1", "1/1", -1},
{"-5/1434770811533343057144", "-5/1434770811533343057145", -1},
{"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1},
{"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1},
{"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0},
}
func TestRatCmp(t *testing.T) {
for i, test := range ratCmpTests {
x, _ := new(Rat).SetString(test.rat1)
y, _ := new(Rat).SetString(test.rat2)
out := x.Cmp(y)
if out != test.out {
t.Errorf("#%d got out = %v; want %v", i, out, test.out)
}
}
}
func TestIsInt(t *testing.T) {
one := NewInt(1)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
i := x.IsInt()
e := x.Denom().Cmp(one) == 0
if i != e {
t.Errorf("got IsInt(%v) == %v; want %v", x, i, e)
}
}
}
func TestRatAbs(t *testing.T) {
zero := new(Rat)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
e := new(Rat).Set(x)
if e.Cmp(zero) < 0 {
e.Sub(zero, e)
}
z := new(Rat).Abs(x)
if z.Cmp(e) != 0 {
t.Errorf("got Abs(%v) = %v; want %v", x, z, e)
}
}
}
func TestRatNeg(t *testing.T) {
zero := new(Rat)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
e := new(Rat).Sub(zero, x)
z := new(Rat).Neg(x)
if z.Cmp(e) != 0 {
t.Errorf("got Neg(%v) = %v; want %v", x, z, e)
}
}
}
func TestRatInv(t *testing.T) {
zero := new(Rat)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
if x.Cmp(zero) == 0 {
continue // avoid division by zero
}
e := new(Rat).SetFrac(x.Denom(), x.Num())
z := new(Rat).Inv(x)
if z.Cmp(e) != 0 {
t.Errorf("got Inv(%v) = %v; want %v", x, z, e)
}
}
}
type ratBinFun func(z, x, y *Rat) *Rat
type ratBinArg struct {
x, y, z string
}
func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) {
x, _ := new(Rat).SetString(a.x)
y, _ := new(Rat).SetString(a.y)
z, _ := new(Rat).SetString(a.z)
out := f(new(Rat), x, y)
if out.Cmp(z) != 0 {
t.Errorf("%s #%d got %s want %s", name, i, out, z)
}
}
var ratBinTests = []struct {
x, y string
sum, prod string
}{
{"0", "0", "0", "0"},
{"0", "1", "1", "0"},
{"-1", "0", "-1", "0"},
{"-1", "1", "0", "-1"},
{"1", "1", "2", "1"},
{"1/2", "1/2", "1", "1/4"},
{"1/4", "1/3", "7/12", "1/12"},
{"2/5", "-14/3", "-64/15", "-28/15"},
{"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"},
{"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"},
{"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"},
{"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"},
{"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"},
{"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"},
{"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"},
{"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"},
{"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"},
{"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"},
}
func TestRatBin(t *testing.T) {
for i, test := range ratBinTests {
arg := ratBinArg{test.x, test.y, test.sum}
testRatBin(t, i, "Add", (*Rat).Add, arg)
arg = ratBinArg{test.y, test.x, test.sum}
testRatBin(t, i, "Add symmetric", (*Rat).Add, arg)
arg = ratBinArg{test.sum, test.x, test.y}
testRatBin(t, i, "Sub", (*Rat).Sub, arg)
arg = ratBinArg{test.sum, test.y, test.x}
testRatBin(t, i, "Sub symmetric", (*Rat).Sub, arg)
arg = ratBinArg{test.x, test.y, test.prod}
testRatBin(t, i, "Mul", (*Rat).Mul, arg)
arg = ratBinArg{test.y, test.x, test.prod}
testRatBin(t, i, "Mul symmetric", (*Rat).Mul, arg)
if test.x != "0" {
arg = ratBinArg{test.prod, test.x, test.y}
testRatBin(t, i, "Quo", (*Rat).Quo, arg)
}
if test.y != "0" {
arg = ratBinArg{test.prod, test.y, test.x}
testRatBin(t, i, "Quo symmetric", (*Rat).Quo, arg)
}
}
}
func TestIssue820(t *testing.T) {
x := NewRat(3, 1)
y := NewRat(2, 1)
z := y.Quo(x, y)
q := NewRat(3, 2)
if z.Cmp(q) != 0 {
t.Errorf("got %s want %s", z, q)
}
y = NewRat(3, 1)
x = NewRat(2, 1)
z = y.Quo(x, y)
q = NewRat(2, 3)
if z.Cmp(q) != 0 {
t.Errorf("got %s want %s", z, q)
}
x = NewRat(3, 1)
z = x.Quo(x, x)
q = NewRat(3, 3)
if z.Cmp(q) != 0 {
t.Errorf("got %s want %s", z, q)
}
}
var setFrac64Tests = []struct {
a, b int64
out string
}{
{0, 1, "0"},
{0, -1, "0"},
{1, 1, "1"},
{-1, 1, "-1"},
{1, -1, "-1"},
{-1, -1, "1"},
{-9223372036854775808, -9223372036854775808, "1"},
}
func TestRatSetFrac64Rat(t *testing.T) {
for i, test := range setFrac64Tests {
x := new(Rat).SetFrac64(test.a, test.b)
if x.RatString() != test.out {
t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
}
}
}
func TestRatGobEncoding(t *testing.T) {
var medium bytes.Buffer
enc := gob.NewEncoder(&medium)
dec := gob.NewDecoder(&medium)
for _, test := range encodingTests {
medium.Reset() // empty buffer for each test case (in case of failures)
var tx Rat
tx.SetString(test + ".14159265")
if err := enc.Encode(&tx); err != nil {
t.Errorf("encoding of %s failed: %s", &tx, err)
}
var rx Rat
if err := dec.Decode(&rx); err != nil {
t.Errorf("decoding of %s failed: %s", &tx, err)
}
if rx.Cmp(&tx) != 0 {
t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
// Sending a nil Rat pointer (inside a slice) on a round trip through gob should yield a zero.
// TODO: top-level nils.
func TestGobEncodingNilRatInSlice(t *testing.T) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
dec := gob.NewDecoder(buf)
var in = make([]*Rat, 1)
err := enc.Encode(&in)
if err != nil {
t.Errorf("gob encode failed: %q", err)
}
var out []*Rat
err = dec.Decode(&out)
if err != nil {
t.Fatalf("gob decode failed: %q", err)
}
if len(out) != 1 {
t.Fatalf("wrong len; want 1 got %d", len(out))
}
var zero Rat
if out[0].Cmp(&zero) != 0 {
t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out)
}
}
var ratNums = []string{
"-141592653589793238462643383279502884197169399375105820974944592307816406286",
"-1415926535897932384626433832795028841971",
"-141592653589793",
"-1",
"0",
"1",
"141592653589793",
"1415926535897932384626433832795028841971",
"141592653589793238462643383279502884197169399375105820974944592307816406286",
}
var ratDenoms = []string{
"1",
"718281828459045",
"7182818284590452353602874713526624977572",
"718281828459045235360287471352662497757247093699959574966967627724076630353",
}
func TestRatJSONEncoding(t *testing.T) {
for _, num := range ratNums {
for _, denom := range ratDenoms {
var tx Rat
tx.SetString(num + "/" + denom)
b, err := json.Marshal(&tx)
if err != nil {
t.Errorf("marshaling of %s failed: %s", &tx, err)
continue
}
var rx Rat
if err := json.Unmarshal(b, &rx); err != nil {
t.Errorf("unmarshaling of %s failed: %s", &tx, err)
continue
}
if rx.Cmp(&tx) != 0 {
t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
}
func TestRatXMLEncoding(t *testing.T) {
for _, num := range ratNums {
for _, denom := range ratDenoms {
var tx Rat
tx.SetString(num + "/" + denom)
b, err := xml.Marshal(&tx)
if err != nil {
t.Errorf("marshaling of %s failed: %s", &tx, err)
continue
}
var rx Rat
if err := xml.Unmarshal(b, &rx); err != nil {
t.Errorf("unmarshaling of %s failed: %s", &tx, err)
continue
}
if rx.Cmp(&tx) != 0 {
t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
}
func TestIssue2379(t *testing.T) {
// 1) no aliasing
q := NewRat(3, 2)
x := new(Rat)
x.SetFrac(NewInt(3), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("1) got %s want %s", x, q)
}
// 2) aliasing of numerator
x = NewRat(2, 3)
x.SetFrac(NewInt(3), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("2) got %s want %s", x, q)
}
// 3) aliasing of denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("3) got %s want %s", x, q)
}
// 4) aliasing of numerator and denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("4) got %s want %s", x, q)
}
// 5) numerator and denominator are the same
q = NewRat(1, 1)
x = new(Rat)
n := NewInt(7)
x.SetFrac(n, n)
if x.Cmp(q) != 0 {
t.Errorf("5) got %s want %s", x, q)
}
}
func TestIssue3521(t *testing.T) {
a := new(Int)
b := new(Int)
a.SetString("64375784358435883458348587", 0)
b.SetString("4789759874531", 0)
// 0) a raw zero value has 1 as denominator
zero := new(Rat)
one := NewInt(1)
if zero.Denom().Cmp(one) != 0 {
t.Errorf("0) got %s want %s", zero.Denom(), one)
}
// 1a) a zero value remains zero independent of denominator
x := new(Rat)
x.Denom().Set(new(Int).Neg(b))
if x.Cmp(zero) != 0 {
t.Errorf("1a) got %s want %s", x, zero)
}
// 1b) a zero value may have a denominator != 0 and != 1
x.Num().Set(a)
qab := new(Rat).SetFrac(a, b)
if x.Cmp(qab) != 0 {
t.Errorf("1b) got %s want %s", x, qab)
}
// 2a) an integral value becomes a fraction depending on denominator
x.SetFrac64(10, 2)
x.Denom().SetInt64(3)
q53 := NewRat(5, 3)
if x.Cmp(q53) != 0 {
t.Errorf("2a) got %s want %s", x, q53)
}
// 2b) an integral value becomes a fraction depending on denominator
x = NewRat(10, 2)
x.Denom().SetInt64(3)
if x.Cmp(q53) != 0 {
t.Errorf("2b) got %s want %s", x, q53)
}
// 3) changing the numerator/denominator of a Rat changes the Rat
x.SetFrac(a, b)
a = x.Num()
b = x.Denom()
a.SetInt64(5)
b.SetInt64(3)
if x.Cmp(q53) != 0 {
t.Errorf("3) got %s want %s", x, q53)
}
}
// Test inputs to Rat.SetString. The prefix "long:" causes the test
// to be skipped in --test.short mode. (The threshold is about 500us.)
var float64inputs = []string{
// Constants plundered from strconv/testfp.txt.
// Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
"5e+125",
"69e+267",
"999e-026",
"7861e-034",
"75569e-254",
"928609e-261",
"9210917e+080",
"84863171e+114",
"653777767e+273",
"5232604057e-298",
"27235667517e-109",
"653532977297e-123",
"3142213164987e-294",
"46202199371337e-072",
"231010996856685e-073",
"9324754620109615e+212",
"78459735791271921e+049",
"272104041512242479e+200",
"6802601037806061975e+198",
"20505426358836677347e-221",
"836168422905420598437e-234",
"4891559871276714924261e+222",
// Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP
"9e-265",
"85e-037",
"623e+100",
"3571e+263",
"81661e+153",
"920657e-023",
"4603285e-024",
"87575437e-309",
"245540327e+122",
"6138508175e+120",
"83356057653e+193",
"619534293513e+124",
"2335141086879e+218",
"36167929443327e-159",
"609610927149051e-255",
"3743626360493413e-165",
"94080055902682397e-242",
"899810892172646163e+283",
"7120190517612959703e+120",
"25188282901709339043e-252",
"308984926168550152811e-052",
"6372891218502368041059e+064",
// Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP
"5e-20",
"67e+14",
"985e+15",
"7693e-42",
"55895e-16",
"996622e-44",
"7038531e-32",
"60419369e-46",
"702990899e-20",
"6930161142e-48",
"25933168707e+13",
"596428896559e+20",
// Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP
"3e-23",
"57e+18",
"789e-35",
"2539e-18",
"76173e+28",
"887745e-11",
"5382571e-37",
"82381273e-35",
"750486563e-38",
"3752432815e-39",
"75224575729e-45",
"459926601011e+15",
// Constants plundered from strconv/atof_test.go.
"0",
"1",
"+1",
"1e23",
"1E23",
"100000000000000000000000",
"1e-100",
"123456700",
"99999999999999974834176",
"100000000000000000000001",
"100000000000000008388608",
"100000000000000016777215",
"100000000000000016777216",
"-1",
"-0.1",
"-0", // NB: exception made for this input
"1e-20",
"625e-3",
// largest float64
"1.7976931348623157e308",
"-1.7976931348623157e308",
// next float64 - too large
"1.7976931348623159e308",
"-1.7976931348623159e308",
// the border is ...158079
// borderline - okay
"1.7976931348623158e308",
"-1.7976931348623158e308",
// borderline - too large
"1.797693134862315808e308",
"-1.797693134862315808e308",
// a little too large
"1e308",
"2e308",
"1e309",
// way too large
"1e310",
"-1e310",
"1e400",
"-1e400",
"long:1e400000",
"long:-1e400000",
// denormalized
"1e-305",
"1e-306",
"1e-307",
"1e-308",
"1e-309",
"1e-310",
"1e-322",
// smallest denormal
"5e-324",
"4e-324",
"3e-324",
// too small
"2e-324",
// way too small
"1e-350",
"long:1e-400000",
// way too small, negative
"-1e-350",
"long:-1e-400000",
// try to overflow exponent
// [Disabled: too slow and memory-hungry with rationals.]
// "1e-4294967296",
// "1e+4294967296",
// "1e-18446744073709551616",
// "1e+18446744073709551616",
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
"2.2250738585072012e-308",
// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
"2.2250738585072011e-308",
// A very large number (initially wrongly parsed by the fast algorithm).
"4.630813248087435e+307",
// A different kind of very large number.
"22.222222222222222",
"long:2." + strings.Repeat("2", 4000) + "e+1",
// Exactly halfway between 1 and math.Nextafter(1, 2).
// Round to even (down).
"1.00000000000000011102230246251565404236316680908203125",
// Slightly lower; still round down.
"1.00000000000000011102230246251565404236316680908203124",
// Slightly higher; round up.
"1.00000000000000011102230246251565404236316680908203126",
// Slightly higher, but you have to read all the way to the end.
"long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1",
// Smallest denormal, 2^(-1022-52)
"4.940656458412465441765687928682213723651e-324",
// Half of smallest denormal, 2^(-1022-53)
"2.470328229206232720882843964341106861825e-324",
// A little more than the exact half of smallest denormal
// 2^-1075 + 2^-1100. (Rounds to 1p-1074.)
"2.470328302827751011111470718709768633275e-324",
// The exact halfway between smallest normal and largest denormal:
// 2^-1022 - 2^-1075. (Rounds to 2^-1022.)
"2.225073858507201136057409796709131975935e-308",
"1152921504606846975", // 1<<60 - 1
"-1152921504606846975", // -(1<<60 - 1)
"1152921504606846977", // 1<<60 + 1
"-1152921504606846977", // -(1<<60 + 1)
"1/3",
}
// isFinite reports whether f represents a finite rational value.
// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
func isFinite(f float64) bool {
return math.Abs(f) <= math.MaxFloat64
}
func TestFloat32SpecialCases(t *testing.T) {
for _, input := range float64inputs {
if strings.HasPrefix(input, "long:") {
if testing.Short() {
continue
}
input = input[len("long:"):]
}
r, ok := new(Rat).SetString(input)
if !ok {
t.Errorf("Rat.SetString(%q) failed", input)
continue
}
f, exact := r.Float32()
// 1. Check string -> Rat -> float32 conversions are
// consistent with strconv.ParseFloat.
// Skip this check if the input uses "a/b" rational syntax.
if !strings.Contains(input, "/") {
e64, _ := strconv.ParseFloat(input, 32)
e := float32(e64)
// Careful: negative Rats too small for
// float64 become -0, but Rat obviously cannot
// preserve the sign from SetString("-0").
switch {
case math.Float32bits(e) == math.Float32bits(f):
// Ok: bitwise equal.
case f == 0 && r.Num().BitLen() == 0:
// Ok: Rat(0) is equivalent to both +/- float64(0).
default:
t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
}
}
if !isFinite(float64(f)) {
continue
}
// 2. Check f is best approximation to r.
if !checkIsBestApprox32(t, f, r) {
// Append context information.
t.Errorf("(input was %q)", input)
}
// 3. Check f->R->f roundtrip is non-lossy.
checkNonLossyRoundtrip32(t, f)
// 4. Check exactness using slow algorithm.
if wasExact := new(Rat).SetFloat64(float64(f)).Cmp(r) == 0; wasExact != exact {
t.Errorf("Rat.SetString(%q).Float32().exact = %t, want %t", input, exact, wasExact)
}
}
}
func TestFloat64SpecialCases(t *testing.T) {
for _, input := range float64inputs {
if strings.HasPrefix(input, "long:") {
if testing.Short() {
continue
}
input = input[len("long:"):]
}
r, ok := new(Rat).SetString(input)
if !ok {
t.Errorf("Rat.SetString(%q) failed", input)
continue
}
f, exact := r.Float64()
// 1. Check string -> Rat -> float64 conversions are
// consistent with strconv.ParseFloat.
// Skip this check if the input uses "a/b" rational syntax.
if !strings.Contains(input, "/") {
e, _ := strconv.ParseFloat(input, 64)
// Careful: negative Rats too small for
// float64 become -0, but Rat obviously cannot
// preserve the sign from SetString("-0").
switch {
case math.Float64bits(e) == math.Float64bits(f):
// Ok: bitwise equal.
case f == 0 && r.Num().BitLen() == 0:
// Ok: Rat(0) is equivalent to both +/- float64(0).
default:
t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
}
}
if !isFinite(f) {
continue
}
// 2. Check f is best approximation to r.
if !checkIsBestApprox64(t, f, r) {
// Append context information.
t.Errorf("(input was %q)", input)
}
// 3. Check f->R->f roundtrip is non-lossy.
checkNonLossyRoundtrip64(t, f)
// 4. Check exactness using slow algorithm.
if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact {
t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
}
}
}
func TestFloat32Distribution(t *testing.T) {
// Generate a distribution of (sign, mantissa, exp) values
// broader than the float32 range, and check Rat.Float32()
// always picks the closest float32 approximation.
var add = []int64{
0,
1,
3,
5,
7,
9,
11,
}
var winc, einc = uint64(1), 1 // soak test (~1.5s on x86-64)
if testing.Short() {
winc, einc = 5, 15 // quick test (~60ms on x86-64)
}
for _, sign := range "+-" {
for _, a := range add {
for wid := uint64(0); wid < 30; wid += winc {
b := 1<<wid + a
if sign == '-' {
b = -b
}
for exp := -150; exp < 150; exp += einc {
num, den := NewInt(b), NewInt(1)
if exp > 0 {
num.Lsh(num, uint(exp))
} else {
den.Lsh(den, uint(-exp))
}
r := new(Rat).SetFrac(num, den)
f, _ := r.Float32()
if !checkIsBestApprox32(t, f, r) {
// Append context information.
t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)",
b, exp, f, f, math.Ldexp(float64(b), exp), r)
}
checkNonLossyRoundtrip32(t, f)
}
}
}
}
}
func TestFloat64Distribution(t *testing.T) {
// Generate a distribution of (sign, mantissa, exp) values
// broader than the float64 range, and check Rat.Float64()
// always picks the closest float64 approximation.
var add = []int64{
0,
1,
3,
5,
7,
9,
11,
}
var winc, einc = uint64(1), 1 // soak test (~75s on x86-64)
if testing.Short() {
winc, einc = 10, 500 // quick test (~12ms on x86-64)
}
for _, sign := range "+-" {
for _, a := range add {
for wid := uint64(0); wid < 60; wid += winc {
b := 1<<wid + a
if sign == '-' {
b = -b
}
for exp := -1100; exp < 1100; exp += einc {
num, den := NewInt(b), NewInt(1)
if exp > 0 {
num.Lsh(num, uint(exp))
} else {
den.Lsh(den, uint(-exp))
}
r := new(Rat).SetFrac(num, den)
f, _ := r.Float64()
if !checkIsBestApprox64(t, f, r) {
// Append context information.
t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)",
b, exp, f, f, math.Ldexp(float64(b), exp), r)
}
checkNonLossyRoundtrip64(t, f)
}
}
}
}
}
// TestSetFloat64NonFinite checks that SetFloat64 of a non-finite value
// returns nil.
func TestSetFloat64NonFinite(t *testing.T) {
for _, f := range []float64{math.NaN(), math.Inf(+1), math.Inf(-1)} {
var r Rat
if r2 := r.SetFloat64(f); r2 != nil {
t.Errorf("SetFloat64(%g) was %v, want nil", f, r2)
}
}
}
// checkNonLossyRoundtrip32 checks that a float->Rat->float roundtrip is
// non-lossy for finite f.
func checkNonLossyRoundtrip32(t *testing.T, f float32) {
if !isFinite(float64(f)) {
return
}
r := new(Rat).SetFloat64(float64(f))
if r == nil {
t.Errorf("Rat.SetFloat64(float64(%g) (%b)) == nil", f, f)
return
}
f2, exact := r.Float32()
if f != f2 || !exact {
t.Errorf("Rat.SetFloat64(float64(%g)).Float32() = %g (%b), %v, want %g (%b), %v; delta = %b",
f, f2, f2, exact, f, f, true, f2-f)
}
}
// checkNonLossyRoundtrip64 checks that a float->Rat->float roundtrip is
// non-lossy for finite f.
func checkNonLossyRoundtrip64(t *testing.T, f float64) {
if !isFinite(f) {
return
}
r := new(Rat).SetFloat64(f)
if r == nil {
t.Errorf("Rat.SetFloat64(%g (%b)) == nil", f, f)
return
}
f2, exact := r.Float64()
if f != f2 || !exact {
t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta = %b",
f, f2, f2, exact, f, f, true, f2-f)
}
}
// delta returns the absolute difference between r and f.
func delta(r *Rat, f float64) *Rat {
d := new(Rat).Sub(r, new(Rat).SetFloat64(f))
return d.Abs(d)
}
// checkIsBestApprox32 checks that f is the best possible float32
// approximation of r.
// Returns true on success.
func checkIsBestApprox32(t *testing.T, f float32, r *Rat) bool {
if math.Abs(float64(f)) >= math.MaxFloat32 {
// Cannot check +Inf, -Inf, nor the float next to them (MaxFloat32).
// But we have tests for these special cases.
return true
}
// r must be strictly between f0 and f1, the floats bracketing f.
f0 := math.Nextafter32(f, float32(math.Inf(-1)))
f1 := math.Nextafter32(f, float32(math.Inf(+1)))
// For f to be correct, r must be closer to f than to f0 or f1.
df := delta(r, float64(f))
df0 := delta(r, float64(f0))
df1 := delta(r, float64(f1))
if df.Cmp(df0) > 0 {
t.Errorf("Rat(%v).Float32() = %g (%b), but previous float32 %g (%b) is closer", r, f, f, f0, f0)
return false
}
if df.Cmp(df1) > 0 {
t.Errorf("Rat(%v).Float32() = %g (%b), but next float32 %g (%b) is closer", r, f, f, f1, f1)
return false
}
if df.Cmp(df0) == 0 && !isEven32(f) {
t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0)
return false
}
if df.Cmp(df1) == 0 && !isEven32(f) {
t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1)
return false
}
return true
}
// checkIsBestApprox64 checks that f is the best possible float64
// approximation of r.
// Returns true on success.
func checkIsBestApprox64(t *testing.T, f float64, r *Rat) bool {
if math.Abs(f) >= math.MaxFloat64 {
// Cannot check +Inf, -Inf, nor the float next to them (MaxFloat64).
// But we have tests for these special cases.
return true
}
// r must be strictly between f0 and f1, the floats bracketing f.
f0 := math.Nextafter(f, math.Inf(-1))
f1 := math.Nextafter(f, math.Inf(+1))
// For f to be correct, r must be closer to f than to f0 or f1.
df := delta(r, f)
df0 := delta(r, f0)
df1 := delta(r, f1)
if df.Cmp(df0) > 0 {
t.Errorf("Rat(%v).Float64() = %g (%b), but previous float64 %g (%b) is closer", r, f, f, f0, f0)
return false
}
if df.Cmp(df1) > 0 {
t.Errorf("Rat(%v).Float64() = %g (%b), but next float64 %g (%b) is closer", r, f, f, f1, f1)
return false
}
if df.Cmp(df0) == 0 && !isEven64(f) {
t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0)
return false
}
if df.Cmp(df1) == 0 && !isEven64(f) {
t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1)
return false
}
return true
}
func isEven32(f float32) bool { return math.Float32bits(f)&1 == 0 }
func isEven64(f float64) bool { return math.Float64bits(f)&1 == 0 }
func TestIsFinite(t *testing.T) {
finites := []float64{
1.0 / 3,
4891559871276714924261e+222,
math.MaxFloat64,
math.SmallestNonzeroFloat64,
-math.MaxFloat64,
-math.SmallestNonzeroFloat64,
}
for _, f := range finites {
if !isFinite(f) {
t.Errorf("!IsFinite(%g (%b))", f, f)
}
}
nonfinites := []float64{
math.NaN(),
math.Inf(-1),
math.Inf(+1),
}
for _, f := range nonfinites {
if isFinite(f) {
t.Errorf("IsFinite(%g, (%b))", f, f)
}
}
}