Skip to content

Hashes of numbers with identical final digits match #143

Description

@ilyvion

This feels very problematic to me and is causing me to have to hash BigDecimal values using a different technique than their real Hash impl.

#[test]
fn big_decimal_hash_issue() {
    use bigdecimal::BigDecimal;
    use std::hash::{DefaultHasher, Hash, Hasher};

    let d1: BigDecimal = "1011".parse().unwrap();
    let d2: BigDecimal = "0.01011".parse().unwrap();

    let mut d1_hasher = DefaultHasher::new();
    let mut d2_hasher = DefaultHasher::new();

    d1.hash(&mut d1_hasher);
    d2.hash(&mut d2_hasher);

    let d1_hash = d1_hasher.finish();
    let d2_hash = d2_hasher.finish();

    assert_ne!(d1_hash, d2_hash, "{d1:?}, {d2:?}");
}

This test fails with

assertion `left != right` failed: BigDecimal(sign=Plus, scale=0, digits=[1011]), BigDecimal(sign=Plus, scale=5, digits=[1011])
  left: 1407011009942640606
 right: 1407011009942640606

You can change d1 and d2 to any two numbers that "end in" (or I guess, more accurately, have the significant digits) 1011, and you still get the panic, e.g. 10.11 or 0.00000001011 -- basically as long as digits=[1011], you'll get the panic.

It does not happen when you go in the other direction, like with 10110 because then you get scale=0, digits=[10110], which hashes differently. Unless you use .normalized() on them first; since you then get something like scale=-1, digits=[1011] instead, which once more gives an identical hash, (I was mistaken about that; while normalizing do give them identical digits, they still hash differently)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions