1
0
mirror of https://github.com/golang/go synced 2024-11-24 20:30:14 -07:00

math: handle int64 overflows when checking for odd integer exponents in Pow(-0, y)

The existing implementation does a float64 to int64 comversion in order to check whether the number is odd, however it does not check for overflows.
If an overflow occurs, the result is implementation-defined and while it happens to work on amd64 and i386, it produces an incorrect result on arm64
and possibly other architectures.

This change fixes that and also avoids calling isOddInt altogether if the base is +0, because it's unnecessary.

(I was considering avoiding the extra check if runtime.GOARCH is "amd64" or "i386", but I can't see this pattern being used anywhere outside the tests.
And having separate files with build tags just for isOddInt() seems like an overkill)

Fixes #57465
This commit is contained in:
Dmitry Panov 2022-12-28 18:31:53 +00:00
parent e870de9936
commit 9a6d4584a8
2 changed files with 13 additions and 3 deletions

View File

@ -1687,6 +1687,9 @@ var vfpowSC = [][2]float64{
{Nextafter(1, -2), float64(1 << 63)}, {Nextafter(1, -2), float64(1 << 63)},
{Nextafter(-1, 2), float64(1 << 63)}, {Nextafter(-1, 2), float64(1 << 63)},
{Nextafter(-1, -2), float64(1 << 63)}, {Nextafter(-1, -2), float64(1 << 63)},
// Issue #57465, exponent is an even number that overflows int64
{Copysign(0, -1), 1e19},
} }
var powSC = []float64{ var powSC = []float64{
0, // pow(-Inf, -Pi) 0, // pow(-Inf, -Pi)
@ -1762,6 +1765,9 @@ var powSC = []float64{
0, // pow(Nextafter(1, -2), float64(1 << 63)) 0, // pow(Nextafter(1, -2), float64(1 << 63))
0, // pow(Nextafter(-1, 2), float64(1 << 63)) 0, // pow(Nextafter(-1, 2), float64(1 << 63))
Inf(1), // pow(Nextafter(-1, -2), float64(1 << 63)) Inf(1), // pow(Nextafter(-1, -2), float64(1 << 63))
// Issue #57465, exponent is an even number that overflows int64
0,
} }
var vfpow10SC = []int{ var vfpow10SC = []int{

View File

@ -5,6 +5,10 @@
package math package math
func isOddInt(x float64) bool { func isOddInt(x float64) bool {
if Abs(x) >= (1 << 53) {
return false
}
xi, xf := Modf(x) xi, xf := Modf(x)
return xf == 0 && int64(xi)&1 == 1 return xf == 0 && int64(xi)&1 == 1
} }
@ -54,12 +58,12 @@ func pow(x, y float64) float64 {
case x == 0: case x == 0:
switch { switch {
case y < 0: case y < 0:
if isOddInt(y) { if Signbit(x) && isOddInt(y) {
return Copysign(Inf(1), x) return Inf(-1)
} }
return Inf(1) return Inf(1)
case y > 0: case y > 0:
if isOddInt(y) { if Signbit(x) && isOddInt(y) {
return x return x
} }
return 0 return 0