ftobasestr.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package ftoa
  2. import (
  3. "fmt"
  4. "math"
  5. "math/big"
  6. "strconv"
  7. "strings"
  8. )
  9. const (
  10. digits = "0123456789abcdefghijklmnopqrstuvwxyz"
  11. )
  12. func FToBaseStr(num float64, radix int) string {
  13. var negative bool
  14. if num < 0 {
  15. num = -num
  16. negative = true
  17. }
  18. dfloor := math.Floor(num)
  19. ldfloor := int64(dfloor)
  20. var intDigits string
  21. if dfloor == float64(ldfloor) {
  22. if negative {
  23. ldfloor = -ldfloor
  24. }
  25. intDigits = strconv.FormatInt(ldfloor, radix)
  26. } else {
  27. floorBits := math.Float64bits(num)
  28. exp := int(floorBits>>exp_shiftL) & exp_mask_shifted
  29. var mantissa int64
  30. if exp == 0 {
  31. mantissa = int64((floorBits & frac_maskL) << 1)
  32. } else {
  33. mantissa = int64((floorBits & frac_maskL) | exp_msk1L)
  34. }
  35. if negative {
  36. mantissa = -mantissa
  37. }
  38. exp -= 1075
  39. x := big.NewInt(mantissa)
  40. if exp > 0 {
  41. x.Lsh(x, uint(exp))
  42. } else if exp < 0 {
  43. x.Rsh(x, uint(-exp))
  44. }
  45. intDigits = x.Text(radix)
  46. }
  47. if num == dfloor {
  48. // No fraction part
  49. return intDigits
  50. } else {
  51. /* We have a fraction. */
  52. var buffer strings.Builder
  53. buffer.WriteString(intDigits)
  54. buffer.WriteByte('.')
  55. df := num - dfloor
  56. dBits := math.Float64bits(num)
  57. word0 := uint32(dBits >> 32)
  58. word1 := uint32(dBits)
  59. dblBits := make([]byte, 0, 8)
  60. e, _, dblBits := d2b(df, dblBits)
  61. // JS_ASSERT(e < 0);
  62. /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */
  63. s2 := -int((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
  64. if s2 == 0 {
  65. s2 = -1
  66. }
  67. s2 += bias + p
  68. /* 1/2^s2 = (nextDouble(d) - d)/2 */
  69. // JS_ASSERT(-s2 < e);
  70. if -s2 >= e {
  71. panic(fmt.Errorf("-s2 >= e: %d, %d", -s2, e))
  72. }
  73. mlo := big.NewInt(1)
  74. mhi := mlo
  75. if (word1 == 0) && ((word0 & bndry_mask) == 0) && ((word0 & (exp_mask & (exp_mask << 1))) != 0) {
  76. /* The special case. Here we want to be within a quarter of the last input
  77. significant digit instead of one half of it when the output string's value is less than d. */
  78. s2 += log2P
  79. mhi = big.NewInt(1 << log2P)
  80. }
  81. b := new(big.Int).SetBytes(dblBits)
  82. b.Lsh(b, uint(e+s2))
  83. s := big.NewInt(1)
  84. s.Lsh(s, uint(s2))
  85. /* At this point we have the following:
  86. * s = 2^s2;
  87. * 1 > df = b/2^s2 > 0;
  88. * (d - prevDouble(d))/2 = mlo/2^s2;
  89. * (nextDouble(d) - d)/2 = mhi/2^s2. */
  90. bigBase := big.NewInt(int64(radix))
  91. done := false
  92. m := &big.Int{}
  93. delta := &big.Int{}
  94. for !done {
  95. b.Mul(b, bigBase)
  96. b.DivMod(b, s, m)
  97. digit := byte(b.Int64())
  98. b, m = m, b
  99. mlo.Mul(mlo, bigBase)
  100. if mlo != mhi {
  101. mhi.Mul(mhi, bigBase)
  102. }
  103. /* Do we yet have the shortest string that will round to d? */
  104. j := b.Cmp(mlo)
  105. /* j is b/2^s2 compared with mlo/2^s2. */
  106. delta.Sub(s, mhi)
  107. var j1 int
  108. if delta.Sign() <= 0 {
  109. j1 = 1
  110. } else {
  111. j1 = b.Cmp(delta)
  112. }
  113. /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
  114. if j1 == 0 && (word1&1) == 0 {
  115. if j > 0 {
  116. digit++
  117. }
  118. done = true
  119. } else if j < 0 || (j == 0 && ((word1 & 1) == 0)) {
  120. if j1 > 0 {
  121. /* Either dig or dig+1 would work here as the least significant digit.
  122. Use whichever would produce an output value closer to d. */
  123. b.Lsh(b, 1)
  124. j1 = b.Cmp(s)
  125. if j1 > 0 { /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output such as 3.5 in base 3. */
  126. digit++
  127. }
  128. }
  129. done = true
  130. } else if j1 > 0 {
  131. digit++
  132. done = true
  133. }
  134. // JS_ASSERT(digit < (uint32)base);
  135. buffer.WriteByte(digits[digit])
  136. }
  137. return buffer.String()
  138. }
  139. }