Commit 4fc9565f authored by Robert Griesemer's avatar Robert Griesemer

math/big: implement negative precision for Float.Append/Text

Enabled all but a handful of disabled Float formatting test cases.

Fixes #10991.

Change-Id: Id18e160e857be2743429a377000e996978015a1a
Reviewed-on: https://go-review.googlesource.com/14850Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent d02a4c1d
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
package big package big
// A decimal represents an unsigned floating-point number in decimal representation. // A decimal represents an unsigned floating-point number in decimal representation.
// The value of a non-zero decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1, // The value of a non-zero decimal d is d.mant * 10**d.exp with 0.5 <= d.mant < 1,
// with the most-significant mantissa digit at index 0. For the zero decimal, the // with the most-significant mantissa digit at index 0. For the zero decimal, the
// mantissa length and exponent are 0. // mantissa length and exponent are 0.
// The zero value for decimal represents a ready-to-use 0.0. // The zero value for decimal represents a ready-to-use 0.0.
......
...@@ -139,6 +139,8 @@ func TestFloatSetFloat64String(t *testing.T) { ...@@ -139,6 +139,8 @@ func TestFloatSetFloat64String(t *testing.T) {
} }
} }
func fdiv(a, b float64) float64 { return a / b }
const ( const (
below1e23 = 99999999999999974834176 below1e23 = 99999999999999974834176
above1e23 = 100000000000000008388608 above1e23 = 100000000000000008388608
...@@ -187,11 +189,11 @@ func TestFloat64Text(t *testing.T) { ...@@ -187,11 +189,11 @@ func TestFloat64Text(t *testing.T) {
{1, 'e', 5, "1.00000e+00"}, {1, 'e', 5, "1.00000e+00"},
{1, 'f', 5, "1.00000"}, {1, 'f', 5, "1.00000"},
{1, 'g', 5, "1"}, {1, 'g', 5, "1"},
// {1, 'g', -1, "1"}, {1, 'g', -1, "1"},
// {20, 'g', -1, "20"}, {20, 'g', -1, "20"},
// {1234567.8, 'g', -1, "1.2345678e+06"}, {1234567.8, 'g', -1, "1.2345678e+06"},
// {200000, 'g', -1, "200000"}, {200000, 'g', -1, "200000"},
// {2000000, 'g', -1, "2e+06"}, {2000000, 'g', -1, "2e+06"},
// g conversion and zero suppression // g conversion and zero suppression
{400, 'g', 2, "4e+02"}, {400, 'g', 2, "4e+02"},
...@@ -207,22 +209,22 @@ func TestFloat64Text(t *testing.T) { ...@@ -207,22 +209,22 @@ func TestFloat64Text(t *testing.T) {
{0, 'e', 5, "0.00000e+00"}, {0, 'e', 5, "0.00000e+00"},
{0, 'f', 5, "0.00000"}, {0, 'f', 5, "0.00000"},
{0, 'g', 5, "0"}, {0, 'g', 5, "0"},
// {0, 'g', -1, "0"}, {0, 'g', -1, "0"},
{-1, 'e', 5, "-1.00000e+00"}, {-1, 'e', 5, "-1.00000e+00"},
{-1, 'f', 5, "-1.00000"}, {-1, 'f', 5, "-1.00000"},
{-1, 'g', 5, "-1"}, {-1, 'g', 5, "-1"},
// {-1, 'g', -1, "-1"}, {-1, 'g', -1, "-1"},
{12, 'e', 5, "1.20000e+01"}, {12, 'e', 5, "1.20000e+01"},
{12, 'f', 5, "12.00000"}, {12, 'f', 5, "12.00000"},
{12, 'g', 5, "12"}, {12, 'g', 5, "12"},
// {12, 'g', -1, "12"}, {12, 'g', -1, "12"},
{123456700, 'e', 5, "1.23457e+08"}, {123456700, 'e', 5, "1.23457e+08"},
{123456700, 'f', 5, "123456700.00000"}, {123456700, 'f', 5, "123456700.00000"},
{123456700, 'g', 5, "1.2346e+08"}, {123456700, 'g', 5, "1.2346e+08"},
// {123456700, 'g', -1, "1.234567e+08"}, {123456700, 'g', -1, "1.234567e+08"},
{1.2345e6, 'e', 5, "1.23450e+06"}, {1.2345e6, 'e', 5, "1.23450e+06"},
{1.2345e6, 'f', 5, "1234500.00000"}, {1.2345e6, 'f', 5, "1234500.00000"},
...@@ -232,36 +234,39 @@ func TestFloat64Text(t *testing.T) { ...@@ -232,36 +234,39 @@ func TestFloat64Text(t *testing.T) {
{1e23, 'f', 17, "99999999999999991611392.00000000000000000"}, {1e23, 'f', 17, "99999999999999991611392.00000000000000000"},
{1e23, 'g', 17, "9.9999999999999992e+22"}, {1e23, 'g', 17, "9.9999999999999992e+22"},
// {1e23, 'e', -1, "1e+23"}, {1e23, 'e', -1, "1e+23"},
// {1e23, 'f', -1, "100000000000000000000000"}, {1e23, 'f', -1, "100000000000000000000000"},
// {1e23, 'g', -1, "1e+23"}, {1e23, 'g', -1, "1e+23"},
{below1e23, 'e', 17, "9.99999999999999748e+22"}, {below1e23, 'e', 17, "9.99999999999999748e+22"},
{below1e23, 'f', 17, "99999999999999974834176.00000000000000000"}, {below1e23, 'f', 17, "99999999999999974834176.00000000000000000"},
{below1e23, 'g', 17, "9.9999999999999975e+22"}, {below1e23, 'g', 17, "9.9999999999999975e+22"},
// {below1e23, 'e', -1, "9.999999999999997e+22"}, {below1e23, 'e', -1, "9.999999999999997e+22"},
// {below1e23, 'f', -1, "99999999999999970000000"}, {below1e23, 'f', -1, "99999999999999970000000"},
// {below1e23, 'g', -1, "9.999999999999997e+22"}, {below1e23, 'g', -1, "9.999999999999997e+22"},
{above1e23, 'e', 17, "1.00000000000000008e+23"}, {above1e23, 'e', 17, "1.00000000000000008e+23"},
{above1e23, 'f', 17, "100000000000000008388608.00000000000000000"}, {above1e23, 'f', 17, "100000000000000008388608.00000000000000000"},
// {above1e23, 'g', 17, "1.0000000000000001e+23"}, {above1e23, 'g', 17, "1.0000000000000001e+23"},
// {above1e23, 'e', -1, "1.0000000000000001e+23"}, {above1e23, 'e', -1, "1.0000000000000001e+23"},
// {above1e23, 'f', -1, "100000000000000010000000"}, {above1e23, 'f', -1, "100000000000000010000000"},
// {above1e23, 'g', -1, "1.0000000000000001e+23"}, {above1e23, 'g', -1, "1.0000000000000001e+23"},
// {fdiv(5e-304, 1e20), 'g', -1, "5e-324"}, // TODO(gri) track down why these don't work yet
// {fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, // {5e-304/1e20, 'g', -1, "5e-324"},
// {-5e-304/1e20, 'g', -1, "-5e-324"},
// {fdiv(5e-304, 1e20), 'g', -1, "5e-324"}, // avoid constant arithmetic
// {fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, // avoid constant arithmetic
// {32, 'g', -1, "32"}, {32, 'g', -1, "32"},
// {32, 'g', 0, "3e+01"}, {32, 'g', 0, "3e+01"},
// {100, 'x', -1, "%x"}, {100, 'x', -1, "%x"},
// {math.NaN(), 'g', -1, "NaN"}, // {math.NaN(), 'g', -1, "NaN"}, // Float doesn't support NaNs
// {-math.NaN(), 'g', -1, "NaN"}, // {-math.NaN(), 'g', -1, "NaN"}, // Float doesn't support NaNs
{math.Inf(0), 'g', -1, "+Inf"}, {math.Inf(0), 'g', -1, "+Inf"},
{math.Inf(-1), 'g', -1, "-Inf"}, {math.Inf(-1), 'g', -1, "-Inf"},
{-math.Inf(0), 'g', -1, "-Inf"}, {-math.Inf(0), 'g', -1, "-Inf"},
...@@ -279,13 +284,13 @@ func TestFloat64Text(t *testing.T) { ...@@ -279,13 +284,13 @@ func TestFloat64Text(t *testing.T) {
{1.5, 'f', 0, "2"}, {1.5, 'f', 0, "2"},
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
// {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"},
// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
// {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"},
// Issue 2625. // Issue 2625.
{383260575764816448, 'f', 0, "383260575764816448"}, {383260575764816448, 'f', 0, "383260575764816448"},
// {383260575764816448, 'g', -1, "3.8326057576481645e+17"}, {383260575764816448, 'g', -1, "3.8326057576481645e+17"},
} { } {
f := new(Float).SetFloat64(test.x) f := new(Float).SetFloat64(test.x)
got := f.Text(test.format, test.prec) got := f.Text(test.format, test.prec)
...@@ -448,9 +453,6 @@ func TestFloatFormat(t *testing.T) { ...@@ -448,9 +453,6 @@ func TestFloatFormat(t *testing.T) {
value interface{} // float32, float64, or string (== 512bit *Float) value interface{} // float32, float64, or string (== 512bit *Float)
want string want string
}{ }{
// TODO(gri) uncomment the disabled 'g'/'G' formats
// below once (*Float).Text supports prec < 0
// from fmt/fmt_test.go // from fmt/fmt_test.go
{"%+.3e", 0.0, "+0.000e+00"}, {"%+.3e", 0.0, "+0.000e+00"},
{"%+.3e", 1.0, "+1.000e+00"}, {"%+.3e", 1.0, "+1.000e+00"},
...@@ -481,9 +483,9 @@ func TestFloatFormat(t *testing.T) { ...@@ -481,9 +483,9 @@ func TestFloatFormat(t *testing.T) {
{"%f", 1234.5678e-8, "0.000012"}, {"%f", 1234.5678e-8, "0.000012"},
{"%f", -7.0, "-7.000000"}, {"%f", -7.0, "-7.000000"},
{"%f", -1e-9, "-0.000000"}, {"%f", -1e-9, "-0.000000"},
// {"%g", 1234.5678e3, "1.2345678e+06"}, {"%g", 1234.5678e3, "1.2345678e+06"},
// {"%g", float32(1234.5678e3), "1.2345678e+06"}, {"%g", float32(1234.5678e3), "1.2345678e+06"},
// {"%g", 1234.5678e-8, "1.2345678e-05"}, {"%g", 1234.5678e-8, "1.2345678e-05"},
{"%g", -7.0, "-7"}, {"%g", -7.0, "-7"},
{"%g", -1e-9, "-1e-09"}, {"%g", -1e-9, "-1e-09"},
{"%g", float32(-1e-9), "-1e-09"}, {"%g", float32(-1e-9), "-1e-09"},
...@@ -492,9 +494,9 @@ func TestFloatFormat(t *testing.T) { ...@@ -492,9 +494,9 @@ func TestFloatFormat(t *testing.T) {
{"%E", 1234.5678e-8, "1.234568E-05"}, {"%E", 1234.5678e-8, "1.234568E-05"},
{"%E", -7.0, "-7.000000E+00"}, {"%E", -7.0, "-7.000000E+00"},
{"%E", -1e-9, "-1.000000E-09"}, {"%E", -1e-9, "-1.000000E-09"},
// {"%G", 1234.5678e3, "1.2345678E+06"}, {"%G", 1234.5678e3, "1.2345678E+06"},
// {"%G", float32(1234.5678e3), "1.2345678E+06"}, {"%G", float32(1234.5678e3), "1.2345678E+06"},
// {"%G", 1234.5678e-8, "1.2345678E-05"}, {"%G", 1234.5678e-8, "1.2345678E-05"},
{"%G", -7.0, "-7"}, {"%G", -7.0, "-7"},
{"%G", -1e-9, "-1E-09"}, {"%G", -1e-9, "-1E-09"},
{"%G", float32(-1e-9), "-1E-09"}, {"%G", float32(-1e-9), "-1E-09"},
...@@ -510,9 +512,9 @@ func TestFloatFormat(t *testing.T) { ...@@ -510,9 +512,9 @@ func TestFloatFormat(t *testing.T) {
{"%-20f", 1.23456789e3, "1234.567890 "}, {"%-20f", 1.23456789e3, "1234.567890 "},
{"%20.8f", 1.23456789e3, " 1234.56789000"}, {"%20.8f", 1.23456789e3, " 1234.56789000"},
{"%20.8f", 1.23456789e-3, " 0.00123457"}, {"%20.8f", 1.23456789e-3, " 0.00123457"},
// {"%g", 1.23456789e3, "1234.56789"}, {"%g", 1.23456789e3, "1234.56789"},
// {"%g", 1.23456789e-3, "0.00123456789"}, {"%g", 1.23456789e-3, "0.00123456789"},
// {"%g", 1.23456789e20, "1.23456789e+20"}, {"%g", 1.23456789e20, "1.23456789e+20"},
{"%20e", math.Inf(1), " +Inf"}, {"%20e", math.Inf(1), " +Inf"},
{"%-20f", math.Inf(-1), "-Inf "}, {"%-20f", math.Inf(-1), "-Inf "},
......
...@@ -39,8 +39,6 @@ import ( ...@@ -39,8 +39,6 @@ import (
// the total number of digits. A negative precision selects the smallest // the total number of digits. A negative precision selects the smallest
// number of digits necessary to identify the value x uniquely. // number of digits necessary to identify the value x uniquely.
// The prec value is ignored for the 'b' or 'p' format. // The prec value is ignored for the 'b' or 'p' format.
//
// BUG(gri) Float.Text does not accept negative precisions (issue #10991).
func (x *Float) Text(format byte, prec int) string { func (x *Float) Text(format byte, prec int) string {
const extra = 10 // TODO(gri) determine a good/better value here const extra = 10 // TODO(gri) determine a good/better value here
return string(x.Append(make([]byte, 0, prec+extra), format, prec)) return string(x.Append(make([]byte, 0, prec+extra), format, prec))
...@@ -83,6 +81,7 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte { ...@@ -83,6 +81,7 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
// 1) convert Float to multiprecision decimal // 1) convert Float to multiprecision decimal
var d decimal // == 0.0 var d decimal // == 0.0
if x.form == finite { if x.form == finite {
// x != 0
d.init(x.mant, int(x.exp)-x.mant.bitLen()) d.init(x.mant, int(x.exp)-x.mant.bitLen())
} }
...@@ -90,9 +89,7 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte { ...@@ -90,9 +89,7 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
shortest := false shortest := false
if prec < 0 { if prec < 0 {
shortest = true shortest = true
panic("unimplemented") roundShortest(&d, x)
// TODO(gri) complete this
// roundShortest(&d, f.mant, int(f.exp))
// Precision for shortest representation mode. // Precision for shortest representation mode.
switch fmt { switch fmt {
case 'e', 'E': case 'e', 'E':
...@@ -158,6 +155,86 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte { ...@@ -158,6 +155,86 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
return append(buf, '%', fmt) return append(buf, '%', fmt)
} }
func roundShortest(d *decimal, x *Float) {
// if the mantissa is zero, the number is zero - stop now
if len(d.mant) == 0 {
return
}
// Approach: All numbers in the interval [x - 1/2ulp, x + 1/2ulp]
// (possibly exclusive) round to x for the given precision of x.
// Compute the lower and upper bound in decimal form and find the
// the shortest decimal number d such that lower <= d <= upper.
// TODO(gri) strconv/ftoa.do describes a shortcut in some cases.
// See if we can use it (in adjusted form) here as well.
// 1) Compute normalized mantissa mant and exponent exp for x such
// that the lsb of mant corresponds to 1/2 ulp for the precision of
// x (i.e., for mant we want x.prec + 1 bits).
mant := nat(nil).set(x.mant)
exp := int(x.exp) - mant.bitLen()
s := mant.bitLen() - int(x.prec+1)
switch {
case s < 0:
mant = mant.shl(mant, uint(-s))
case s > 0:
mant = mant.shr(mant, uint(+s))
}
exp += s
// x = mant * 2**exp with lsb(mant) == 1/2 ulp of x.prec
// 2) Compute lower bound by subtracting 1/2 ulp.
var lower decimal
var tmp nat
lower.init(tmp.sub(mant, natOne), exp)
// 3) Compute upper bound by adding 1/2 ulp.
var upper decimal
upper.init(tmp.add(mant, natOne), exp)
// The upper and lower bounds are possible outputs only if
// the original mantissa is even, so that ToNearestEven rounding
// would round to the original mantissa and not the neighbors.
inclusive := mant[0]&2 == 0 // test bit 1 since original mantissa was shifted by 1
// Now we can figure out the minimum number of digits required.
// Walk along until d has distinguished itself from upper and lower.
for i, m := range d.mant {
l := byte('0') // lower digit
if i < len(lower.mant) {
l = lower.mant[i]
}
u := byte('0') // upper digit
if i < len(upper.mant) {
u = upper.mant[i]
}
// Okay to round down (truncate) if lower has a different digit
// or if lower is inclusive and is exactly the result of rounding
// down (i.e., and we have reached the final digit of lower).
okdown := l != m || inclusive && i+1 == len(lower.mant)
// Okay to round up if upper has a different digit and either upper
// is inclusive or upper is bigger than the result of rounding up.
okup := m != u && (inclusive || m+1 < u || i+1 < len(upper.mant))
// If it's okay to do either, then round to the nearest one.
// If it's okay to do only one, do it.
switch {
case okdown && okup:
d.round(i + 1)
return
case okdown:
d.roundDown(i + 1)
return
case okup:
d.roundUp(i + 1)
return
}
}
}
// %e: d.ddddde±dd // %e: d.ddddde±dd
func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte { func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte {
// first digit // first digit
...@@ -336,8 +413,7 @@ func (x *Float) Format(s fmt.State, format rune) { ...@@ -336,8 +413,7 @@ func (x *Float) Format(s fmt.State, format rune) {
fallthrough fallthrough
case 'g', 'G': case 'g', 'G':
if !hasPrec { if !hasPrec {
// TODO(gri) uncomment once (*Float).Text handles prec < 0 prec = -1 // default precision for 'g', 'G'
// prec = -1 // default precision for 'g', 'G'
} }
default: default:
fmt.Fprintf(s, "%%!%c(*big.Float=%s)", format, x.String()) fmt.Fprintf(s, "%%!%c(*big.Float=%s)", format, x.String())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment