diff options
Diffstat (limited to 'libgo/go/strconv/ftoa.go')
-rw-r--r-- | libgo/go/strconv/ftoa.go | 78 |
1 files changed, 51 insertions, 27 deletions
diff --git a/libgo/go/strconv/ftoa.go b/libgo/go/strconv/ftoa.go index f4434fd..8eefbee 100644 --- a/libgo/go/strconv/ftoa.go +++ b/libgo/go/strconv/ftoa.go @@ -98,29 +98,43 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { return fmtB(dst, neg, mant, exp, flt) } - // Create exact decimal representation. - // The shift is exp - flt.mantbits because mant is a 1-bit integer - // followed by a flt.mantbits fraction, and we are treating it as - // a 1+flt.mantbits-bit integer. - d := new(decimal) - d.Assign(mant) - d.Shift(exp - int(flt.mantbits)) - - // Round appropriately. // Negative precision means "only as much as needed to be exact." - shortest := false - if prec < 0 { - shortest = true - roundShortest(d, mant, exp, flt) - switch fmt { - case 'e', 'E': - prec = d.nd - 1 - case 'f': - prec = max(d.nd-d.dp, 0) - case 'g', 'G': - prec = d.nd + shortest := prec < 0 + + d := new(decimal) + if shortest { + ok := false + if optimize && bitSize == 64 { + // Try Grisu3 algorithm. + f := new(extFloat) + lower, upper := f.AssignComputeBounds(val) + ok = f.ShortestDecimal(d, &lower, &upper) + } + if !ok { + // Create exact decimal representation. + // The shift is exp - flt.mantbits because mant is a 1-bit integer + // followed by a flt.mantbits fraction, and we are treating it as + // a 1+flt.mantbits-bit integer. + d.Assign(mant) + d.Shift(exp - int(flt.mantbits)) + roundShortest(d, mant, exp, flt) + } + // Precision for shortest representation mode. + if prec < 0 { + switch fmt { + case 'e', 'E': + prec = d.nd - 1 + case 'f': + prec = max(d.nd-d.dp, 0) + case 'g', 'G': + prec = d.nd + } } } else { + // Create exact decimal representation. + d.Assign(mant) + d.Shift(exp - int(flt.mantbits)) + // Round appropriately. switch fmt { case 'e', 'E': d.Round(prec + 1) @@ -178,15 +192,26 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { return } - // TODO(rsc): Unless exp == minexp, if the number of digits in d - // is less than 17, it seems likely that it would be - // the shortest possible number already. So maybe we can - // bail out without doing the extra multiprecision math here. - // Compute upper and lower such that any decimal number // between upper and lower (possibly inclusive) // will round to the original floating point number. + // We may see at once that the number is already shortest. + // + // Suppose d is not denormal, so that 2^exp <= d < 10^dp. + // The closest shorter number is at least 10^(dp-nd) away. + // The lower/upper bounds computed below are at distance + // at most 2^(exp-mantbits). + // + // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits), + // or equivalently log2(10)*(dp-nd) > exp-mantbits. + // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32). + minexp := flt.bias + 1 // minimum possible exponent + if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) { + // The number is already shortest. + return + } + // d = mant << (exp - mantbits) // Next highest floating point number is mant+1 << exp-mantbits. // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1. @@ -200,7 +225,6 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // in which case the next lowest is mant*2-1 << exp-mantbits-1. // Either way, call it mantlo << explo-mantbits. // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1. - minexp := flt.bias + 1 // minimum possible exponent var mantlo uint64 var explo int if mant > 1<<flt.mantbits || exp == minexp { @@ -241,7 +265,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // 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 || i+1 < upper.nd) + okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd) // If it's okay to do either, then round to the nearest one. // If it's okay to do only one, do it. |