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.

decimal.go 2.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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. func Float64ToDecimal(f float64) (Decimal, error) {
  31. var dec Decimal
  32. if math.IsNaN(f) {
  33. return dec, errors.New("NaN")
  34. }
  35. if math.IsInf(f, 0) {
  36. return dec, errors.New("Infinity can't be converted to decimal")
  37. }
  38. dec.positive = f >= 0
  39. if !dec.positive {
  40. f = math.Abs(f)
  41. }
  42. if f > 3.402823669209385e+38 {
  43. return dec, errors.New("Float value is out of range")
  44. }
  45. dec.prec = 20
  46. var integer float64
  47. for dec.scale = 0; dec.scale <= 20; dec.scale++ {
  48. integer = f * scaletblflt64[dec.scale]
  49. _, frac := math.Modf(integer)
  50. if frac == 0 {
  51. break
  52. }
  53. }
  54. for i := 0; i < 4; i++ {
  55. mod := math.Mod(integer, 0x100000000)
  56. integer -= mod
  57. integer /= 0x100000000
  58. dec.integer[i] = uint32(mod)
  59. }
  60. return dec, nil
  61. }
  62. func init() {
  63. var acc float64 = 1
  64. for i := 0; i <= 38; i++ {
  65. scaletblflt64[i] = acc
  66. acc *= 10
  67. }
  68. }
  69. func (d Decimal) Bytes() []byte {
  70. bytes := make([]byte, 16)
  71. binary.BigEndian.PutUint32(bytes[0:4], d.integer[3])
  72. binary.BigEndian.PutUint32(bytes[4:8], d.integer[2])
  73. binary.BigEndian.PutUint32(bytes[8:12], d.integer[1])
  74. binary.BigEndian.PutUint32(bytes[12:16], d.integer[0])
  75. var x big.Int
  76. x.SetBytes(bytes)
  77. if !d.positive {
  78. x.Neg(&x)
  79. }
  80. return scaleBytes(x.String(), d.scale)
  81. }
  82. func scaleBytes(s string, scale uint8) []byte {
  83. z := make([]byte, 0, len(s)+1)
  84. if s[0] == '-' || s[0] == '+' {
  85. z = append(z, byte(s[0]))
  86. s = s[1:]
  87. }
  88. pos := len(s) - int(scale)
  89. if pos <= 0 {
  90. z = append(z, byte('0'))
  91. } else if pos > 0 {
  92. z = append(z, []byte(s[:pos])...)
  93. }
  94. if scale > 0 {
  95. z = append(z, byte('.'))
  96. for pos < 0 {
  97. z = append(z, byte('0'))
  98. pos++
  99. }
  100. z = append(z, []byte(s[pos:])...)
  101. }
  102. return z
  103. }
  104. func (d Decimal) String() string {
  105. return string(d.Bytes())
  106. }