Skip to content

RedisValue equality treats "1,000", "(5)" and " 5 " as numbers, via NumberStyles.Any #3233

Description

@mgravell

Raised during review of #3230, where the numeric side of RedisValue equality came up. Pre-existing
behaviour, not caused by that PR.

What happens

operator == runs Simplify() on both sides, so text that parses as a number is compared as a number.
Format.cs:178 does that parse with NumberStyles.Any:

return double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out value);

NumberStyles.Any allows thousands separators, parentheses for negatives, leading and trailing whitespace, a
currency symbol, and exponents. So:

comparison result storage
"1,000" == "1000" True String / String
"(5)" == "-5" True String / String
" 5 " == "5" True String / String
"1,0,0,0" == "1000" True String / String
"5." == "5" True String / String
"+5" == "5" True String / String
"1e2" == "100" True String / String

Both sides are String-typed throughout — these are ordinary string values, not numbers that happen to be
spelled differently.

Every integer path in Format.cs uses the much tighter NumberStyles.Integer; TryParseDouble is the only
one using Any, which makes it look more like an oversight than a decision.

Why it matters

These are distinct values on the server — distinct set members, distinct hash fields, distinct keys — that
compare equal in the client. Anything deduplicating RedisValues in a HashSet<RedisValue>, or keying a
Dictionary<RedisValue, T>, silently merges them. " 5 " and "5" are plainly different values by any
reading.

It is at least self-consistent: GetHashCode simplifies too, so equal values do share a hash and no hash
container is corrupted. The problem is that the equality is much wider than "numeric text".

Possible direction

NumberStyles.Float (leading/trailing whitespace, leading sign, decimal point, exponent) would drop the
separators, parentheses and currency symbol while keeping what Redis actually emits for numbers. Even that
leaves " 5 " == "5" true and "1e2" == "100" true, so the whitespace and exponent allowances are worth a
separate decision.

Any change here alters shipped comparison behaviour, so it wants its own discussion rather than being folded
into unrelated work — hence this issue rather than a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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