mirror of
https://github.com/golang/go
synced 2024-11-21 23:34:42 -07:00
gc, runtime: handle floating point map keys
Fixes #2609. R=ken2 CC=golang-dev https://golang.org/cl/5572069
This commit is contained in:
parent
109a976355
commit
408f0b1f74
@ -57,6 +57,10 @@ enum
|
|||||||
AINTER,
|
AINTER,
|
||||||
ANILINTER,
|
ANILINTER,
|
||||||
ASLICE,
|
ASLICE,
|
||||||
|
AFLOAT32,
|
||||||
|
AFLOAT64,
|
||||||
|
ACPLX64,
|
||||||
|
ACPLX128,
|
||||||
|
|
||||||
BADWIDTH = -1000000000,
|
BADWIDTH = -1000000000,
|
||||||
};
|
};
|
||||||
|
@ -515,23 +515,31 @@ algtype1(Type *t, Type **bad)
|
|||||||
case TINT:
|
case TINT:
|
||||||
case TUINT:
|
case TUINT:
|
||||||
case TUINTPTR:
|
case TUINTPTR:
|
||||||
case TCOMPLEX64:
|
|
||||||
case TCOMPLEX128:
|
|
||||||
case TFLOAT32:
|
|
||||||
case TFLOAT64:
|
|
||||||
case TBOOL:
|
case TBOOL:
|
||||||
case TPTR32:
|
case TPTR32:
|
||||||
case TPTR64:
|
case TPTR64:
|
||||||
case TCHAN:
|
case TCHAN:
|
||||||
case TUNSAFEPTR:
|
case TUNSAFEPTR:
|
||||||
return AMEM;
|
return AMEM;
|
||||||
|
|
||||||
case TFUNC:
|
case TFUNC:
|
||||||
case TMAP:
|
case TMAP:
|
||||||
if(bad)
|
if(bad)
|
||||||
*bad = t;
|
*bad = t;
|
||||||
return ANOEQ;
|
return ANOEQ;
|
||||||
|
|
||||||
|
case TFLOAT32:
|
||||||
|
return AFLOAT32;
|
||||||
|
|
||||||
|
case TFLOAT64:
|
||||||
|
return AFLOAT64;
|
||||||
|
|
||||||
|
case TCOMPLEX64:
|
||||||
|
return ACPLX64;
|
||||||
|
|
||||||
|
case TCOMPLEX128:
|
||||||
|
return ACPLX128;
|
||||||
|
|
||||||
case TSTRING:
|
case TSTRING:
|
||||||
return ASTRING;
|
return ASTRING;
|
||||||
|
|
||||||
@ -2511,6 +2519,18 @@ hashfor(Type *t)
|
|||||||
case ASTRING:
|
case ASTRING:
|
||||||
sym = pkglookup("strhash", runtimepkg);
|
sym = pkglookup("strhash", runtimepkg);
|
||||||
break;
|
break;
|
||||||
|
case AFLOAT32:
|
||||||
|
sym = pkglookup("f32hash", runtimepkg);
|
||||||
|
break;
|
||||||
|
case AFLOAT64:
|
||||||
|
sym = pkglookup("f64hash", runtimepkg);
|
||||||
|
break;
|
||||||
|
case ACPLX64:
|
||||||
|
sym = pkglookup("c64hash", runtimepkg);
|
||||||
|
break;
|
||||||
|
case ACPLX128:
|
||||||
|
sym = pkglookup("c128hash", runtimepkg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
sym = typesymprefix(".hash", t);
|
sym = typesymprefix(".hash", t);
|
||||||
break;
|
break;
|
||||||
@ -2537,7 +2557,7 @@ genhash(Sym *sym, Type *t)
|
|||||||
Node *hashel;
|
Node *hashel;
|
||||||
Type *first, *t1;
|
Type *first, *t1;
|
||||||
int old_safemode;
|
int old_safemode;
|
||||||
int64 size;
|
int64 size, mul;
|
||||||
|
|
||||||
if(debug['r'])
|
if(debug['r'])
|
||||||
print("genhash %S %T\n", sym, t);
|
print("genhash %S %T\n", sym, t);
|
||||||
@ -2594,6 +2614,17 @@ genhash(Sym *sym, Type *t)
|
|||||||
nod(OLSH, nod(OIND, nh, N), nodintconst(3)),
|
nod(OLSH, nod(OIND, nh, N), nodintconst(3)),
|
||||||
nod(ORSH, nod(OIND, nh, N), nodintconst(widthptr*8-3)))));
|
nod(ORSH, nod(OIND, nh, N), nodintconst(widthptr*8-3)))));
|
||||||
|
|
||||||
|
// *h *= mul
|
||||||
|
// Same multipliers as in runtime.memhash.
|
||||||
|
if(widthptr == 4)
|
||||||
|
mul = 3267000013LL;
|
||||||
|
else
|
||||||
|
mul = 23344194077549503LL;
|
||||||
|
n->nbody = list(n->nbody,
|
||||||
|
nod(OAS,
|
||||||
|
nod(OIND, nh, N),
|
||||||
|
nod(OMUL, nod(OIND, nh, N), nodintconst(mul))));
|
||||||
|
|
||||||
// hashel(h, sizeof(p[i]), &p[i])
|
// hashel(h, sizeof(p[i]), &p[i])
|
||||||
call = nod(OCALL, hashel, N);
|
call = nod(OCALL, hashel, N);
|
||||||
call->list = list(call->list, nh);
|
call->list = list(call->list, nh);
|
||||||
|
@ -197,6 +197,106 @@ runtime·memcopy128(uintptr s, void *a, void *b)
|
|||||||
((uint64*)a)[1] = ((uint64*)b)[1];
|
((uint64*)a)[1] = ((uint64*)b)[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·f32equal(bool *eq, uintptr s, void *a, void *b)
|
||||||
|
{
|
||||||
|
USED(s);
|
||||||
|
*eq = *(float32*)a == *(float32*)b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·f64equal(bool *eq, uintptr s, void *a, void *b)
|
||||||
|
{
|
||||||
|
USED(s);
|
||||||
|
*eq = *(float64*)a == *(float64*)b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·c64equal(bool *eq, uintptr s, void *a, void *b)
|
||||||
|
{
|
||||||
|
Complex64 *ca, *cb;
|
||||||
|
|
||||||
|
USED(s);
|
||||||
|
ca = a;
|
||||||
|
cb = b;
|
||||||
|
*eq = ca->real == cb->real && ca->imag == cb->imag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·c128equal(bool *eq, uintptr s, void *a, void *b)
|
||||||
|
{
|
||||||
|
Complex128 *ca, *cb;
|
||||||
|
|
||||||
|
USED(s);
|
||||||
|
ca = a;
|
||||||
|
cb = b;
|
||||||
|
*eq = ca->real == cb->real && ca->imag == cb->imag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Because NaN != NaN, a map can contain any
|
||||||
|
// number of (mostly useless) entries keyed with NaNs.
|
||||||
|
// To avoid long hash chains, we assign a random number
|
||||||
|
// as the hash value for a NaN.
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·f32hash(uintptr *h, uintptr s, void *a)
|
||||||
|
{
|
||||||
|
uintptr hash;
|
||||||
|
float32 f;
|
||||||
|
|
||||||
|
USED(s);
|
||||||
|
f = *(float32*)a;
|
||||||
|
if(f == 0)
|
||||||
|
hash = 0; // +0, -0
|
||||||
|
else if(f != f)
|
||||||
|
hash = runtime·fastrand1(); // any kind of NaN
|
||||||
|
else
|
||||||
|
hash = *(uint32*)a;
|
||||||
|
*h ^= (*h ^ hash ^ 2860486313U) * 3267000013U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·f64hash(uintptr *h, uintptr s, void *a)
|
||||||
|
{
|
||||||
|
uintptr hash;
|
||||||
|
float64 f;
|
||||||
|
uint64 u;
|
||||||
|
|
||||||
|
USED(s);
|
||||||
|
f = *(float32*)a;
|
||||||
|
if(f == 0)
|
||||||
|
hash = 0; // +0, -0
|
||||||
|
else if(f != f)
|
||||||
|
hash = runtime·fastrand1(); // any kind of NaN
|
||||||
|
else {
|
||||||
|
u = *(uint64*)a;
|
||||||
|
if(sizeof(uintptr) == 4)
|
||||||
|
hash = ((uint32)(u>>32) ^ 2860486313) * (uint32)u;
|
||||||
|
else
|
||||||
|
hash = u;
|
||||||
|
}
|
||||||
|
if(sizeof(uintptr) == 4)
|
||||||
|
*h = (*h ^ hash ^ 2860486313U) * 3267000013U;
|
||||||
|
else
|
||||||
|
*h = (*h ^ hash ^ 33054211828000289ULL) * 23344194077549503ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·c64hash(uintptr *h, uintptr s, void *a)
|
||||||
|
{
|
||||||
|
USED(s);
|
||||||
|
runtime·f32hash(h, 0, a);
|
||||||
|
runtime·f32hash(h, 0, (float32*)a+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
runtime·c128hash(uintptr *h, uintptr s, void *a)
|
||||||
|
{
|
||||||
|
USED(s);
|
||||||
|
runtime·f64hash(h, 0, a);
|
||||||
|
runtime·f64hash(h, 0, (float64*)a+1);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
runtime·slicecopy(uintptr s, void *a, void *b)
|
runtime·slicecopy(uintptr s, void *a, void *b)
|
||||||
{
|
{
|
||||||
@ -349,6 +449,10 @@ runtime·algarray[] =
|
|||||||
[AINTER] { runtime·interhash, runtime·interequal, runtime·interprint, runtime·intercopy },
|
[AINTER] { runtime·interhash, runtime·interequal, runtime·interprint, runtime·intercopy },
|
||||||
[ANILINTER] { runtime·nilinterhash, runtime·nilinterequal, runtime·nilinterprint, runtime·nilintercopy },
|
[ANILINTER] { runtime·nilinterhash, runtime·nilinterequal, runtime·nilinterprint, runtime·nilintercopy },
|
||||||
[ASLICE] { runtime·nohash, runtime·noequal, runtime·memprint, runtime·slicecopy },
|
[ASLICE] { runtime·nohash, runtime·noequal, runtime·memprint, runtime·slicecopy },
|
||||||
|
[AFLOAT32] { runtime·f32hash, runtime·f32equal, runtime·memprint, runtime·memcopy },
|
||||||
|
[AFLOAT64] { runtime·f64hash, runtime·f64equal, runtime·memprint, runtime·memcopy },
|
||||||
|
[ACPLX64] { runtime·c64hash, runtime·c64equal, runtime·memprint, runtime·memcopy },
|
||||||
|
[ACPLX128] { runtime·c128hash, runtime·c128equal, runtime·memprint, runtime·memcopy },
|
||||||
[AMEM0] { runtime·memhash, runtime·memequal0, runtime·memprint, runtime·memcopy0 },
|
[AMEM0] { runtime·memhash, runtime·memequal0, runtime·memprint, runtime·memcopy0 },
|
||||||
[AMEM8] { runtime·memhash, runtime·memequal8, runtime·memprint, runtime·memcopy8 },
|
[AMEM8] { runtime·memhash, runtime·memequal8, runtime·memprint, runtime·memcopy8 },
|
||||||
[AMEM16] { runtime·memhash, runtime·memequal16, runtime·memprint, runtime·memcopy16 },
|
[AMEM16] { runtime·memhash, runtime·memequal16, runtime·memprint, runtime·memcopy16 },
|
||||||
|
@ -278,8 +278,8 @@ runtime·check(void)
|
|||||||
uint32 f;
|
uint32 f;
|
||||||
int64 g;
|
int64 g;
|
||||||
uint64 h;
|
uint64 h;
|
||||||
float32 i;
|
float32 i, i1;
|
||||||
float64 j;
|
float64 j, j1;
|
||||||
void* k;
|
void* k;
|
||||||
uint16* l;
|
uint16* l;
|
||||||
struct x1 {
|
struct x1 {
|
||||||
@ -319,6 +319,30 @@ runtime·check(void)
|
|||||||
if(z != 4)
|
if(z != 4)
|
||||||
runtime·throw("cas4");
|
runtime·throw("cas4");
|
||||||
|
|
||||||
|
*(uint64*)&j = ~0ULL;
|
||||||
|
if(j == j)
|
||||||
|
runtime·throw("float64nan");
|
||||||
|
if(!(j != j))
|
||||||
|
runtime·throw("float64nan1");
|
||||||
|
|
||||||
|
*(uint64*)&j1 = ~1ULL;
|
||||||
|
if(j == j1)
|
||||||
|
runtime·throw("float64nan2");
|
||||||
|
if(!(j != j1))
|
||||||
|
runtime·throw("float64nan3");
|
||||||
|
|
||||||
|
*(uint32*)&i = ~0UL;
|
||||||
|
if(i == i)
|
||||||
|
runtime·throw("float32nan");
|
||||||
|
if(!(i != i))
|
||||||
|
runtime·throw("float32nan1");
|
||||||
|
|
||||||
|
*(uint32*)&i1 = ~1UL;
|
||||||
|
if(i == i1)
|
||||||
|
runtime·throw("float32nan2");
|
||||||
|
if(!(i != i1))
|
||||||
|
runtime·throw("float32nan3");
|
||||||
|
|
||||||
runtime·initsig(0);
|
runtime·initsig(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,6 +375,10 @@ enum
|
|||||||
AINTER,
|
AINTER,
|
||||||
ANILINTER,
|
ANILINTER,
|
||||||
ASLICE,
|
ASLICE,
|
||||||
|
AFLOAT32,
|
||||||
|
AFLOAT64,
|
||||||
|
ACPLX64,
|
||||||
|
ACPLX128,
|
||||||
Amax
|
Amax
|
||||||
};
|
};
|
||||||
typedef struct Alg Alg;
|
typedef struct Alg Alg;
|
||||||
|
157
test/map.go
157
test/map.go
@ -8,6 +8,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -488,4 +489,160 @@ func main() {
|
|||||||
for _, _ = range mnil {
|
for _, _ = range mnil {
|
||||||
panic("range mnil")
|
panic("range mnil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testfloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testfloat() {
|
||||||
|
// Test floating point numbers in maps.
|
||||||
|
// Two map keys refer to the same entry if the keys are ==.
|
||||||
|
// The special cases, then, are that +0 == -0 and that NaN != NaN.
|
||||||
|
|
||||||
|
{
|
||||||
|
var (
|
||||||
|
pz = float32(0)
|
||||||
|
nz = math.Float32frombits(1 << 31)
|
||||||
|
nana = float32(math.NaN())
|
||||||
|
nanb = math.Float32frombits(math.Float32bits(nana) ^ 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
m := map[float32]string{
|
||||||
|
pz: "+0",
|
||||||
|
nana: "NaN",
|
||||||
|
nanb: "NaN",
|
||||||
|
}
|
||||||
|
if m[pz] != "+0" {
|
||||||
|
fmt.Println("float32 map cannot read back m[+0]:", m[pz])
|
||||||
|
}
|
||||||
|
if m[nz] != "+0" {
|
||||||
|
fmt.Println("float32 map does not treat", pz, "and", nz, "as equal for read")
|
||||||
|
fmt.Println("float32 map does not treat -0 and +0 as equal for read")
|
||||||
|
}
|
||||||
|
m[nz] = "-0"
|
||||||
|
if m[pz] != "-0" {
|
||||||
|
fmt.Println("float32 map does not treat -0 and +0 as equal for write")
|
||||||
|
}
|
||||||
|
if _, ok := m[nana]; ok {
|
||||||
|
fmt.Println("float32 map allows NaN lookup (a)")
|
||||||
|
}
|
||||||
|
if _, ok := m[nanb]; ok {
|
||||||
|
fmt.Println("float32 map allows NaN lookup (b)")
|
||||||
|
}
|
||||||
|
if len(m) != 3 {
|
||||||
|
fmt.Println("float32 map should have 3 entries:", m)
|
||||||
|
}
|
||||||
|
m[nana] = "NaN"
|
||||||
|
m[nanb] = "NaN"
|
||||||
|
if len(m) != 5 {
|
||||||
|
fmt.Println("float32 map should have 5 entries:", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var (
|
||||||
|
pz = float64(0)
|
||||||
|
nz = math.Float64frombits(1 << 63)
|
||||||
|
nana = float64(math.NaN())
|
||||||
|
nanb = math.Float64frombits(math.Float64bits(nana) ^ 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
m := map[float64]string{
|
||||||
|
pz: "+0",
|
||||||
|
nana: "NaN",
|
||||||
|
nanb: "NaN",
|
||||||
|
}
|
||||||
|
if m[nz] != "+0" {
|
||||||
|
fmt.Println("float64 map does not treat -0 and +0 as equal for read")
|
||||||
|
}
|
||||||
|
m[nz] = "-0"
|
||||||
|
if m[pz] != "-0" {
|
||||||
|
fmt.Println("float64 map does not treat -0 and +0 as equal for write")
|
||||||
|
}
|
||||||
|
if _, ok := m[nana]; ok {
|
||||||
|
fmt.Println("float64 map allows NaN lookup (a)")
|
||||||
|
}
|
||||||
|
if _, ok := m[nanb]; ok {
|
||||||
|
fmt.Println("float64 map allows NaN lookup (b)")
|
||||||
|
}
|
||||||
|
if len(m) != 3 {
|
||||||
|
fmt.Println("float64 map should have 3 entries:", m)
|
||||||
|
}
|
||||||
|
m[nana] = "NaN"
|
||||||
|
m[nanb] = "NaN"
|
||||||
|
if len(m) != 5 {
|
||||||
|
fmt.Println("float64 map should have 5 entries:", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var (
|
||||||
|
pz = complex64(0)
|
||||||
|
nz = complex(0, math.Float32frombits(1<<31))
|
||||||
|
nana = complex(5, float32(math.NaN()))
|
||||||
|
nanb = complex(5, math.Float32frombits(math.Float32bits(float32(math.NaN()))^2))
|
||||||
|
)
|
||||||
|
|
||||||
|
m := map[complex64]string{
|
||||||
|
pz: "+0",
|
||||||
|
nana: "NaN",
|
||||||
|
nanb: "NaN",
|
||||||
|
}
|
||||||
|
if m[nz] != "+0" {
|
||||||
|
fmt.Println("complex64 map does not treat -0 and +0 as equal for read")
|
||||||
|
}
|
||||||
|
m[nz] = "-0"
|
||||||
|
if m[pz] != "-0" {
|
||||||
|
fmt.Println("complex64 map does not treat -0 and +0 as equal for write")
|
||||||
|
}
|
||||||
|
if _, ok := m[nana]; ok {
|
||||||
|
fmt.Println("complex64 map allows NaN lookup (a)")
|
||||||
|
}
|
||||||
|
if _, ok := m[nanb]; ok {
|
||||||
|
fmt.Println("complex64 map allows NaN lookup (b)")
|
||||||
|
}
|
||||||
|
if len(m) != 3 {
|
||||||
|
fmt.Println("complex64 map should have 3 entries:", m)
|
||||||
|
}
|
||||||
|
m[nana] = "NaN"
|
||||||
|
m[nanb] = "NaN"
|
||||||
|
if len(m) != 5 {
|
||||||
|
fmt.Println("complex64 map should have 5 entries:", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var (
|
||||||
|
pz = complex128(0)
|
||||||
|
nz = complex(0, math.Float64frombits(1<<63))
|
||||||
|
nana = complex(5, float64(math.NaN()))
|
||||||
|
nanb = complex(5, math.Float64frombits(math.Float64bits(float64(math.NaN()))^2))
|
||||||
|
)
|
||||||
|
|
||||||
|
m := map[complex128]string{
|
||||||
|
pz: "+0",
|
||||||
|
nana: "NaN",
|
||||||
|
nanb: "NaN",
|
||||||
|
}
|
||||||
|
if m[nz] != "+0" {
|
||||||
|
fmt.Println("complex128 map does not treat -0 and +0 as equal for read")
|
||||||
|
}
|
||||||
|
m[nz] = "-0"
|
||||||
|
if m[pz] != "-0" {
|
||||||
|
fmt.Println("complex128 map does not treat -0 and +0 as equal for write")
|
||||||
|
}
|
||||||
|
if _, ok := m[nana]; ok {
|
||||||
|
fmt.Println("complex128 map allows NaN lookup (a)")
|
||||||
|
}
|
||||||
|
if _, ok := m[nanb]; ok {
|
||||||
|
fmt.Println("complex128 map allows NaN lookup (b)")
|
||||||
|
}
|
||||||
|
if len(m) != 3 {
|
||||||
|
fmt.Println("complex128 map should have 3 entries:", m)
|
||||||
|
}
|
||||||
|
m[nana] = "NaN"
|
||||||
|
m[nanb] = "NaN"
|
||||||
|
if len(m) != 5 {
|
||||||
|
fmt.Println("complex128 map should have 5 entries:", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user