From the theoretical information representation, you have the values 2^64
to display in the values 2^63-1
.
Thus, the mapping is trivial with the module operator, since it always has a non-negative result:
y = 1 + x % 0x7fffffffffffffff;
It can be quite expensive, so what else is possible?
Simple math 2^64 = 2 * (2^63 - 1) + 2
says that we will have two source values for one target value, except for two special cases where three will go to one. Think of it as two special 64-bit values, name them x1
and x2
so that everyone shares the target with two other source values. In the mod
expression above, this happens by "wrapping". The target values y=2^31-2
and y=2^31-3
have three mappings. Everyone else has two. Since in any case, we should use something more complex than mod
, let's look for a way to match special values wherever we like, at a low cost
For illustration, let's work with matching a 4-bit signed int x
in [-8..7] to y
in [1..7], and not with 64-bit space.
An easy course is to have the values of x
in [1..7] for yourself, then the task is reduced to mapping x
in [-8..0] to y
in [1 .. 7]. Note that there are 9 initial values and only 7 goals, as discussed above.
There are many strategies. At this point, you are likely to see gazillion. I will describe only one that is especially simple.
Let y = 1 - x
for all values, except in special cases x1 == -8
and x2 == -7
. So the whole hash function becomes
y = x <= -7 ? S(x) : x <= 0 ? 1 - x : x;
Here S(x)
is a simple function that says where x1
and x2
displayed. Choose S
based on what you know about the data. For example, if you think that high target values are unlikely, compare them with 6 and 7 with S(x) = -1 - x
.
Final display:
-8: 7 -7: 6 -6: 7 -5: 6 -4: 5 -3: 4 -2: 3 -1: 2 0: 1 1: 1 2: 2 3: 3 4: 4 5: 5 6: 6 7: 7
Taking this logic up to 64-bit space, you will have
y = (x <= Long.MIN_VALUE + 1) ? -1 - x : x <= 0 ? 1 - x : x;
Many other settings are possible in this structure.