diff --git a/src/internal/strconv/ftoa.go b/src/internal/strconv/ftoa.go
index c8c98c1..027bc2c 100644
--- a/src/internal/strconv/ftoa.go
+++ b/src/internal/strconv/ftoa.go
@@ -135,7 +135,11 @@
// Use the Dragonbox algorithm.
var buf [32]byte
digs.d = buf[:]
- dboxFtoa(&digs, mant, exp-int(flt.mantbits), denorm, bitSize)
+ if bitSize == 32 {
+ dboxFtoa32(&digs, uint32(mant), exp-int(flt.mantbits), denorm)
+ } else {
+ dboxFtoa64(&digs, mant, exp-int(flt.mantbits), denorm)
+ }
// Precision for shortest representation mode.
switch fmt {
case 'e', 'E':
diff --git a/src/internal/strconv/ftoadbox.go b/src/internal/strconv/ftoadbox.go
index bdd9cd1..018984f 100644
--- a/src/internal/strconv/ftoadbox.go
+++ b/src/internal/strconv/ftoadbox.go
@@ -19,17 +19,9 @@
// The reference implementation in C++ by Junekey Jeon can be found at:
// https://github.com/jk-jeon/dragonbox/blob/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/simple/include/simple_dragonbox.h
-// dragonboxFtoa computes the decimal significand and exponent
-// from the binary significand and exponent using the Dragonbox algorithm
+// dboxFtoa64 computes the decimal significand and exponent
+// from the binary significand and exponent using the Dragonbox algorithm,
// and formats the decimal floating point number in d.
-func dboxFtoa(d *decimalSlice, mant uint64, exp int, denorm bool, bitSize int) {
- if bitSize == 32 {
- dboxFtoa32(d, uint32(mant), exp, denorm)
- return
- }
- dboxFtoa64(d, mant, exp, denorm)
-}
-
func dboxFtoa64(d *decimalSlice, mant uint64, exp int, denorm bool) {
if mant == 1<<float64MantBits && !denorm {
// Algorithm 5.6 (page 24).
@@ -65,7 +57,8 @@
// Algorithm 5.2 (page 15).
k0 := -mulLog10_2(exp)
φ, β := dboxPow64(κ+k0, exp)
- zi, exact := dboxMulPow64(uint64(mant*2+1)<<β, φ)
+ mant2 := mant << 1
+ zi, exact := dboxMulPow64(uint64(mant2+1)<<β, φ)
s, r := zi/p10κ1, uint32(zi%p10κ1)
δi := dboxDelta64(φ, β)
@@ -78,7 +71,7 @@
s--
r = p10κ * 10
} else if r == δi {
- parity, exact := dboxParity64(uint64(mant*2-1), φ, β)
+ parity, exact := dboxParity64(uint64(mant2-1), φ, β)
if parity || (exact && mant%2 == 0) {
s, zeros := trimZeros(s)
dboxDigits(d, s, -k0+1+zeros)
@@ -91,7 +84,7 @@
t, ρ := D/p10κ, D%p10κ
yru := 10*s + uint64(t)
if ρ == 0 {
- parity, exact := dboxParity64(mant*2, φ, β)
+ parity, exact := dboxParity64(mant2, φ, β)
if parity != ((D-p10κ/2)%2 != 0) || exact && yru%2 != 0 {
yru--
}
@@ -99,7 +92,7 @@
dboxDigits(d, yru, -k0)
}
-// Almost identical to dragonboxFtoa64.
+// Almost identical to dboxFtoa64.
// This is kept as a separate copy to minimize runtime overhead.
func dboxFtoa32(d *decimalSlice, mant uint32, exp int, denorm bool) {
if mant == 1<<float32MantBits && !denorm {
@@ -136,7 +129,8 @@
// Algorithm 5.2 (page 15).
k0 := -mulLog10_2(exp)
φ, β := dboxPow32(κ+k0, exp)
- zi, exact := dboxMulPow32(uint32(mant*2+1)<<β, φ)
+ mant2 := mant << 1
+ zi, exact := dboxMulPow32(uint32(mant2+1)<<β, φ)
s, r := zi/p10κ1, uint32(zi%p10κ1)
δi := dboxDelta32(φ, β)
@@ -149,7 +143,7 @@
s--
r = p10κ * 10
} else if r == δi {
- parity, exact := dboxParity32(uint32(mant*2-1), φ, β)
+ parity, exact := dboxParity32(uint32(mant2-1), φ, β)
if parity || (exact && mant%2 == 0) {
s, zeros := trimZeros(uint64(s))
dboxDigits(d, s, -k0+1+zeros)
@@ -162,7 +156,7 @@
t, ρ := D/p10κ, D%p10κ
yru := 10*s + uint32(t)
if ρ == 0 {
- parity, exact := dboxParity32(mant*2, φ, β)
+ parity, exact := dboxParity32(mant2, φ, β)
if parity != ((D-p10κ/2)%2 != 0) || exact && yru%2 != 0 {
yru--
}
@@ -179,17 +173,6 @@
d.dp = d.nd + exp
}
-// uadd128 returns the full 128 bits of u + n.
-func uadd128(u uint128, n uint64) uint128 {
- sum := uint64(u.Lo + n)
- // Check if lo is wrapped around.
- if sum < u.Lo {
- u.Hi++
- }
- u.Lo = sum
- return u
-}
-
// umul64 returns the full 64 bits of x * y.
func umul64(x, y uint32) uint64 {
return uint64(x) * uint64(y)
@@ -211,45 +194,12 @@
return uint64(uint64(x) * y)
}
-// umul128Upper64 returns the upper 64 bits (out of 128 bits) of x * y.
-func umul128Upper64(x, y uint64) uint64 {
- a := uint32(x >> 32)
- b := uint32(x)
- c := uint32(y >> 32)
- d := uint32(y)
-
- ac := umul64(a, c)
- bc := umul64(b, c)
- ad := umul64(a, d)
- bd := umul64(b, d)
-
- intermediate := (bd >> 32) + uint64(uint32(ad)) + uint64(uint32(bc))
-
- return ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32)
-}
-
-// umul192Upper128 returns the upper 128 bits (out of 192 bits) of x * y.
-func umul192Upper128(x uint64, y uint128) uint128 {
- r := umul128(x, y.Hi)
- t := umul128Upper64(x, y.Lo)
- return uadd128(r, t)
-}
-
-// umul192Lower128 returns the lower 128 bits (out of 192 bits) of x * y.
-func umul192Lower128(x uint64, y uint128) uint128 {
- high := x * y.Hi
- highLow := umul128(x, y.Lo)
- return uint128{uint64(high + highLow.Hi), highLow.Lo}
-}
-
// dboxMulPow64 computes x^(i), y^(i), z^(i)
// from the precomputed value of φ̃k for float64
// and also checks if x^(f), y^(f), z^(f) == 0 (section 5.2.1).
func dboxMulPow64(u uint64, phi uint128) (intPart uint64, isInt bool) {
- r := umul192Upper128(u, phi)
- intPart = r.Hi
- isInt = r.Lo == 0
- return
+ hi, mid, _ := umul192(u, phi)
+ return hi, mid == 0
}
// dboxMulPow32 computes x^(i), y^(i), z^(i)
@@ -266,9 +216,9 @@
// from the precomputed value of φ̃k for float64
// and also checks if x^(f), y^(f), z^(f) = 0 (section 5.2.1).
func dboxParity64(mant2 uint64, phi uint128, beta int) (parity bool, isInt bool) {
- r := umul192Lower128(mant2, phi)
- parity = ((r.Hi >> (64 - beta)) & 1) != 0
- isInt = ((uint64(r.Hi << beta)) | (r.Lo >> (64 - beta))) == 0
+ _, mid, lo := umul192(mant2, phi)
+ parity = ((mid >> (64 - beta)) & 1) != 0
+ isInt = ((mid << beta) | (lo >> (64 - beta))) == 0
return
}
@@ -299,11 +249,6 @@
return (e*631305 - 261663) >> 21
}
-const (
- floatMantBits64 = 52 // p = 52 for float64.
- floatMantBits32 = 23 // p = 23 for float32.
-)
-
// dboxRange64 returns the left and right float64 endpoints.
func dboxRange64(φ uint128, β int) (left, right uint64) {
left = (φ.Hi - (φ.Hi >> (float64MantBits + 2))) >> (64 - float64MantBits - 1 - β)
@@ -313,19 +258,19 @@
// dboxRange32 returns the left and right float32 endpoints.
func dboxRange32(φ uint64, β int) (left, right uint32) {
- left = uint32((φ - (φ >> (floatMantBits32 + 2))) >> (64 - floatMantBits32 - 1 - β))
- right = uint32((φ + (φ >> (floatMantBits32 + 1))) >> (64 - floatMantBits32 - 1 - β))
+ left = uint32((φ - (φ >> (float32MantBits + 2))) >> (64 - float32MantBits - 1 - β))
+ right = uint32((φ + (φ >> (float32MantBits + 1))) >> (64 - float32MantBits - 1 - β))
return left, right
}
// dboxRoundUp64 computes the round up of y (i.e., y^(ru)).
func dboxRoundUp64(phi uint128, beta int) uint64 {
- return (phi.Hi>>(128/2-floatMantBits64-2-beta) + 1) / 2
+ return (phi.Hi>>(128/2-float64MantBits-2-beta) + 1) / 2
}
// dboxRoundUp32 computes the round up of y (i.e., y^(ru)).
func dboxRoundUp32(phi uint64, beta int) uint32 {
- return uint32(phi>>(64-floatMantBits32-2-beta)+1) / 2
+ return uint32(phi>>(64-float32MantBits-2-beta)+1) / 2
}
// dboxPow64 gets the precomputed value of φ̃̃k for float64.
diff --git a/src/internal/strconv/math_test.go b/src/internal/strconv/math_test.go
index 55e25f9..995ab95 100644
--- a/src/internal/strconv/math_test.go
+++ b/src/internal/strconv/math_test.go
@@ -62,6 +62,17 @@
}{
{0, u128(0, 0), 0, 0, 0},
{^uint64(0), u128(^uint64(0), ^uint64(0)), ^uint64(1), ^uint64(0), 1},
+
+ // Type 1: Cases where mid == 0 (tests isInt logic in dboxMulPow64)
+ {1, u128(0, 1), 0, 0, 1}, // 1 * {0, 1} = {0, 0, 1}
+ {1 << 32, u128(1<<32, 0), 1, 0, 0}, // 2^32 * {2^32, 0} = {1, 0, 0}
+ {0x100, u128(0x100000000000000, 0), 1, 0, 0}, // 256 * 2^56 = 2^64 = {1, 0, 0}
+
+ // Type 2: Powers of 2 and interesting bit patterns
+ {1 << 32, u128(0, 1<<32), 0, 1, 0}, // 2^32 * {0, 2^32} = {0, 1, 0}
+ {0x123456789ABCDEF0, u128(1, 0), 0, 0x123456789ABCDEF0, 0}, // Large value * {1, 0}
+ {0x123456789ABCDEF0, u128(0, 2), 0, 0, 0x2468ACF13579BDE0}, // Large value * {0, 2}
+ {1 << 63, u128(2, 0), 1, 0, 0}, // 2^63 * {2, 0} = {1, 0, 0}
}
func TestUmul192(t *testing.T) {