123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- package goja
- // Ported from Rhino (https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/DToA.java)
- import (
- "bytes"
- "fmt"
- "math"
- "math/big"
- "strconv"
- )
- const (
- frac_mask = 0xfffff
- exp_shift = 20
- exp_msk1 = 0x100000
- exp_shiftL = 52
- exp_mask_shifted = 0x7ff
- frac_maskL = 0xfffffffffffff
- exp_msk1L = 0x10000000000000
- exp_shift1 = 20
- exp_mask = 0x7ff00000
- bias = 1023
- p = 53
- bndry_mask = 0xfffff
- log2P = 1
- digits = "0123456789abcdefghijklmnopqrstuvwxyz"
- )
- func lo0bits(x uint32) (k uint32) {
- if (x & 7) != 0 {
- if (x & 1) != 0 {
- return 0
- }
- if (x & 2) != 0 {
- return 1
- }
- return 2
- }
- if (x & 0xffff) == 0 {
- k = 16
- x >>= 16
- }
- if (x & 0xff) == 0 {
- k += 8
- x >>= 8
- }
- if (x & 0xf) == 0 {
- k += 4
- x >>= 4
- }
- if (x & 0x3) == 0 {
- k += 2
- x >>= 2
- }
- if (x & 1) == 0 {
- k++
- x >>= 1
- if (x & 1) == 0 {
- return 32
- }
- }
- return
- }
- func hi0bits(x uint32) (k uint32) {
- if (x & 0xffff0000) == 0 {
- k = 16
- x <<= 16
- }
- if (x & 0xff000000) == 0 {
- k += 8
- x <<= 8
- }
- if (x & 0xf0000000) == 0 {
- k += 4
- x <<= 4
- }
- if (x & 0xc0000000) == 0 {
- k += 2
- x <<= 2
- }
- if (x & 0x80000000) == 0 {
- k++
- if (x & 0x40000000) == 0 {
- return 32
- }
- }
- return
- }
- func stuffBits(bits []byte, offset int, val uint32) {
- bits[offset] = byte(val >> 24)
- bits[offset+1] = byte(val >> 16)
- bits[offset+2] = byte(val >> 8)
- bits[offset+3] = byte(val)
- }
- func d2b(d float64) (b *big.Int, e int32, bits uint32) {
- dBits := math.Float64bits(d)
- d0 := uint32(dBits >> 32)
- d1 := uint32(dBits)
- z := d0 & frac_mask
- d0 &= 0x7fffffff /* clear sign bit, which we ignore */
- var de, k, i uint32
- var dbl_bits []byte
- if de = (d0 >> exp_shift); de != 0 {
- z |= exp_msk1
- }
- y := d1
- if y != 0 {
- dbl_bits = make([]byte, 8)
- k = lo0bits(y)
- y >>= k
- if k != 0 {
- stuffBits(dbl_bits, 4, y|z<<(32-k))
- z >>= k
- } else {
- stuffBits(dbl_bits, 4, y)
- }
- stuffBits(dbl_bits, 0, z)
- if z != 0 {
- i = 2
- } else {
- i = 1
- }
- } else {
- dbl_bits = make([]byte, 4)
- k = lo0bits(z)
- z >>= k
- stuffBits(dbl_bits, 0, z)
- k += 32
- i = 1
- }
- if de != 0 {
- e = int32(de - bias - (p - 1) + k)
- bits = p - k
- } else {
- e = int32(de - bias - (p - 1) + 1 + k)
- bits = 32*i - hi0bits(z)
- }
- b = (&big.Int{}).SetBytes(dbl_bits)
- return
- }
- func dtobasestr(num float64, radix int) string {
- var negative bool
- if num < 0 {
- num = -num
- negative = true
- }
- dfloor := math.Floor(num)
- ldfloor := int64(dfloor)
- var intDigits string
- if dfloor == float64(ldfloor) {
- if negative {
- ldfloor = -ldfloor
- }
- intDigits = strconv.FormatInt(ldfloor, radix)
- } else {
- floorBits := math.Float64bits(num)
- exp := int(floorBits>>exp_shiftL) & exp_mask_shifted
- var mantissa int64
- if exp == 0 {
- mantissa = int64((floorBits & frac_maskL) << 1)
- } else {
- mantissa = int64((floorBits & frac_maskL) | exp_msk1L)
- }
- if negative {
- mantissa = -mantissa
- }
- exp -= 1075
- x := big.NewInt(mantissa)
- if exp > 0 {
- x.Lsh(x, uint(exp))
- } else if exp < 0 {
- x.Rsh(x, uint(-exp))
- }
- intDigits = x.Text(radix)
- }
- if num == dfloor {
- // No fraction part
- return intDigits
- } else {
- /* We have a fraction. */
- var buffer bytes.Buffer
- buffer.WriteString(intDigits)
- buffer.WriteByte('.')
- df := num - dfloor
- dBits := math.Float64bits(num)
- word0 := uint32(dBits >> 32)
- word1 := uint32(dBits)
- b, e, _ := d2b(df)
- // JS_ASSERT(e < 0);
- /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */
- s2 := -int32((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
- if s2 == 0 {
- s2 = -1
- }
- s2 += bias + p
- /* 1/2^s2 = (nextDouble(d) - d)/2 */
- // JS_ASSERT(-s2 < e);
- if -s2 >= e {
- panic(fmt.Errorf("-s2 >= e: %d, %d", -s2, e))
- }
- mlo := big.NewInt(1)
- mhi := mlo
- if (word1 == 0) && ((word0 & bndry_mask) == 0) && ((word0 & (exp_mask & exp_mask << 1)) != 0) {
- /* The special case. Here we want to be within a quarter of the last input
- significant digit instead of one half of it when the output string's value is less than d. */
- s2 += log2P
- mhi = big.NewInt(1 << log2P)
- }
- b.Lsh(b, uint(e+s2))
- s := big.NewInt(1)
- s.Lsh(s, uint(s2))
- /* At this point we have the following:
- * s = 2^s2;
- * 1 > df = b/2^s2 > 0;
- * (d - prevDouble(d))/2 = mlo/2^s2;
- * (nextDouble(d) - d)/2 = mhi/2^s2. */
- bigBase := big.NewInt(int64(radix))
- done := false
- m := &big.Int{}
- delta := &big.Int{}
- for !done {
- b.Mul(b, bigBase)
- b.DivMod(b, s, m)
- digit := byte(b.Int64())
- b, m = m, b
- mlo.Mul(mlo, bigBase)
- if mlo != mhi {
- mhi.Mul(mhi, bigBase)
- }
- /* Do we yet have the shortest string that will round to d? */
- j := b.Cmp(mlo)
- /* j is b/2^s2 compared with mlo/2^s2. */
- delta.Sub(s, mhi)
- var j1 int
- if delta.Sign() <= 0 {
- j1 = 1
- } else {
- j1 = b.Cmp(delta)
- }
- /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
- if j1 == 0 && (word1&1) == 0 {
- if j > 0 {
- digit++
- }
- done = true
- } else if j < 0 || (j == 0 && ((word1 & 1) == 0)) {
- if j1 > 0 {
- /* Either dig or dig+1 would work here as the least significant digit.
- Use whichever would produce an output value closer to d. */
- b.Lsh(b, 1)
- j1 = b.Cmp(s)
- 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. */
- digit++
- }
- }
- done = true
- } else if j1 > 0 {
- digit++
- done = true
- }
- // JS_ASSERT(digit < (uint32)base);
- buffer.WriteByte(digits[digit])
- }
- return buffer.String()
- }
- }
|