我正在尝试学习C并且遇到无法使用真正的大数字(即100位,1000位等)。我知道存在可以执行此操作的库,但我想尝试自己实现它。
我只是想知道是否有人或者可以提供任意精度算术的非常详细,愚蠢的解释。
答案 0 :(得分:152)
答案 1 :(得分:8)
虽然重新发明轮子非常适合您的个人教育和学习,但它也是一项非常大的任务。我不想劝阻你作为一项重要的练习,也不是我自己完成的练习,但是你应该意识到大型课程所涉及的工作中存在微妙而复杂的问题。
例如,乘法。天真地,你可能会想到“小学生”的方法,即在一个数字之上写一个数字,然后在学校学习时进行长时间的乘法。例如:
123
x 34
-----
492
+ 3690
---------
4182
但这种方法非常慢(O(n ^ 2),n是位数)。相反,现代bignum包使用离散傅立叶变换或数值变换将其转换为基本上为O(n ln(n))的操作。
这只适用于整数。当你在某种类型的真实数字表示(log,sqrt,exp等)上进入更复杂的函数时,事情变得更加复杂。
如果您有一些理论背景,我强烈建议您阅读Yap的书的第一章"Fundamental Problems of Algorithmic Algebra"。如前所述,gmp bignum库是一个很好的库。对于实数,我使用了mpfr并喜欢它。
答案 2 :(得分:6)
不要重新发明轮子:它可能会变成方形!
使用经过试用和测试的第三方库,例如GNU MP。
答案 3 :(得分:4)
你的方式基本上和铅笔和纸一样......
malloc
和realloc
)通常,您将使用基本的计算单位
由您的架构决定。
二进制或十进制基数的选择取决于您对最大空间效率,人类可读性以及芯片上是否存在二进制编码十进制(BCD)数学支持的需求。
答案 4 :(得分:3)
你可以用高中数学水平做到这一点。虽然现实中使用了更先进的算法。例如,添加两个1024字节的数字:
unsigned char first[1024], second[1024], result[1025];
unsigned char carry = 0;
unsigned int sum = 0;
for(size_t i = 0; i < 1024; i++)
{
sum = first[i] + second[i] + carry;
carry = sum - 255;
}
如果要添加以处理最大值,结果必须更大one place
。看看这个:
9
+
9
----
18
如果你想学习,TTMath是一个很棒的图书馆。它是使用C ++构建的。上面的例子很愚蠢,但这就是加法和减法一般的做法!
关于这个主题的一个很好的参考是Computational complexity of mathematical operations。它告诉您要实现的每个操作需要多少空间。例如,如果您有两个N-digit
个数字,则需要2N digits
来存储乘法结果。
正如 Mitch 所说,实施起来并不是一件容易的事!如果您了解C ++,我建议您查看TTMath。
答案 5 :(得分:3)
最终参考之一(恕我直言)是Knuth的TAOCP第二卷。它解释了许多用于表示这些表示的数字和算术运算的算法。
@Book{Knuth:taocp:2,
author = {Knuth, Donald E.},
title = {The Art of Computer Programming},
volume = {2: Seminumerical Algorithms, second edition},
year = {1981},
publisher = {\Range{Addison}{Wesley}},
isbn = {0-201-03822-6},
}
答案 6 :(得分:1)
假设您希望自己编写一个大整数代码,这可能会非常简单,最近会做一些人(尽管在MATLAB中。)以下是我使用过的一些技巧:
我将每个十进制数字存储为一个双数字。这使得许多操作变得简单,尤其是输出。虽然它确实占用了比你想象的更多的存储空间,但这里的内存很便宜,如果你能有效地卷积一对矢量,它会使乘法变得非常有效。或者,你可以在一个double中存储几个十进制数字,但要注意那么进行乘法的卷积会导致非常大的数字上的数字问题。
单独存储符号位。
添加两个数字主要是添加数字,然后检查每一步的进位。
一对数字的乘法最好以卷积后跟一个进位步骤进行,至少如果你有一个快速卷积码,那么这就好了。
即使将数字存储为单个十进制数字的字符串,也可以进行除法(也就是mod / rem操作)以在结果中一次获得大约13个十进制数字。这比一次只能处理1位十进制数的除法效率要高得多。
要计算整数的整数次幂,请计算指数的二进制表示。然后使用重复的平方操作来根据需要计算功率。
许多操作(分解,素数测试等)将受益于powermod操作。也就是说,当你计算mod(a ^ p,N)时,在求幂的每一步减少结果mod N,其中p已经以二进制形式表示。不要先计算^ p,然后再尝试将其缩小为N.
答案 7 :(得分:0)
这是我在PHP中做的一个简单(天真)的例子。
我实现了“添加”和“乘法”,并将其用作指数示例。
http://adevsoft.com/simple-php-arbitrary-precision-integer-big-num-example/
代码片段
// Add two big integers
function ba($a, $b)
{
if( $a === "0" ) return $b;
else if( $b === "0") return $a;
$aa = str_split(strrev(strlen($a)>1?ltrim($a,"0"):$a), 9);
$bb = str_split(strrev(strlen($b)>1?ltrim($b,"0"):$b), 9);
$rr = Array();
$maxC = max(Array(count($aa), count($bb)));
$aa = array_pad(array_map("strrev", $aa),$maxC+1,"0");
$bb = array_pad(array_map("strrev", $bb),$maxC+1,"0");
for( $i=0; $i<=$maxC; $i++ )
{
$t = str_pad((string) ($aa[$i] + $bb[$i]), 9, "0", STR_PAD_LEFT);
if( strlen($t) > 9 )
{
$aa[$i+1] = ba($aa[$i+1], substr($t,0,1));
$t = substr($t, 1);
}
array_unshift($rr, $t);
}
return implode($rr);
}