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.
Raised during review of #3230, where the numeric side of
RedisValueequality came up. Pre-existingbehaviour, not caused by that PR.
What happens
operator ==runsSimplify()on both sides, so text that parses as a number is compared as a number.Format.cs:178does that parse withNumberStyles.Any:NumberStyles.Anyallows thousands separators, parentheses for negatives, leading and trailing whitespace, acurrency symbol, and exponents. So:
"1,000" == "1000""(5)" == "-5"" 5 " == "5""1,0,0,0" == "1000""5." == "5""+5" == "5""1e2" == "100"Both sides are
String-typed throughout — these are ordinary string values, not numbers that happen to bespelled differently.
Every integer path in
Format.csuses the much tighterNumberStyles.Integer;TryParseDoubleis the onlyone 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 aHashSet<RedisValue>, or keying aDictionary<RedisValue, T>, silently merges them." 5 "and"5"are plainly different values by anyreading.
It is at least self-consistent:
GetHashCodesimplifies too, so equal values do share a hash and no hashcontainer 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 theseparators, 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 aseparate 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.