6

我有这个代码

#include <iostream>

using namespace std;

int main(int argc,char **argv) {

    unsigned long long num1 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999995LL;
    unsigned long long num2 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999996LL;
    unsigned long long num3 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997LL;
    unsigned long long num4 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998LL;
    unsigned long long num5 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999LL;

    cout << (unsigned long long)(num1 * num2 * num3 * num4 * num5) << endl;
    return 0;
}

如您所见,数字很大,但是当我在那里进行数学运算时,我得到了:18446744073709551496

在编译时,我收到以下警告:

warning: integer constant is too large for its type|
In function `int main(int, char**)':|
warning: this decimal constant is unsigned only in ISO C90|
...
4

8 回答 8

22

您的结果大于 long long 类型 - 您需要查看BigInteger或任意精度库,例如gmp

于 2008-10-27T18:08:00.190 回答
7

这些数字不适合任何 C++ 数据类型。如果您只想打印它们,请将数字存储在字符串中。如果您想对其进行数学运算,请找到一个任意精度的数学库并使用它。

于 2008-10-27T18:07:48.490 回答
3

如果您希望在代码中使用这么大的文字,则必须将它们作为字符串文字输入并将它们加载到某种 BigInt 类中。现在没有办法在源代码中表达这么大的整数文字(尽管 C++0x 有望解决这个不足)。

如果您使用的是BigInteger库,请查看用于从字符串构建大整数的stringToBigUnsigned函数。BigIntegerUtils.hh

#include "BigUnsigned.hh"
#include "BigIntegerUtils.hh"     

 BigUnsigned  num1 = stringToBigUnsigned (
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999995"
    );
于 2008-10-27T19:22:51.147 回答
3

你想做什么?您了解二进制和十进制数的基础知识吗?为什么 8 位只保存 0 到 255、12 位 0 到 4095 等值?保存您感兴趣的数字需要多少位?或者更好的是,您有兴趣创建多大的数字?您是否使用 9 来使数字更大?十六进制 0xF... 怎么样?如果您想要最大的无符号数(在标准整数类型之一内),为什么不:

unsigned long long a,b;

a = -1; //混合有符号和无符号似乎是错误的,但它是有效的,数字在存储之前转换为无符号

b = 0; b--; //做和上面一样的事情

你真的需要那个级别的精度吗?您意识到乘法可能需要两倍于每个操作数大小的结果吗?0xFF * 0xFF = 0xFE01,如果在这种情况下您使用的是 8 位整数,则无法进行数学运算。当您继续乘以 0xFF * 0xFF * 0xFF = 0xFD02FF 时,情况只会变得更糟。

想做什么?


看到你的回复:

我以前没有见过 8 号欧拉。听起来像是一个很好的面试问题,因为它只需要几行代码就可以解决。


您的其他回复:

数字...

可能是因为我们有 10 个手指(也许还有 10 个脚趾),所以我们以“10 基数”长大。我们的时钟大部分以 60 为基数,但它已与以 10 为基数混合,以使其更加混乱。无论如何,以 10 为底,意味着对于每个数字占位符,您有 10 个唯一数字之一,当您达到该位置的最大值时,您将滚动到下一个位置。这都是小学的东西。

000
001
002
003
...
008
009
010
011
012
...

看看最右边的数字如何有 10 个符号(0、1、2、3、4、5、6、7、8、9),当它到达最后一个符号时,它会重新开始,左边的数字增加一。此规则适用于所有基本编号系统。

除了只有两个符号 0 和 1 之外,对于底数 2 是正确的

000
001
010
011
100
101
...

八进制也是如此,但有 8 个符号 (0,1,2,3,4,5,6,7)

000
001
002
003
004
005
006
007
010
011
012
013
...

十六进制也是如此,16个符号(0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f)

000
001
002
003
004
005
006
007
008
009
00a
00b
00c
00d
00e
00f
010
011
012
013
...

我正要讨论在计算机中使用二进制而不是其他基数(如 10)的原因。底线很容易有两种状态开或关,或高和低。两种状态就像基数 2 中的两个符号 1 和 0。试图在可用电压范围内将电子设备调整到两个以上的状态是很困难的,至少以前是这样,将其保持在零伏附近或高于一些小伏特是相对容易,所以数字电子使用两种状态,二进制。

即使是一个简单的二进制任务也是冗长的,简单的二年级数学仍然有很多 1 和 0。因此八进制变得流行,因为它允许您以三位为一组进行思考,并且您可以使用我们熟悉的符号作为数字 0、1、2、3、4、5、6、7。但是四组是 2 的另一个幂,它比八进制赋予人类更多的心理计算能力,十六进制基于 4 位,这也是 2 的幂。我们必须在我们借用的 10 个符号中添加更多符号传统的阿拉伯文以 10 为基数,因此使用了字母表的前 6 个。八进制很少使用,如果他们认为八进制而不是十六进制,您可以告诉他们年龄。(我来自十六进制一代,但与八进制一代的人一起工作,因为他们无法在他们的脑海中从八进制到二进制再到十六进制)。

计算机中的以 10 为基数就像人类在十六进制中的平均思维。计算机不以 10 为基数(对于他们曾经使用 bcd 的懒人来说很好),它们以 2 为基数。计算机中的十进制数 1234 实际上是 0x4D2 或 0b010011010010。那是一个值,假设您要添加 1234 加上一些其他数字,您需要该值与符号 1、2、3 和 4 无关。但是要在 stackoverflow 上发布此答案,我们不使用我们的数字使用 ASCII,所以 ascii 中的 1234 是 0x31、0x32、0x33、0x34,假设 1000 位数字作为 ascii 字符串提供,这对于您的 euler 解决方案很重要,它必须是,否则您必须转换它从二进制到 ascii,因为问题是一个以 10 为底的问题,而不是根据定义以 2 为底的问题。

所以回到我问的问题。假设你有 4 位内存来存储一个数字,你能存储多大的数字?如果您认为仅以 10 为底,您可能会认为该数字是 9,因为您被训练认为在每个存储位置中使用最大的符号,如果您有 5 个以 10 为底的存储位置,则 99999 是最大的数字。回到四位但是,单个位的最大符号是 1,将该数字放入每个存储位置,您会得到 1111(四个 1)。只需查看这四个,您就应该能够在脑海中轻松看到相同数字 17 八进制或 F hex 的八进制和十六进制版本。要查看十进制需要数学,或者在这种情况下记忆,该数字是十进制的 15。所以你可以拥有的最大四位数是 0xF 或 15 而不是 9。8 位数呢?0xFF 或 255(2 的 8 次方减一)。最大的 16 位数字?65535等

所以当我问你要使用多少位时,这就是我的意思。看看这个数字 99999。再次以 10 为底,你会认为这是最大的数字,但对于计算机来说,它只是其中的一部分,十进制的 99999 是 0x1869F,它需要 17 位内存来存储,这是你可以存储的最大 17 位数字store 是 0x1FFFF,即 131071,比 99999 大一点。因此,当您想在计算机上考虑大数和数学时,您必须考虑二进制(或十六进制)。

最初你在做乘法,这仍然是欧拉问题的一部分,但我问的是与精度和位存储有关的问题。以下是一些基础知识,我不会深入探讨,但您可以了解为什么我们依赖计算机中的浮点单元。

取最大的 4 位数字 1111(二进制),即十进制的 15。加上最大的四位数,你得到 15+15 = 30 = 0x1E 或 11110 二进制。因此,要添加两个四位数字,您需要五个位来保存您的答案。计算机为这个额外的位保留一个“进位”位。本质上,计算机中的加/减整数数学函数允许您拥有 N+1 位。因此,如果它是一台 32 位计算机,则基本上有 33 位用于加/减数学。

问题是乘法和除法,即使在今天,许多处理器也不支持(是的,许多处理器没有 fpu,只做加减法,有时乘法,但除法很少见。乘法和除法需要很多电子设备,但要权衡的是你可以用软件中的加法和减法来做它们)。以四位系统的最坏情况乘法 1111 * 1111 = 11100001 因此需要 8 位来存储 4 位乘法的结果,你会很快发现,如果你有一个 4 位系统,你想要做的乘法的大部分将产生一个无法以 4 位存储的数字。因此,当我看到您采用 64 位整数(无符号 long long 通常为 64 位)并乘以四次时,这意味着您需要 64*5 或 320 位整数来存储您的答案,您试图将答案放入64 大结果,这很常见,

浮点数只不过是科学计数法,而是二进制,如果你想用科学计数法将数字 1234 和 5678 相乘,你需要 1.234*10^3 乘以 5.678*10^3 并得到 7.007*10^6。你保持你的精确度,并且能够代表更广泛的数字。我不会讨论它是如何在二进制中工作的。但这不适用于您的原始问题。

啊,最后一件事澄清我在我的问题/回复中做了什么。二进制中的负整数。由于加减法和基本系统之间的关系,您可以玩一些技巧。假设我想使用二进制从数字 7(十进制)中减去 1。好吧,没有减法电路之类的东西,而是添加一个负数,因此实际上不是 7 - 1,而是 7 + (-1),它会有所不同:

0111 + ???? = 0110

你可以将什么数字加到 7 以得到 6...二进制?

0111 + 1111 = 0110

二进制中的负数称为“二进制补码”,长话短说答案是“反转并加 1”。你如何用二进制表示负1?取加一个 0001 然后反转它意味着使一个为零和零一个(也称为补码)1110 然后加一个 1111。减一是计算机中的一个特殊数字(无处不在),无论你有多少位它表示为全1。所以当你看到有人这样做时:

无符号字符 a;

a = -1;

编译器首先查看 -1 并认为 ...11111(binary) 然后查看等号和另一边,哦,您希望 a 全部为 1,它看到您有一个有符号整数和一个无符号整数但转换只是将位移动,所以你在上面说你想要 a = 0xFF; (假设一个 8 位无符号字符)。

一些编译器可能会抱怨您试图将负数存储在无符号数中。其他编译器将查看 -1 并将其视为 32 位,或者这些天可能是 64 位有符号整数常量,然后当它将等于计算为 8 位无符号时,您将收到警告,您不能将 -1 存储在有符号中或无类型转换的无符号字符。但如果你这样做:

a = 0; 一种 - ;

所有编译器都会喜欢这样。并且不会抱怨,它只是在运行时而不是编译时消耗计算周期。

现在在某个地方,一位朋友告诉我一本连续进行二进制数学的书。例如,要否定一个数字,通常你会做反转和广告一个技巧,但用铅笔和纸有些人可能会告诉你另一个技巧。从右边开始复制零直到并包括第一个 1,然后反转,所以减去 2

0010
1110

从右边开始复制 0 然后是第一个,然后向左反转剩余的位。

负 6

0110
1010

负 4

0100
1100

据说有一些技巧可以做加法和减法(嗯,这些很容易),但也有乘法和除法。如果您连续执行它们,那么您可以使用相同的 alu 以二进制方式进行无限长的数学运算。如果您知道如何做到这一点,您可以在软件中实现它,并且您最初的乘以大常数的问题(假设保留所有精度)在任何计算机上都是微不足道的。

于 2008-10-28T00:58:12.780 回答
1

您得到的答案 18446744073709551496 是由于您的 999...9 在分配给 long long 时被截断,加上多个操作溢出。它是确定性的,但实际上只是位的随机集合。

于 2008-10-27T19:16:03.283 回答
0

unsigned int 表示系统字。今天,这个词的最大值将是 2^32 -1 或 2^64 - 1,这取决于您的系统是 32 位还是 64 位。你正在击中上限。

您必须编写一个 bignum 类或使用一个脱离网络的类。

你为什么要做这个问题呢?

于 2008-10-27T18:07:00.397 回答
0

这些数字不适合unsigned long long范围,因此您可以使用 GMP 库或使用字符串来表示大数字,就像我在计算 50 等数字的阶乘时所做的那样:

http://codepad.org/bkWNV0JC

#include <cmath>
#include <iostream>
using namespace std;
int main()
{
  unsigned int nd, nz;   
  unsigned char *ca;   
  unsigned int j, n=50, q, temp;
  int i;
  double p;
    p = 0.0;
    for(j = 2; j <= n; j++)
    {
      p += log10((double)j);  
    }
    nd = (int)p + 1;

    ca = new unsigned char[nd+1];
    if (!ca)
    {
      cout << "Could not allocate memory!!!";
      exit(0);
    }
    for (i = 1; (unsigned)i < nd; i++)
    {
      ca[i] = 0;
    }
    ca[0] = 1;

    p = 0.0;
    for (j = 2; j <= n; j++)
    {
      p += log10((double)j);   
      nz = (int)p + 1;        
      q = 0;                  
      for (i = 0;(unsigned) i <= nz; i++)
      {
        temp = (ca[i] * j) + q;
        q = (temp / 10);
        ca[i] = (char)(temp % 10);
      }
    }

    cout << "\nThe Factorial of " << n << " is: ";
    for( i = nd - 1; i >= 0; i--)
    {
      cout << (int)ca[i];
    }
  //  delete []ca;    
  return 0;
}
于 2012-09-17T11:16:39.303 回答
0

如果您可以使用 Boost,则可以尝试cpp_int。它可能比 GMP 慢一点,但它只是一个标头库。

#include <boost/multiprecision/cpp_int.hpp>
#include <iostream>

int main()
{
   using namespace boost::multiprecision;
// Repeat at arbitrary precision:
   cpp_int u = 1;
   for(unsigned i = 1; i <= 100; ++i)
      u *= i;

   // prints 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 (i.e. 100!)
   std::cout << u << std::endl;

   return 0;
}
于 2019-11-23T07:57:48.350 回答