为什么 Double.ToString(“#”) 和 BigInteger.ToString() 对相同的值产生不同的结果?

Why does Double.ToString("#") and BigInteger.ToString() produce different results for the same value?

提问人:Matthew Layton 提问时间:8/18/2022 更新时间:8/18/2022 访问量:61

问:

请考虑以下代码:

double d = double.MaxValue;
BigInteger bi = new(d);
Console.WriteLine(d.ToString("#"));
Console.WriteLine(bi.ToString());

这将产生以下结果:

179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368

此值的表示形式包含看似随机的数字,而表示形式包含 294 个尾随零。两者的长度相同。BigIntegerdouble

为什么会这样,那些看似随机的数字是什么?

C# 精度 BigInteger

评论


答:

3赞 Jeremy Lakeman 8/18/2022 #1

如果你看一下 的实现,你会看到内部表示存储在以 2 为基数的 2 中。double 的值被打包到这个数组的最后 3 个元素中;BigInteger(double)int[]_bits

// Populate the uints.
_bits = new uint[cu + 2];
_bits[cu + 1] = (uint)(man >> (cbit + kcbitUint));
_bits[cu] = unchecked((uint)(man >> cbit));
if (cbit > 0)
    _bits[cu - 1] = unchecked((uint)man) << (kcbitUint - cbit);
_sign = sign;

所有其他元素将初始化为零。给出形式的最终值,而不是 。任何较大的值在转换为以 10 为基数时可能看起来像随机噪声。_bits[0..cu -1]x * 2^kx * 10^kk

但在 base 16 中,答案是显而易见的;

new BigInteger(double.MaxValue).ToString("X") == "0FFFFFFFFFFFFF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

它是将以 10 为基数的有效数字四舍五入。double.MaxValue.ToString("#")