mirror of
https://github.com/golang/go
synced 2024-09-05 16:04:50 +00:00
![Junchen Li](/assets/img/avatar_default.png)
Consider the following example, func test(a, b float64, x uint64) uint64 { if a < b { x = 0 } return x } func main() { fmt.Println(test(1, math.NaN(), 123)) } The output is 0, but the expectation is 123. This is because the rewrite rule (CSEL [cc] (MOVDconst [0]) y flag) => (CSEL0 [arm64Negate(cc)] y flag) converts FCMP NaN, 1 CSEL MI, 0, 123, R0 // if 1 < NaN then R0 = 0 else R0 = 123 to FCMP NaN, 1 CSEL GE, 123, 0, R0 // if 1 >= NaN then R0 = 123 else R0 = 0 But both 1 < NaN and 1 >= NaN are false. So the output is 0, not 123. The root cause is arm64Negate not handle negation of floating comparison correctly. According to the ARM manual, the meaning of MI, GE, and PL are MI: Less than GE: Greater than or equal to PL: Greater than, equal to, or unordered Because NaN cannot be compared with other numbers, the result of such comparison is unordered. So when NaN is involved, unlike integer, the result of !(a < b) is not a >= b, it is a >= b || a is NaN || b is NaN. This is exactly what PL means. We add NotLessThanF to represent PL. Then the negation of LessThanF is NotLessThanF rather than GreaterEqualF. The same reason for the other floating comparison operations. Fixes #43619 Change-Id: Ia511b0027ad067436bace9fbfd261dbeaae01bcd Reviewed-on: https://go-review.googlesource.com/c/go/+/283572 Reviewed-by: Cherry Zhang <cherryyz@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Keith Randall <khr@golang.org>
120 lines
2.2 KiB
Go
120 lines
2.2 KiB
Go
// run
|
|
|
|
// Copyright 2021 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
//go:noinline
|
|
func fcmplt(a, b float64, x uint64) uint64 {
|
|
if a < b {
|
|
x = 0
|
|
}
|
|
return x
|
|
}
|
|
|
|
//go:noinline
|
|
func fcmple(a, b float64, x uint64) uint64 {
|
|
if a <= b {
|
|
x = 0
|
|
}
|
|
return x
|
|
}
|
|
|
|
//go:noinline
|
|
func fcmpgt(a, b float64, x uint64) uint64 {
|
|
if a > b {
|
|
x = 0
|
|
}
|
|
return x
|
|
}
|
|
|
|
//go:noinline
|
|
func fcmpge(a, b float64, x uint64) uint64 {
|
|
if a >= b {
|
|
x = 0
|
|
}
|
|
return x
|
|
}
|
|
|
|
//go:noinline
|
|
func fcmpeq(a, b float64, x uint64) uint64 {
|
|
if a == b {
|
|
x = 0
|
|
}
|
|
return x
|
|
}
|
|
|
|
//go:noinline
|
|
func fcmpne(a, b float64, x uint64) uint64 {
|
|
if a != b {
|
|
x = 0
|
|
}
|
|
return x
|
|
}
|
|
|
|
func main() {
|
|
type fn func(a, b float64, x uint64) uint64
|
|
|
|
type testCase struct {
|
|
f fn
|
|
a, b float64
|
|
x, want uint64
|
|
}
|
|
NaN := math.NaN()
|
|
for _, t := range []testCase{
|
|
{fcmplt, 1.0, 1.0, 123, 123},
|
|
{fcmple, 1.0, 1.0, 123, 0},
|
|
{fcmpgt, 1.0, 1.0, 123, 123},
|
|
{fcmpge, 1.0, 1.0, 123, 0},
|
|
{fcmpeq, 1.0, 1.0, 123, 0},
|
|
{fcmpne, 1.0, 1.0, 123, 123},
|
|
|
|
{fcmplt, 1.0, 2.0, 123, 0},
|
|
{fcmple, 1.0, 2.0, 123, 0},
|
|
{fcmpgt, 1.0, 2.0, 123, 123},
|
|
{fcmpge, 1.0, 2.0, 123, 123},
|
|
{fcmpeq, 1.0, 2.0, 123, 123},
|
|
{fcmpne, 1.0, 2.0, 123, 0},
|
|
|
|
{fcmplt, 2.0, 1.0, 123, 123},
|
|
{fcmple, 2.0, 1.0, 123, 123},
|
|
{fcmpgt, 2.0, 1.0, 123, 0},
|
|
{fcmpge, 2.0, 1.0, 123, 0},
|
|
{fcmpeq, 2.0, 1.0, 123, 123},
|
|
{fcmpne, 2.0, 1.0, 123, 0},
|
|
|
|
{fcmplt, 1.0, NaN, 123, 123},
|
|
{fcmple, 1.0, NaN, 123, 123},
|
|
{fcmpgt, 1.0, NaN, 123, 123},
|
|
{fcmpge, 1.0, NaN, 123, 123},
|
|
{fcmpeq, 1.0, NaN, 123, 123},
|
|
{fcmpne, 1.0, NaN, 123, 0},
|
|
|
|
{fcmplt, NaN, 1.0, 123, 123},
|
|
{fcmple, NaN, 1.0, 123, 123},
|
|
{fcmpgt, NaN, 1.0, 123, 123},
|
|
{fcmpge, NaN, 1.0, 123, 123},
|
|
{fcmpeq, NaN, 1.0, 123, 123},
|
|
{fcmpne, NaN, 1.0, 123, 0},
|
|
|
|
{fcmplt, NaN, NaN, 123, 123},
|
|
{fcmple, NaN, NaN, 123, 123},
|
|
{fcmpgt, NaN, NaN, 123, 123},
|
|
{fcmpge, NaN, NaN, 123, 123},
|
|
{fcmpeq, NaN, NaN, 123, 123},
|
|
{fcmpne, NaN, NaN, 123, 0},
|
|
} {
|
|
got := t.f(t.a, t.b, t.x)
|
|
if got != t.want {
|
|
panic(fmt.Sprintf("want %v, got %v", t.want, got))
|
|
}
|
|
}
|
|
}
|