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() } }