Skip to content

GEOHASH returns incorrect last character due to missing 52-bit truncation handling #1625

@sspeaks

Description

@sspeaks

GEOHASH returns incorrect last character due to missing 52-bit truncation handling

Problem

Garnet's GEOHASH command produces a different final character than Redis for certain coordinates. For example:

GEOADD mygeo 13.361389 38.115556 "Palermo"
GEOHASH mygeo Palermo
Server Result
Redis sqc8b49rny0
Garnet sqc8b49rnys

The first 10 characters match, but the 11th diverges.

Root Cause

A standard geohash string is 11 base32 characters, which requires 55 bits (11 × 5). Both Redis and Garnet store geo data as sorted se
t scores using 64-bit doubles, which only have 52 bits of significand — 3 bits short of what the final character needs.

Redis accounts for this in src/geo.c lines 921–925 by setti
ng the 11th character to '0', since there aren't enough stored bits to determine it:

if (i == 10) {
    /* We have just 52 bits, but the API used to output
     * an 11 bytes geohash. For compatibility we assume
     * zero. */
    idx = 0;
}

Garnet's GetGeoHashCode
doesn't have this special case — it shifts the remaining 2 bits into the high bits of a 5-bit index, so the last character ends up re
flecting bit positions that don't carry real precision:

for (var i = 0; i < chars.Length; i++)
{
    chars[i] = (char)base32Chars[(int)(hash >> (BitsOfPrecision - 5)) & 0x1F];
    hash <<= 5;
}

Why It Matters

Clients and applications that compare, cache, or index geohash strings will see mismatches against values produced by Redis or other
standard implementations. Since the 52-bit score can't fully determine the 11th character, outputting '0' — as Redis does — is the
established convention.

Suggested Fix

On the last iteration of GetGeoHashCode, set the index to 0 rather than reading the remaining bits:

for (var i = 0; i < chars.Length; i++)
{
    var idx = i < chars.Length - 1
        ? (int)(hash >> (BitsOfPrecision - 5)) & 0x1F
        : 0;
    chars[i] = (char)base32Chars[idx];
    hash <<= 5;
}

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions