StackExchange / StackExchange.Redis

General purpose redis client
https://stackexchange.github.io/StackExchange.Redis/
Other
5.88k stars 1.51k forks source link

StackExchange.Redis.Format Double digits question #1695

Open oneheed opened 3 years ago

oneheed commented 3 years ago

I am using this function and the result will always be unexpected

double StringIncrement(RedisKey key, double value, CommandFlags flags = CommandFlags.None)

var stringIncrement= StringIncrement("StringIncrement", 2.2) // 2.2000000000000002

So I went to check the problem, it might be double 15 digits but when outputting 17 digits

var redisValue= (RedisValue)2.2;
var result = redisValue.ToString(); // 2.2000000000000002

"StackExchange.Redis.Format" ToString(double value)

internal static string ToString(double value)
{
    if (double.IsInfinity(value))
    {
        if (double.IsPositiveInfinity(value)) return "+inf";
        if (double.IsNegativeInfinity(value)) return "-inf";
    }
    return value.ToString("G17", NumberFormatInfo.InvariantInfo);  <= this
}
mgravell commented 3 years ago

This sounds like a classic IEEE question. Most floating point numbers (like 2.2) do not exist, so... it never was 2.2.

I haven't checked for this exact value, but my guess is: that

oneheed commented 3 years ago

Can it be solved if the digits is adjusted?

NickCraver commented 2 years ago

FWIW this seems to be an issue specifically with .ToString(), as a quick test I tried before/after/after conversions:

var d = ((double)2.2);
d.ToString().Dump();
var rv = ((RedisValue)d);
rv.ToString().Dump();
var d2 = ((double)rv);
d2.ToString().Dump();

Which yields:

2.2
2.2000000000000002
2.2

So the actual double output is fine, but the .ToString() directly off RedisValue specifically differs. That implementation is a string cast and indeed ((string)rv).Dump(); == "2.2000000000000002". Ultimately this is calling Format.ToString(double) which is:

internal static string ToString(double value)
{
    if (double.IsInfinity(value))
    {
        if (double.IsPositiveInfinity(value)) return "+inf";
        if (double.IsNegativeInfinity(value)) return "-inf";
    }
    return value.ToString("G17", NumberFormatInfo.InvariantInfo);
}

@mgravell is it possible we should be using a slightly different format here to match? I don't think it's a major issue since it's just string, just wanted to see what's up here.