You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

146 lines
3.8 KiB

  1. package inf
  2. import (
  3. "math/big"
  4. )
  5. // Rounder represents a method for rounding the (possibly infinite decimal)
  6. // result of a division to a finite Dec. It is used by Dec.Round() and
  7. // Dec.Quo().
  8. //
  9. // See the Example for results of using each Rounder with some sample values.
  10. //
  11. type Rounder rounder
  12. // See http://speleotrove.com/decimal/damodel.html#refround for more detailed
  13. // definitions of these rounding modes.
  14. var (
  15. RoundDown Rounder // towards 0
  16. RoundUp Rounder // away from 0
  17. RoundFloor Rounder // towards -infinity
  18. RoundCeil Rounder // towards +infinity
  19. RoundHalfDown Rounder // to nearest; towards 0 if same distance
  20. RoundHalfUp Rounder // to nearest; away from 0 if same distance
  21. RoundHalfEven Rounder // to nearest; even last digit if same distance
  22. )
  23. // RoundExact is to be used in the case when rounding is not necessary.
  24. // When used with Quo or Round, it returns the result verbatim when it can be
  25. // expressed exactly with the given precision, and it returns nil otherwise.
  26. // QuoExact is a shorthand for using Quo with RoundExact.
  27. var RoundExact Rounder
  28. type rounder interface {
  29. // When UseRemainder() returns true, the Round() method is passed the
  30. // remainder of the division, expressed as the numerator and denominator of
  31. // a rational.
  32. UseRemainder() bool
  33. // Round sets the rounded value of a quotient to z, and returns z.
  34. // quo is rounded down (truncated towards zero) to the scale obtained from
  35. // the Scaler in Quo().
  36. //
  37. // When the remainder is not used, remNum and remDen are nil.
  38. // When used, the remainder is normalized between -1 and 1; that is:
  39. //
  40. // -|remDen| < remNum < |remDen|
  41. //
  42. // remDen has the same sign as y, and remNum is zero or has the same sign
  43. // as x.
  44. Round(z, quo *Dec, remNum, remDen *big.Int) *Dec
  45. }
  46. type rndr struct {
  47. useRem bool
  48. round func(z, quo *Dec, remNum, remDen *big.Int) *Dec
  49. }
  50. func (r rndr) UseRemainder() bool {
  51. return r.useRem
  52. }
  53. func (r rndr) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
  54. return r.round(z, quo, remNum, remDen)
  55. }
  56. var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)}
  57. func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *big.Int) *Dec {
  58. return func(z, q *Dec, rA, rB *big.Int) *Dec {
  59. z.Set(q)
  60. brA, brB := rA.BitLen(), rB.BitLen()
  61. if brA < brB-1 {
  62. // brA < brB-1 => |rA| < |rB/2|
  63. return z
  64. }
  65. roundUp := false
  66. srA, srB := rA.Sign(), rB.Sign()
  67. s := srA * srB
  68. if brA == brB-1 {
  69. rA2 := new(big.Int).Lsh(rA, 1)
  70. if s < 0 {
  71. rA2.Neg(rA2)
  72. }
  73. roundUp = f(rA2.Cmp(rB)*srB, z.UnscaledBig().Bit(0))
  74. } else {
  75. // brA > brB-1 => |rA| > |rB/2|
  76. roundUp = true
  77. }
  78. if roundUp {
  79. z.UnscaledBig().Add(z.UnscaledBig(), intSign[s+1])
  80. }
  81. return z
  82. }
  83. }
  84. func init() {
  85. RoundExact = rndr{true,
  86. func(z, q *Dec, rA, rB *big.Int) *Dec {
  87. if rA.Sign() != 0 {
  88. return nil
  89. }
  90. return z.Set(q)
  91. }}
  92. RoundDown = rndr{false,
  93. func(z, q *Dec, rA, rB *big.Int) *Dec {
  94. return z.Set(q)
  95. }}
  96. RoundUp = rndr{true,
  97. func(z, q *Dec, rA, rB *big.Int) *Dec {
  98. z.Set(q)
  99. if rA.Sign() != 0 {
  100. z.UnscaledBig().Add(z.UnscaledBig(), intSign[rA.Sign()*rB.Sign()+1])
  101. }
  102. return z
  103. }}
  104. RoundFloor = rndr{true,
  105. func(z, q *Dec, rA, rB *big.Int) *Dec {
  106. z.Set(q)
  107. if rA.Sign()*rB.Sign() < 0 {
  108. z.UnscaledBig().Add(z.UnscaledBig(), intSign[0])
  109. }
  110. return z
  111. }}
  112. RoundCeil = rndr{true,
  113. func(z, q *Dec, rA, rB *big.Int) *Dec {
  114. z.Set(q)
  115. if rA.Sign()*rB.Sign() > 0 {
  116. z.UnscaledBig().Add(z.UnscaledBig(), intSign[2])
  117. }
  118. return z
  119. }}
  120. RoundHalfDown = rndr{true, roundHalf(
  121. func(c int, odd uint) bool {
  122. return c > 0
  123. })}
  124. RoundHalfUp = rndr{true, roundHalf(
  125. func(c int, odd uint) bool {
  126. return c >= 0
  127. })}
  128. RoundHalfEven = rndr{true, roundHalf(
  129. func(c int, odd uint) bool {
  130. return c > 0 || c == 0 && odd == 1
  131. })}
  132. }