291 lines
5.6 KiB
Go
291 lines
5.6 KiB
Go
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()
|
|
}
|
|
}
|