Commit 7c86263b authored by Robert Griesemer's avatar Robert Griesemer

math/big: much simplified and faster Float rounding

Change-Id: Iab0add7aee51a8c72a81f51d980d22d2fd612f5c
Reviewed-on: https://go-review.googlesource.com/20817Reviewed-by: 's avatarAlan Donovan <adonovan@google.com>
parent e61db311
...@@ -392,15 +392,13 @@ func (z *Float) round(sbit uint) { ...@@ -392,15 +392,13 @@ func (z *Float) round(sbit uint) {
// m > 0 implies z.prec > 0 (checked by validate) // m > 0 implies z.prec > 0 (checked by validate)
m := uint32(len(z.mant)) // present mantissa length in words m := uint32(len(z.mant)) // present mantissa length in words
bits := m * _W // present mantissa bits bits := m * _W // present mantissa bits; bits > 0
if bits <= z.prec { if bits <= z.prec {
// mantissa fits => nothing to do // mantissa fits => nothing to do
return return
} }
// bits > z.prec // bits > z.prec
n := (z.prec + (_W - 1)) / _W // mantissa length in words for desired precision
// Rounding is based on two bits: the rounding bit (rbit) and the // Rounding is based on two bits: the rounding bit (rbit) and the
// sticky bit (sbit). The rbit is the bit immediately before the // sticky bit (sbit). The rbit is the bit immediately before the
// z.prec leading mantissa bits (the "0.5"). The sbit is set if any // z.prec leading mantissa bits (the "0.5"). The sbit is set if any
...@@ -415,111 +413,77 @@ func (z *Float) round(sbit uint) { ...@@ -415,111 +413,77 @@ func (z *Float) round(sbit uint) {
// bits > z.prec: mantissa too large => round // bits > z.prec: mantissa too large => round
r := uint(bits - z.prec - 1) // rounding bit position; r >= 0 r := uint(bits - z.prec - 1) // rounding bit position; r >= 0
rbit := z.mant.bit(r) // rounding bit rbit := z.mant.bit(r) & 1 // rounding bit; be safe and ensure it's a single bit
if sbit == 0 { if sbit == 0 {
// TODO(gri) if rbit != 0 we don't need to compute sbit for some rounding modes (optimization)
sbit = z.mant.sticky(r) sbit = z.mant.sticky(r)
} }
if debugFloat && sbit&^1 != 0 { sbit &= 1 // be safe and ensure it's a single bit
panic(fmt.Sprintf("invalid sbit %#x", sbit))
}
// convert ToXInf rounding modes
mode := z.mode
switch mode {
case ToNegativeInf:
mode = ToZero
if z.neg {
mode = AwayFromZero
}
case ToPositiveInf:
mode = AwayFromZero
if z.neg {
mode = ToZero
}
}
// cut off extra words // cut off extra words
n := (z.prec + (_W - 1)) / _W // mantissa length in words for desired precision
if m > n { if m > n {
copy(z.mant, z.mant[m-n:]) // move n last words to front copy(z.mant, z.mant[m-n:]) // move n last words to front
z.mant = z.mant[:n] z.mant = z.mant[:n]
} }
// determine number of trailing zero bits t // determine number of trailing zero bits (ntz) and compute lsb mask of mantissa's least-significant word
t := n*_W - z.prec // 0 <= t < _W ntz := n*_W - z.prec // 0 <= ntz < _W
lsb := Word(1) << t lsb := Word(1) << ntz
// make rounding decision // round if result is inexact
// TODO(gri) This can be simplified (see Bits.round in bits_test.go). if rbit|sbit != 0 {
switch mode { // Make rounding decision: The result mantissa is truncated ("rounded down")
case ToZero: // by default. Decide if we need to increment, or "round up", the (unsigned)
// nothing to do // mantissa.
case ToNearestEven, ToNearestAway: inc := false
if rbit == 0 { switch z.mode {
// rounding bits == 0b0x case ToNegativeInf:
mode = ToZero inc = z.neg
} else if sbit == 1 { case ToZero:
// rounding bits == 0b11 // nothing to do
mode = AwayFromZero case ToNearestEven:
} inc = rbit != 0 && (sbit != 0 || z.mant[0]&lsb != 0)
case AwayFromZero: case ToNearestAway:
if rbit|sbit == 0 { inc = rbit != 0
mode = ToZero case AwayFromZero:
} inc = true
default: case ToPositiveInf:
// ToXInf modes have been converted to ToZero or AwayFromZero inc = !z.neg
panic("unreachable") default:
} panic("unreachable")
// round and determine accuracy
switch mode {
case ToZero:
if rbit|sbit != 0 {
z.acc = Below
} }
case ToNearestEven, ToNearestAway: // A positive result (!z.neg) is Above the exact result if we increment,
if debugFloat && rbit != 1 { // and it's Below if we truncate (Exact results require no rounding).
panic("internal error in rounding") // For a negative result (z.neg) it is exactly the opposite.
} z.acc = makeAcc(inc != z.neg)
if mode == ToNearestEven && sbit == 0 && z.mant[0]&lsb == 0 {
z.acc = Below if inc {
break // add 1 to mantissa
} if addVW(z.mant, z.mant, lsb) != 0 {
// mode == ToNearestAway || sbit == 1 || z.mant[0]&lsb != 0 // mantissa overflow => adjust exponent
fallthrough if z.exp >= MaxExp {
// exponent overflow
case AwayFromZero: z.form = inf
// add 1 to mantissa return
if addVW(z.mant, z.mant, lsb) != 0 { }
// overflow => shift mantissa right by 1 and add msb
shrVU(z.mant, z.mant, 1)
z.mant[n-1] |= 1 << (_W - 1)
// adjust exponent
if z.exp < MaxExp {
z.exp++ z.exp++
} else { // adjust mantissa: divide by 2 to compensate for exponent adjustment
// exponent overflow shrVU(z.mant, z.mant, 1)
z.acc = makeAcc(!z.neg) // set msb == carry == 1 from the mantissa overflow above
z.form = inf const msb = 1 << (_W - 1)
return z.mant[n-1] |= msb
} }
} }
z.acc = Above
} }
// zero out trailing bits in least-significant word // zero out trailing bits in least-significant word
z.mant[0] &^= lsb - 1 z.mant[0] &^= lsb - 1
// update accuracy
if z.acc != Exact && z.neg {
z.acc = -z.acc
}
if debugFloat { if debugFloat {
z.validate() z.validate()
} }
return
} }
func (z *Float) setBits64(neg bool, x uint64) *Float { func (z *Float) setBits64(neg bool, x uint64) *Float {
......
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