github.com/godaddy-x/freego@v1.0.156/utils/decimal/rounding.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Multiprecision decimal numbers.
     6  // For floating-point formatting only; not general purpose.
     7  // Only operations are assign and (binary) left/right shift.
     8  // Can do binary floating point in multiprecision decimal precisely
     9  // because 2 divides 10; cannot do decimal floating point
    10  // in multiprecision binary precisely.
    11  package decimal
    12  
    13  type floatInfo struct {
    14  	mantbits uint
    15  	expbits  uint
    16  	bias     int
    17  }
    18  
    19  var float32info = floatInfo{23, 8, -127}
    20  var float64info = floatInfo{52, 11, -1023}
    21  
    22  // roundShortest rounds d (= mant * 2^exp) to the shortest number of digits
    23  // that will let the original floating point value be precisely reconstructed.
    24  func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
    25  	// If mantissa is zero, the number is zero; stop now.
    26  	if mant == 0 {
    27  		d.nd = 0
    28  		return
    29  	}
    30  
    31  	// Compute upper and lower such that any decimal number
    32  	// between upper and lower (possibly inclusive)
    33  	// will round to the original floating point number.
    34  
    35  	// We may see at once that the number is already shortest.
    36  	//
    37  	// Suppose d is not denormal, so that 2^exp <= d < 10^dp.
    38  	// The closest shorter number is at least 10^(dp-nd) away.
    39  	// The lower/upper bounds computed below are at distance
    40  	// at most 2^(exp-mantbits).
    41  	//
    42  	// So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits),
    43  	// or equivalently log2(10)*(dp-nd) > exp-mantbits.
    44  	// It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32).
    45  	minexp := flt.bias + 1 // minimum possible exponent
    46  	if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) {
    47  		// The number is already shortest.
    48  		return
    49  	}
    50  
    51  	// d = mant << (exp - mantbits)
    52  	// Next highest floating point number is mant+1 << exp-mantbits.
    53  	// Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
    54  	upper := new(decimal)
    55  	upper.Assign(mant*2 + 1)
    56  	upper.Shift(exp - int(flt.mantbits) - 1)
    57  
    58  	// d = mant << (exp - mantbits)
    59  	// Next lowest floating point number is mant-1 << exp-mantbits,
    60  	// unless mant-1 drops the significant bit and exp is not the minimum exp,
    61  	// in which case the next lowest is mant*2-1 << exp-mantbits-1.
    62  	// Either way, call it mantlo << explo-mantbits.
    63  	// Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
    64  	var mantlo uint64
    65  	var explo int
    66  	if mant > 1<<flt.mantbits || exp == minexp {
    67  		mantlo = mant - 1
    68  		explo = exp
    69  	} else {
    70  		mantlo = mant*2 - 1
    71  		explo = exp - 1
    72  	}
    73  	lower := new(decimal)
    74  	lower.Assign(mantlo*2 + 1)
    75  	lower.Shift(explo - int(flt.mantbits) - 1)
    76  
    77  	// The upper and lower bounds are possible outputs only if
    78  	// the original mantissa is even, so that IEEE round-to-even
    79  	// would round to the original mantissa and not the neighbors.
    80  	inclusive := mant%2 == 0
    81  
    82  	// Now we can figure out the minimum number of digits required.
    83  	// Walk along until d has distinguished itself from upper and lower.
    84  	for i := 0; i < d.nd; i++ {
    85  		l := byte('0') // lower digit
    86  		if i < lower.nd {
    87  			l = lower.d[i]
    88  		}
    89  		m := d.d[i]    // middle digit
    90  		u := byte('0') // upper digit
    91  		if i < upper.nd {
    92  			u = upper.d[i]
    93  		}
    94  
    95  		// Okay to round down (truncate) if lower has a different digit
    96  		// or if lower is inclusive and is exactly the result of rounding
    97  		// down (i.e., and we have reached the final digit of lower).
    98  		okdown := l != m || inclusive && i+1 == lower.nd
    99  
   100  		// Okay to round up if upper has a different digit and either upper
   101  		// is inclusive or upper is bigger than the result of rounding up.
   102  		okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
   103  
   104  		// If it's okay to do either, then round to the nearest one.
   105  		// If it's okay to do only one, do it.
   106  		switch {
   107  		case okdown && okup:
   108  			d.Round(i + 1)
   109  			return
   110  		case okdown:
   111  			d.RoundDown(i + 1)
   112  			return
   113  		case okup:
   114  			d.RoundUp(i + 1)
   115  			return
   116  		}
   117  	}
   118  }