Skip to content

Commit a73501b

Browse files
committed
py/objfloat: Add implementation of high-quality float hashing.
Disabled by default.
1 parent 816413e commit a73501b

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

py/mpconfig.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,12 @@ typedef double mp_float_t;
548548
#define MICROPY_PY_BUILTINS_COMPLEX (MICROPY_PY_BUILTINS_FLOAT)
549549
#endif
550550

551+
// Whether to provide a high-quality hash for float and complex numbers.
552+
// Otherwise the default is a very simple but correct hashing function.
553+
#ifndef MICROPY_FLOAT_HIGH_QUALITY_HASH
554+
#define MICROPY_FLOAT_HIGH_QUALITY_HASH (0)
555+
#endif
556+
551557
// Enable features which improve CPython compatibility
552558
// but may lead to more code size/memory usage.
553559
// TODO: Originally intended as generic category to not

py/obj.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,11 @@ void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t s
730730

731731
#if MICROPY_PY_BUILTINS_FLOAT
732732
// float
733+
#if MICROPY_FLOAT_HIGH_QUALITY_HASH
734+
mp_int_t mp_float_hash(mp_float_t val);
735+
#else
733736
static inline mp_int_t mp_float_hash(mp_float_t val) { return (mp_int_t)val; }
737+
#endif
734738
mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs); // can return MP_OBJ_NULL if op not supported
735739

736740
// complex

py/objfloat.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,55 @@ const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, M_PI};
5959

6060
#endif
6161

62+
#if MICROPY_FLOAT_HIGH_QUALITY_HASH
63+
// must return actual integer value if it fits in mp_int_t
64+
mp_int_t mp_float_hash(mp_float_t src) {
65+
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
66+
typedef uint64_t mp_float_uint_t;
67+
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
68+
typedef uint32_t mp_float_uint_t;
69+
#endif
70+
union {
71+
mp_float_t f;
72+
#if MP_ENDIANNESS_LITTLE
73+
struct { mp_float_uint_t frc:MP_FLOAT_FRAC_BITS, exp:MP_FLOAT_EXP_BITS, sgn:1; } p;
74+
#else
75+
struct { mp_float_uint_t sgn:1, exp:MP_FLOAT_EXP_BITS, frc:MP_FLOAT_FRAC_BITS; } p;
76+
#endif
77+
mp_float_uint_t i;
78+
} u = {.f = src};
79+
80+
mp_int_t val;
81+
const int adj_exp = (int)u.p.exp - MP_FLOAT_EXP_BIAS;
82+
if (adj_exp < 0) {
83+
// value < 1; must be sure to handle 0.0 correctly (ie return 0)
84+
val = u.i;
85+
} else {
86+
// if adj_exp is max then: u.p.frc==0 indicates inf, else NaN
87+
// else: 1 <= value
88+
mp_float_uint_t frc = u.p.frc | ((mp_float_uint_t)1 << MP_FLOAT_FRAC_BITS);
89+
90+
if (adj_exp <= MP_FLOAT_FRAC_BITS) {
91+
// number may have a fraction; xor the integer part with the fractional part
92+
val = (frc >> (MP_FLOAT_FRAC_BITS - adj_exp))
93+
^ (frc & ((1 << (MP_FLOAT_FRAC_BITS - adj_exp)) - 1));
94+
} else if ((unsigned int)adj_exp < BITS_PER_BYTE * sizeof(mp_int_t) - 1) {
95+
// the number is a (big) whole integer and will fit in val's signed-width
96+
val = (mp_int_t)frc << (adj_exp - MP_FLOAT_FRAC_BITS);
97+
} else {
98+
// integer part will overflow val's width so just use what bits we can
99+
val = frc;
100+
}
101+
}
102+
103+
if (u.p.sgn) {
104+
val = -val;
105+
}
106+
107+
return val;
108+
}
109+
#endif
110+
62111
STATIC void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
63112
(void)kind;
64113
mp_float_t o_val = mp_obj_float_get(o_in);

0 commit comments

Comments
 (0)