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 }