处理大于 18446744073709551615 的数字的计算器
Calculators working with larger numbers than 18446744073709551615
当我用值 18446744073709551615 初始化一个 ulong,然后向它添加一个 1 并显示到控制台时,它显示一个完全符合预期的 0。
我知道这个问题听起来很愚蠢,但我不得不问。如果我的计算机是 64 位架构 CPU 我的计算器如何处理大于 18446744073709551615 的数字?
我想浮点数在这里有很多工作要做。
我想知道这是怎么发生的。
谢谢。
working with larger numbers than 18446744073709551615
“如果我的计算机具有 64 位架构 CPU”--> 架构位大小在很大程度上是无关紧要的。
考虑如何添加总和大于 9 的 2 个小数位。生成了一个 进位,然后在添加下一个最重要的小数位时使用。
CPU 可以做同样的事情,但使用基数 18446744073709551616 而不是基数 10。它使用 进位 位以及 符号 和 溢出 位来执行扩展数学运算。
I suppose floating-point has a lot to do here.
这与浮点无关。
;你说你正在使用 ulong
,这意味着你使用的是无符号 64-but 算术。因此,您可以存储的最大值是“全部”,对于 64 位 - 又名 UInt64.MaxValue
,正如您发现的那样:https://docs.microsoft.com/en-us/dotnet/api/system.uint64.maxvalue
如果您想存储任意大的数字:有相应的 API - 例如 BigInteger
。然而,任意大小的锥体都是有代价的,所以它不是默认的,当然也不是你在使用 ulong
(或 double
,或 decimal
等时得到的 -所有 compiler-level 数值类型都具有固定大小)。
所以:考虑使用 BigInteger
无论哪种方式,您都拥有 64 位架构处理器并且仅限于进行 64 位数学运算 - 如果不在 System.Numerics 命名空间中使用 BigInteger 解决这个问题,您的问题有点难以解释,例如,在 .NET Framework 4.8 中可用。基础是将'decompose'的数字转化为数组表示。
数学表达式 'decompose' 这里的意思是:
“将(数字或函数)表示为更简单组件的组合。”
BigInteger 在内部使用一个内部数组(实际上是多个内部结构)和一个名为 BigIntegerBuilder 的助手 class。 In 可以毫无问题地隐式转换 UInt64 整数,例如,对于更大的数字,您可以使用 + 运算符。
BigInteger bignum = new BigInteger(18446744073709551615);
bignum += 1;
您可以在此处阅读有关隐式运算符的信息:
https://referencesource.microsoft.com/#System.Numerics/System/Numerics/BigInteger.cs
public static BigInteger operator +(BigInteger left, BigInteger right)
{
left.AssertValid();
right.AssertValid();
if (right.IsZero) return left;
if (left.IsZero) return right;
int sign1 = +1;
int sign2 = +1;
BigIntegerBuilder reg1 = new BigIntegerBuilder(left, ref sign1);
BigIntegerBuilder reg2 = new BigIntegerBuilder(right, ref sign2);
if (sign1 == sign2)
reg1.Add(ref reg2);
else
reg1.Sub(ref sign1, ref reg2);
return reg1.GetInteger(sign1);
}
在上面来自ReferenceSource的代码中,您可以看到我们使用BigIntegerBuilder 来添加左右部分,这也是BigInteger 构造。
有趣的是,它似乎将其内部结构保留在一个名为“_bits”的私有数组中,所以这就是你问题的答案。 BigInteger 跟踪 32 位整数数组的数组,因此能够处理大整数,甚至超过 64 位。
您可以将此代码放入控制台应用程序或 Linqpad(它具有我在此处使用的 .Dump() 方法)并检查:
BigInteger bignum = new BigInteger(18446744073709551615);
bignum.GetType().GetField("_bits",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(bignum).Dump();
关于 BigInteger 的详细信息在 Reference Source 上 BigInteger 源代码的注释中有所揭示。因此,对于整数值,BigInteger 将值存储在 _sign 字段中,对于其他值,使用字段 _bits。
显然,内部数组需要能够转换为十进制(以 10 为底)的表示形式,以便人们可以阅读它,ToString() 方法将 BigInteger 转换为字符串表示形式。
为了更好地 in-depth 理解此处,请考虑执行 .NET 源代码单步执行以逐步进入代码,了解您如何执行此处的数学运算。但对于基本理解,BigInteger 使用内部表示,其由 32 位数组组成,该数组被转换为可读格式,允许更大的数字,甚至比 Int64 更大。
// For values int.MinValue < n <= int.MaxValue, the value is stored in sign
// and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits
当我用值 18446744073709551615 初始化一个 ulong,然后向它添加一个 1 并显示到控制台时,它显示一个完全符合预期的 0。 我知道这个问题听起来很愚蠢,但我不得不问。如果我的计算机是 64 位架构 CPU 我的计算器如何处理大于 18446744073709551615 的数字? 我想浮点数在这里有很多工作要做。 我想知道这是怎么发生的。
谢谢。
working with larger numbers than 18446744073709551615
“如果我的计算机具有 64 位架构 CPU”--> 架构位大小在很大程度上是无关紧要的。
考虑如何添加总和大于 9 的 2 个小数位。生成了一个 进位,然后在添加下一个最重要的小数位时使用。
CPU 可以做同样的事情,但使用基数 18446744073709551616 而不是基数 10。它使用 进位 位以及 符号 和 溢出 位来执行扩展数学运算。
I suppose floating-point has a lot to do here.
这与浮点无关。
;你说你正在使用 ulong
,这意味着你使用的是无符号 64-but 算术。因此,您可以存储的最大值是“全部”,对于 64 位 - 又名 UInt64.MaxValue
,正如您发现的那样:https://docs.microsoft.com/en-us/dotnet/api/system.uint64.maxvalue
如果您想存储任意大的数字:有相应的 API - 例如 BigInteger
。然而,任意大小的锥体都是有代价的,所以它不是默认的,当然也不是你在使用 ulong
(或 double
,或 decimal
等时得到的 -所有 compiler-level 数值类型都具有固定大小)。
所以:考虑使用 BigInteger
无论哪种方式,您都拥有 64 位架构处理器并且仅限于进行 64 位数学运算 - 如果不在 System.Numerics 命名空间中使用 BigInteger 解决这个问题,您的问题有点难以解释,例如,在 .NET Framework 4.8 中可用。基础是将'decompose'的数字转化为数组表示。 数学表达式 'decompose' 这里的意思是: “将(数字或函数)表示为更简单组件的组合。”
BigInteger 在内部使用一个内部数组(实际上是多个内部结构)和一个名为 BigIntegerBuilder 的助手 class。 In 可以毫无问题地隐式转换 UInt64 整数,例如,对于更大的数字,您可以使用 + 运算符。
BigInteger bignum = new BigInteger(18446744073709551615);
bignum += 1;
您可以在此处阅读有关隐式运算符的信息: https://referencesource.microsoft.com/#System.Numerics/System/Numerics/BigInteger.cs
public static BigInteger operator +(BigInteger left, BigInteger right)
{
left.AssertValid();
right.AssertValid();
if (right.IsZero) return left;
if (left.IsZero) return right;
int sign1 = +1;
int sign2 = +1;
BigIntegerBuilder reg1 = new BigIntegerBuilder(left, ref sign1);
BigIntegerBuilder reg2 = new BigIntegerBuilder(right, ref sign2);
if (sign1 == sign2)
reg1.Add(ref reg2);
else
reg1.Sub(ref sign1, ref reg2);
return reg1.GetInteger(sign1);
}
在上面来自ReferenceSource的代码中,您可以看到我们使用BigIntegerBuilder 来添加左右部分,这也是BigInteger 构造。
有趣的是,它似乎将其内部结构保留在一个名为“_bits”的私有数组中,所以这就是你问题的答案。 BigInteger 跟踪 32 位整数数组的数组,因此能够处理大整数,甚至超过 64 位。
您可以将此代码放入控制台应用程序或 Linqpad(它具有我在此处使用的 .Dump() 方法)并检查:
BigInteger bignum = new BigInteger(18446744073709551615);
bignum.GetType().GetField("_bits",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(bignum).Dump();
关于 BigInteger 的详细信息在 Reference Source 上 BigInteger 源代码的注释中有所揭示。因此,对于整数值,BigInteger 将值存储在 _sign 字段中,对于其他值,使用字段 _bits。
显然,内部数组需要能够转换为十进制(以 10 为底)的表示形式,以便人们可以阅读它,ToString() 方法将 BigInteger 转换为字符串表示形式。
为了更好地 in-depth 理解此处,请考虑执行 .NET 源代码单步执行以逐步进入代码,了解您如何执行此处的数学运算。但对于基本理解,BigInteger 使用内部表示,其由 32 位数组组成,该数组被转换为可读格式,允许更大的数字,甚至比 Int64 更大。
// For values int.MinValue < n <= int.MaxValue, the value is stored in sign
// and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits