Correct hash behavior for floating point numbers
This fixes HashMap where a key or part of a key is a floating point number. To fix this the following has been done: * HashMap now takes an extra template argument Comparator. This class gets used to compare keys. The default Comperator now works correctly for common types and floating point numbets. * Variant implements ::hash_compare() now. This function implements nan-safe comparison for all types with components that contain floating point numbers. * Variant now has a VariantComparator which uses Variant::hash_compare() safely compare floating point components of variant's types. * The hash functions for floating point numbers will now normalize NaN values so that all floating point numbers that are NaN hash to the same value. C++ module writers that want to use HashMap internally in their modules can now also safeguard against this crash by defining their on Comperator class that safely compares their types. GDScript users, or writers of modules that don't use HashMap internally in their modules don't need to do anything. This fixes #7354 and fixes #6947.
This commit is contained in:
@ -29,7 +29,8 @@
|
||||
#ifndef HASHFUNCS_H
|
||||
#define HASHFUNCS_H
|
||||
|
||||
|
||||
#include "math_funcs.h"
|
||||
#include "math_defs.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
/**
|
||||
@ -69,19 +70,52 @@ static inline uint32_t hash_djb2_one_32(uint32_t p_in,uint32_t p_prev=5381) {
|
||||
return ((p_prev<<5)+p_prev)+p_in;
|
||||
}
|
||||
|
||||
static inline uint32_t hash_one_uint64(const uint64_t p_int) {
|
||||
uint64_t v=p_int;
|
||||
v = (~v) + (v << 18); // v = (v << 18) - v - 1;
|
||||
v = v ^ (v >> 31);
|
||||
v = v * 21; // v = (v + (v << 2)) + (v << 4);
|
||||
v = v ^ (v >> 11);
|
||||
v = v + (v << 6);
|
||||
v = v ^ (v >> 22);
|
||||
return (int) v;
|
||||
}
|
||||
|
||||
static inline uint32_t hash_djb2_one_float(float p_in,uint32_t p_prev=5381) {
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
|
||||
// handle -0 case
|
||||
if (p_in==0.0f) u.f=0.0f;
|
||||
else u.f=p_in;
|
||||
// Normalize +/- 0.0 and NaN values so they hash the same.
|
||||
if (p_in==0.0f)
|
||||
u.f=0.0;
|
||||
else if (Math::is_nan(p_in))
|
||||
u.f=Math_NAN;
|
||||
else
|
||||
u.f=p_in;
|
||||
|
||||
return ((p_prev<<5)+p_prev)+u.i;
|
||||
}
|
||||
|
||||
// Overload for real_t size changes
|
||||
static inline uint32_t hash_djb2_one_float(double p_in,uint32_t p_prev=5381) {
|
||||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} u;
|
||||
|
||||
// Normalize +/- 0.0 and NaN values so they hash the same.
|
||||
if (p_in==0.0f)
|
||||
u.d=0.0;
|
||||
else if (Math::is_nan(p_in))
|
||||
u.d=Math_NAN;
|
||||
else
|
||||
u.d=p_in;
|
||||
|
||||
return ((p_prev<<5)+p_prev) + hash_one_uint64(u.i);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static inline uint32_t make_uint32_t(T p_in) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user