本站源代码
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.

253 lines
5.5KB

  1. package decimal
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "fmt"
  6. "math"
  7. "math/big"
  8. "strings"
  9. )
  10. // Decimal represents decimal type in the Microsoft Open Specifications: http://msdn.microsoft.com/en-us/library/ee780893.aspx
  11. type Decimal struct {
  12. integer [4]uint32 // Little-endian
  13. positive bool
  14. prec uint8
  15. scale uint8
  16. }
  17. var (
  18. scaletblflt64 [39]float64
  19. int10 big.Int
  20. int1e5 big.Int
  21. )
  22. func init() {
  23. var acc float64 = 1
  24. for i := 0; i <= 38; i++ {
  25. scaletblflt64[i] = acc
  26. acc *= 10
  27. }
  28. int10.SetInt64(10)
  29. int1e5.SetInt64(1e5)
  30. }
  31. const autoScale = 100
  32. // SetInteger sets the ind'th element in the integer array
  33. func (d *Decimal) SetInteger(integer uint32, ind uint8) {
  34. d.integer[ind] = integer
  35. }
  36. // SetPositive sets the positive member
  37. func (d *Decimal) SetPositive(positive bool) {
  38. d.positive = positive
  39. }
  40. // SetPrec sets the prec member
  41. func (d *Decimal) SetPrec(prec uint8) {
  42. d.prec = prec
  43. }
  44. // SetScale sets the scale member
  45. func (d *Decimal) SetScale(scale uint8) {
  46. d.scale = scale
  47. }
  48. // IsPositive returns true if the Decimal is positive
  49. func (d *Decimal) IsPositive() bool {
  50. return d.positive
  51. }
  52. // ToFloat64 converts decimal to a float64
  53. func (d Decimal) ToFloat64() float64 {
  54. val := float64(0)
  55. for i := 3; i >= 0; i-- {
  56. val *= 0x100000000
  57. val += float64(d.integer[i])
  58. }
  59. if !d.positive {
  60. val = -val
  61. }
  62. if d.scale != 0 {
  63. val /= scaletblflt64[d.scale]
  64. }
  65. return val
  66. }
  67. // BigInt converts decimal to a bigint
  68. func (d Decimal) BigInt() big.Int {
  69. bytes := make([]byte, 16)
  70. binary.BigEndian.PutUint32(bytes[0:4], d.integer[3])
  71. binary.BigEndian.PutUint32(bytes[4:8], d.integer[2])
  72. binary.BigEndian.PutUint32(bytes[8:12], d.integer[1])
  73. binary.BigEndian.PutUint32(bytes[12:16], d.integer[0])
  74. var x big.Int
  75. x.SetBytes(bytes)
  76. if !d.positive {
  77. x.Neg(&x)
  78. }
  79. return x
  80. }
  81. // Bytes converts decimal to a scaled byte slice
  82. func (d Decimal) Bytes() []byte {
  83. x := d.BigInt()
  84. return ScaleBytes(x.String(), d.scale)
  85. }
  86. // UnscaledBytes converts decimal to a unscaled byte slice
  87. func (d Decimal) UnscaledBytes() []byte {
  88. x := d.BigInt()
  89. return x.Bytes()
  90. }
  91. // String converts decimal to a string
  92. func (d Decimal) String() string {
  93. return string(d.Bytes())
  94. }
  95. // Float64ToDecimal converts float64 to decimal
  96. func Float64ToDecimal(f float64) (Decimal, error) {
  97. return Float64ToDecimalScale(f, autoScale)
  98. }
  99. // Float64ToDecimalScale converts float64 to decimal; user can specify the scale
  100. func Float64ToDecimalScale(f float64, scale uint8) (Decimal, error) {
  101. var dec Decimal
  102. if math.IsNaN(f) {
  103. return dec, errors.New("NaN")
  104. }
  105. if math.IsInf(f, 0) {
  106. return dec, errors.New("Infinity can't be converted to decimal")
  107. }
  108. dec.positive = f >= 0
  109. if !dec.positive {
  110. f = math.Abs(f)
  111. }
  112. if f > 3.402823669209385e+38 {
  113. return dec, errors.New("Float value is out of range")
  114. }
  115. dec.prec = 20
  116. var integer float64
  117. for dec.scale = 0; dec.scale <= scale; dec.scale++ {
  118. integer = f * scaletblflt64[dec.scale]
  119. _, frac := math.Modf(integer)
  120. if frac == 0 && scale == autoScale {
  121. break
  122. }
  123. }
  124. for i := 0; i < 4; i++ {
  125. mod := math.Mod(integer, 0x100000000)
  126. integer -= mod
  127. integer /= 0x100000000
  128. dec.integer[i] = uint32(mod)
  129. if mod-math.Trunc(mod) >= 0.5 {
  130. dec.integer[i] = uint32(mod) + 1
  131. }
  132. }
  133. return dec, nil
  134. }
  135. // Int64ToDecimalScale converts float64 to decimal; user can specify the scale
  136. func Int64ToDecimalScale(v int64, scale uint8) Decimal {
  137. positive := v >= 0
  138. if !positive {
  139. if v == math.MinInt64 {
  140. // Special case - can't negate
  141. return Decimal{
  142. integer: [4]uint32{0, 0x80000000, 0, 0},
  143. positive: false,
  144. prec: 20,
  145. scale: 0,
  146. }
  147. }
  148. v = -v
  149. }
  150. return Decimal{
  151. integer: [4]uint32{uint32(v), uint32(v >> 32), 0, 0},
  152. positive: positive,
  153. prec: 20,
  154. scale: scale,
  155. }
  156. }
  157. // StringToDecimalScale converts string to decimal
  158. func StringToDecimalScale(v string, outScale uint8) (Decimal, error) {
  159. var r big.Int
  160. var unscaled string
  161. var inScale int
  162. point := strings.LastIndexByte(v, '.')
  163. if point == -1 {
  164. inScale = 0
  165. unscaled = v
  166. } else {
  167. inScale = len(v) - point - 1
  168. unscaled = v[:point] + v[point+1:]
  169. }
  170. if inScale > math.MaxUint8 {
  171. return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: scale too large", v)
  172. }
  173. _, ok := r.SetString(unscaled, 10)
  174. if !ok {
  175. return Decimal{}, fmt.Errorf("can't parse %q as a decimal number", v)
  176. }
  177. if inScale > int(outScale) {
  178. return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: scale %d is larger than the scale %d of the target column", v, inScale, outScale)
  179. }
  180. for inScale < int(outScale) {
  181. if int(outScale)-inScale >= 5 {
  182. r.Mul(&r, &int1e5)
  183. inScale += 5
  184. } else {
  185. r.Mul(&r, &int10)
  186. inScale++
  187. }
  188. }
  189. bytes := r.Bytes()
  190. if len(bytes) > 16 {
  191. return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: precision too large", v)
  192. }
  193. var out [4]uint32
  194. for i, b := range bytes {
  195. pos := len(bytes) - i - 1
  196. out[pos/4] += uint32(b) << uint(pos%4*8)
  197. }
  198. return Decimal{
  199. integer: out,
  200. positive: r.Sign() >= 0,
  201. prec: 20,
  202. scale: uint8(inScale),
  203. }, nil
  204. }
  205. // ScaleBytes converts a stringified decimal to a scaled byte slice
  206. func ScaleBytes(s string, scale uint8) []byte {
  207. z := make([]byte, 0, len(s)+1)
  208. if s[0] == '-' || s[0] == '+' {
  209. z = append(z, byte(s[0]))
  210. s = s[1:]
  211. }
  212. pos := len(s) - int(scale)
  213. if pos <= 0 {
  214. z = append(z, byte('0'))
  215. } else if pos > 0 {
  216. z = append(z, []byte(s[:pos])...)
  217. }
  218. if scale > 0 {
  219. z = append(z, byte('.'))
  220. for pos < 0 {
  221. z = append(z, byte('0'))
  222. pos++
  223. }
  224. z = append(z, []byte(s[pos:])...)
  225. }
  226. return z
  227. }
上海开阖软件有限公司 沪ICP备12045867号-1