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.

127 lines
2.0 KiB

  1. package utils
  2. import (
  3. "errors"
  4. "math/big"
  5. )
  6. var (
  7. // ErrRoundingLoss is used when converted big.Int to Float16 causes rounding loss
  8. ErrRoundingLoss = errors.New("input value causes rounding loss")
  9. )
  10. // Float16 represents a float in a 16 bit format
  11. type Float16 uint16
  12. // BigInt converts the Float16 to a big.Int integer
  13. func (fl16 *Float16) BigInt() *big.Int {
  14. fl := int64(*fl16)
  15. m := big.NewInt(fl & 0x3FF)
  16. e := big.NewInt(fl >> 11)
  17. e5 := (fl >> 10) & 0x01
  18. exp := big.NewInt(0).Exp(big.NewInt(10), e, nil)
  19. res := m.Mul(m, exp)
  20. if e5 != 0 && e.Cmp(big.NewInt(0)) != 0 {
  21. res.Add(res, exp.Div(exp, big.NewInt(2)))
  22. }
  23. return res
  24. }
  25. // floorFix2Float converts a fix to a float, always rounding down
  26. func floorFix2Float(_f *big.Int) Float16 {
  27. zero := big.NewInt(0)
  28. ten := big.NewInt(10)
  29. e := int64(0)
  30. m := big.NewInt(0)
  31. m.Set(_f)
  32. if m.Cmp(zero) == 0 {
  33. return 0
  34. }
  35. s := big.NewInt(0).Rsh(m, 10)
  36. for s.Cmp(zero) != 0 {
  37. m.Div(m, ten)
  38. s.Rsh(m, 10)
  39. e++
  40. }
  41. return Float16(m.Int64() | e<<11)
  42. }
  43. // NewFloat16 encodes a big.Int integer as a Float16, returning error in case
  44. // of loss during the encoding.
  45. func NewFloat16(f *big.Int) (Float16, error) {
  46. fl1 := floorFix2Float(f)
  47. fi1 := fl1.BigInt()
  48. fl2 := fl1 | 0x400
  49. fi2 := fl2.BigInt()
  50. m3 := (fl1 & 0x3FF) + 1
  51. e3 := fl1 >> 11
  52. if m3&0x400 == 0 {
  53. m3 = 0x66
  54. e3++
  55. }
  56. fl3 := m3 + e3<<11
  57. fi3 := fl3.BigInt()
  58. res := fl1
  59. d := big.NewInt(0).Abs(fi1.Sub(fi1, f))
  60. d2 := big.NewInt(0).Abs(fi2.Sub(fi2, f))
  61. if d.Cmp(d2) == 1 {
  62. res = fl2
  63. d = d2
  64. }
  65. d3 := big.NewInt(0).Abs(fi3.Sub(fi3, f))
  66. if d.Cmp(d3) == 1 {
  67. res = fl3
  68. }
  69. // Do rounding check
  70. if res.BigInt().Cmp(f) == 0 {
  71. return res, nil
  72. }
  73. return res, ErrRoundingLoss
  74. }
  75. // NewFloat16Floor encodes a big.Int integer as a Float16, rounding down in
  76. // case of loss during the encoding.
  77. func NewFloat16Floor(f *big.Int) Float16 {
  78. fl1 := floorFix2Float(f)
  79. fl2 := fl1 | 0x400
  80. fi2 := fl2.BigInt()
  81. if fi2.Cmp(f) < 1 {
  82. return fl2
  83. }
  84. return fl1
  85. }