decimal.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package mssql
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "math"
  6. "math/big"
  7. )
  8. // http://msdn.microsoft.com/en-us/library/ee780893.aspx
  9. type Decimal struct {
  10. integer [4]uint32
  11. positive bool
  12. prec uint8
  13. scale uint8
  14. }
  15. var scaletblflt64 [39]float64
  16. func (d Decimal) ToFloat64() float64 {
  17. val := float64(0)
  18. for i := 3; i >= 0; i-- {
  19. val *= 0x100000000
  20. val += float64(d.integer[i])
  21. }
  22. if !d.positive {
  23. val = -val
  24. }
  25. if d.scale != 0 {
  26. val /= scaletblflt64[d.scale]
  27. }
  28. return val
  29. }
  30. const autoScale = 100
  31. func Float64ToDecimal(f float64) (Decimal, error) {
  32. return Float64ToDecimalScale(f, autoScale)
  33. }
  34. func Float64ToDecimalScale(f float64, scale uint8) (Decimal, error) {
  35. var dec Decimal
  36. if math.IsNaN(f) {
  37. return dec, errors.New("NaN")
  38. }
  39. if math.IsInf(f, 0) {
  40. return dec, errors.New("Infinity can't be converted to decimal")
  41. }
  42. dec.positive = f >= 0
  43. if !dec.positive {
  44. f = math.Abs(f)
  45. }
  46. if f > 3.402823669209385e+38 {
  47. return dec, errors.New("Float value is out of range")
  48. }
  49. dec.prec = 20
  50. var integer float64
  51. for dec.scale = 0; dec.scale <= scale; dec.scale++ {
  52. integer = f * scaletblflt64[dec.scale]
  53. _, frac := math.Modf(integer)
  54. if frac == 0 && scale == autoScale {
  55. break
  56. }
  57. }
  58. for i := 0; i < 4; i++ {
  59. mod := math.Mod(integer, 0x100000000)
  60. integer -= mod
  61. integer /= 0x100000000
  62. dec.integer[i] = uint32(mod)
  63. }
  64. return dec, nil
  65. }
  66. func init() {
  67. var acc float64 = 1
  68. for i := 0; i <= 38; i++ {
  69. scaletblflt64[i] = acc
  70. acc *= 10
  71. }
  72. }
  73. func (d Decimal) BigInt() big.Int {
  74. bytes := make([]byte, 16)
  75. binary.BigEndian.PutUint32(bytes[0:4], d.integer[3])
  76. binary.BigEndian.PutUint32(bytes[4:8], d.integer[2])
  77. binary.BigEndian.PutUint32(bytes[8:12], d.integer[1])
  78. binary.BigEndian.PutUint32(bytes[12:16], d.integer[0])
  79. var x big.Int
  80. x.SetBytes(bytes)
  81. if !d.positive {
  82. x.Neg(&x)
  83. }
  84. return x
  85. }
  86. func (d Decimal) Bytes() []byte {
  87. x := d.BigInt()
  88. return scaleBytes(x.String(), d.scale)
  89. }
  90. func (d Decimal) UnscaledBytes() []byte {
  91. x := d.BigInt()
  92. return x.Bytes()
  93. }
  94. func scaleBytes(s string, scale uint8) []byte {
  95. z := make([]byte, 0, len(s)+1)
  96. if s[0] == '-' || s[0] == '+' {
  97. z = append(z, byte(s[0]))
  98. s = s[1:]
  99. }
  100. pos := len(s) - int(scale)
  101. if pos <= 0 {
  102. z = append(z, byte('0'))
  103. } else if pos > 0 {
  104. z = append(z, []byte(s[:pos])...)
  105. }
  106. if scale > 0 {
  107. z = append(z, byte('.'))
  108. for pos < 0 {
  109. z = append(z, byte('0'))
  110. pos++
  111. }
  112. z = append(z, []byte(s[pos:])...)
  113. }
  114. return z
  115. }
  116. func (d Decimal) String() string {
  117. return string(d.Bytes())
  118. }