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

Assignees

No one assigned

    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