我一直在写一个大的数字库的实践原因在C和加法运算是给我带来了一些问题。它工作正常,但忽略了最后的进位,结果不正确。
我一直在尝试用c写一个大数字库,这里是到目前为止的加法操作代码。到目前为止的代码:BIG_INT
类型是typedef
,用于8字节的unsigned char
数组。8字节用于测试,之后将是512字节。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BIG_INT_SIZE 8
typedef unsigned char BIG_INT[BIG_INT_SIZE];
int add_big_int(BIG_INT *left, size_t right)
{
if (!right)
return 0;
char buffer[sizeof(size_t)];
memcpy(buffer, &right, sizeof(size_t));
unsigned int carry = 0;
for (int64_t i = BIG_INT_SIZE - 1; i >= 0; --i) {
unsigned int sum = (*left[i] & 0xff) + (buffer[i] & 0xff) + carry;
*left[i] = sum;
carry = sum >> 8;
}
printf("carry: %u\n", carry);
return 0;
}
int main()
{
BIG_INT big = { 0, 0, 0, 0, 0, 0, 0, 1 };
add_big_int(&big, 255);
return 0;
}
字符串
当添加像256 + 1 = 257
这样的数字时,字节数组是[0, 0, 0, 0, 0, 0, 1, 1]
(逆序),但是当添加255 + 1 = 256
时,字节数组是[0, 0, 0, 0, 0, 0, 0, 0]
而不是预期的[0, 0, 0, 0, 0, 0, 1, 0]
,进位变量被设置为1
。
2条答案
按热度按时间k10s72fa1#
代码是无序的,一些组织将帮助修复它。
看看这个循环:它使用
left[i]
和buffer[i]
迭代BIG_INT_SIZE
个元素。buffer
中有多少个元素?buffer
被声明为char buffer[sizeof(size_t)];
,因此,如果sizeof(size_t)
与BIG_INT_SIZE
不同,则循环被大量中断。您需要buffer
和left
具有相同数量的元素,或者需要循环中的代码来处理不同的大小。让我们使用
char buffer[BIG_INT_SIZE];
使它们的大小相同。接下来,比较这两个声明:
个字符
有什么不一样?一个使用
unsigned char
,另一个使用char
。C标准没有说明char
是有符号的还是无符号的。当你把255放在带符号的char
中时,它很可能取-1,尽管这是由实现定义的。您需要unsigned char
:型
接下来,您有了
memcpy(buffer, &right, sizeof(size_t));
,但我们需要考虑buffer
大小设置方式的变化。此外,memcpy
按照字节在内存中的任何顺序复制字节,但根据其他代码的判断,您希望低字节位于较高的地址。所以你可以做的是:型
第一个循环将
right
的低值字节放入buffer
的高地址元素,然后将right
的字节右移。第二个循环用零填充buffer
的任何其他元素。这里的一个潜在危险是
right
大于buffer
,这会使第一个循环溢出缓冲区。我们可以通过以下措施来防范这种情况:型
我在上面使用了
& 0xff
,模仿了您现有的代码。通常,人们会设计一个8位字节,在这种情况下,& 0xff
是不必要的;所述指派可仅为buffer[BIG_INT_SIZE - 1 - i] = right;
,且right
到unsigned char
的自动转换将有效地移除较高位。如果进行了该更改,则可能需要插入CHAR_BIT
为8的_Static_assert
。在<limits.h>
中定义了CHAR_BIT
。接下来,看一下
unsigned int sum = (*left[i] & 0xff) + (buffer[i] & 0xff) + carry;
。数组下标的优先级高于指针解引用,因此*left[i]
是*(left[i])
,而不是(*left)[i]
。left
是指向BIG_INT
的指针,并且只传递了一个BIG_INT
,因此不能在i
为非零的情况下使用left[i]
。您需要(*left)
来获取BIG_INT
,然后需要[i]
来从中选择一个元素,因此您需要unsigned int sum = ((*left)[i] & 0xff) + (buffer[i] & 0xff) + carry;
。同样,
*left[i] = sum;
应该是(*left)[i] = sum;
。进行这些更改后,您的代码将获得所显示的情况的正确答案。但是,您应该考虑其他设计方面,包括:
BIG_INT
定义为数组?作为包含数组的结构,它可能更安全。这会在更早的时候捕获*left[i]
错误吗?right
的类型是size_t
?size_t
有一个含义,即它与对象大小一起使用,而不是与大整数类型一起使用的任意算术值。是否要使用带符号的类型?int64_t
代替i
?这是固定长度的型别。您可能需要像ptrdiff_t
这样的类型,它适用于索引数组(它是有符号的,因此可以与i >= 0
测试一起使用,而且它是一种设计用于根据目标平台调整其宽度的类型)。carry
不为零时,您会怎么做?add_big_int
总是返回零吗,或者你有什么未来的计划吗?ie3xauqp2#
对于初学者来说,函数的第一个参数是指针
字符串
如果
BIG_INT
定义为型
则它意味着该参数具有类型
char ( * )[8]
。通过指向数组的指针传递数组没有多大意义。这样声明函数就足够了
型
就像
型
该函数始终返回
0
,该值对函数的用户没有任何信息。函数应该返回有意义的值,或者其返回类型应该是void
。使用类型
size_t
作为示例型
只会让代码的读者感到困惑。
你可以使用表达式
sizeof( BIG_INT )
,前提是数组BIG_INT
足够大,可以存储size_t
类型的值。使用的表达式
*left[i]
调用未定义的行为。相反,您必须使用表达式( *left )[i]
。但在任何情况下,使用带有位AND运算符的表达式,如型
没有效果,因为您有元素类型为
unsigned char
的数组。一般来说,由于使用进位标志,您应该使用包含在结构中的动态分配数组,因为可能会出现溢出。