patterncModerate
BigInteger implementation in C, supporting addition and multiplication
Viewed 0 times
multiplicationadditionsupportingandimplementationbiginteger
Problem
I've implemented adding and multiplying arbitrary precision integers.
Also available as a gist.
```
#include
#include
#include
typedef struct {
int *data;
int size;
} bi;
bi *bi_new() {
bi *a = malloc(sizeof(bi));
return a;
}
bi bi_from_string(char a) {
bi *b = bi_new();
int skip = 0;
while(a[skip] == '0') { skip++; }
b->size = strlen(a) - skip;
if(b->size == 0) {
b->size++;
b->data = malloc(b->size * sizeof(int));
b->data[0] = 0;
} else {
b->data = malloc(b->size * sizeof(int));
int i;
for(i = 0; i size; i++) {
b->data[i] = a[skip + i] - '0';
}
}
return b;
}
char bi_to_string(bi a) {
char b = malloc(a->size sizeof(char));
int i;
for(i = 0; i size; i++) {
b[i] = a->data[i] + '0';
}
return b;
}
bi bi_add(bi a, bi *b) {
bi *c = bi_new();
// max possible size
c->size = (a->size > b->size ? a->size : b->size) + 1;
c->data = malloc(c->size * sizeof(int));
int i = a->size - 1, j = b->size - 1;
int k = c->size - 1;
int carry = 0, tmp;
while(i >= 0 || j >= 0 || carry > 0) {
if(i >= 0 && j >= 0) {
tmp = a->data[i] + b->data[j];
} else if(i >= 0) {
tmp = a->data[i];
} else if(j >= 0) {
tmp = b->data[j];
} else {
tmp = 0;
}
tmp += carry;
carry = tmp / 10;
c->data[k] = tmp % 10;
i--; j--; k--;
}
// this is definitely leaking memory
if(c->data[0] == 0) { c->size--; c->data++; }
return c;
}
bi bi_multiply(bi a, bi *b) {
bi *c = bi_new();
// max size
c->size = a->size + b->size;
c->data = malloc(c->size * sizeof(int));
{ int i; for(i = 0; i size; i++) { c->data[i] = 0; } }
int i = a->size - 1, j = b->size - 1, k = c->size - 1;
int carry = 0, tmp, push_left = 0;
while(i >= 0) {
k = c->size - 1 - push_left++;
Also available as a gist.
```
#include
#include
#include
typedef struct {
int *data;
int size;
} bi;
bi *bi_new() {
bi *a = malloc(sizeof(bi));
return a;
}
bi bi_from_string(char a) {
bi *b = bi_new();
int skip = 0;
while(a[skip] == '0') { skip++; }
b->size = strlen(a) - skip;
if(b->size == 0) {
b->size++;
b->data = malloc(b->size * sizeof(int));
b->data[0] = 0;
} else {
b->data = malloc(b->size * sizeof(int));
int i;
for(i = 0; i size; i++) {
b->data[i] = a[skip + i] - '0';
}
}
return b;
}
char bi_to_string(bi a) {
char b = malloc(a->size sizeof(char));
int i;
for(i = 0; i size; i++) {
b[i] = a->data[i] + '0';
}
return b;
}
bi bi_add(bi a, bi *b) {
bi *c = bi_new();
// max possible size
c->size = (a->size > b->size ? a->size : b->size) + 1;
c->data = malloc(c->size * sizeof(int));
int i = a->size - 1, j = b->size - 1;
int k = c->size - 1;
int carry = 0, tmp;
while(i >= 0 || j >= 0 || carry > 0) {
if(i >= 0 && j >= 0) {
tmp = a->data[i] + b->data[j];
} else if(i >= 0) {
tmp = a->data[i];
} else if(j >= 0) {
tmp = b->data[j];
} else {
tmp = 0;
}
tmp += carry;
carry = tmp / 10;
c->data[k] = tmp % 10;
i--; j--; k--;
}
// this is definitely leaking memory
if(c->data[0] == 0) { c->size--; c->data++; }
return c;
}
bi bi_multiply(bi a, bi *b) {
bi *c = bi_new();
// max size
c->size = a->size + b->size;
c->data = malloc(c->size * sizeof(int));
{ int i; for(i = 0; i size; i++) { c->data[i] = 0; } }
int i = a->size - 1, j = b->size - 1, k = c->size - 1;
int carry = 0, tmp, push_left = 0;
while(i >= 0) {
k = c->size - 1 - push_left++;
Solution
The good part is that without any real modifications, I can compile your code with lots of the extra compiler flags to detect errors:
Memory Leaking
You provide a
This is evident in the fact that nothing in your
Includes
When you start writing larger programs, you should try to break them up into a header file and an implementation file. Here, you
When you're doing this, you'll also want to surround your header file with an include guard.
Strings
Converting to a string with
A similar argument can be made for your add and multiply functions to take a 3rd parameter which is the result value.
NULL-Terminating Strings
You've been bitten by the oldest (C) bug in the book - not NULL-terminating your strings. It's an easy fix:
That fixes your first assert:
Naming
This is a minor thing. I know everything in C is generally terse, but the name
-Wall, -Wextra, -Werror, -pedantic under -std=c99. That's a good start. There are a few problems, though:Memory Leaking
You provide a
bi_new() function, but nothing to free any of the memory that you allocate. Any time you have something returning a pointer to a heap-allocated struct, you should probably provide a convenience function for deallocation.void bi_free(bi* a)
{
free(a->data);
free(a);
}This is evident in the fact that nothing in your
main function cleans up any of the memory you've allocated - thus leaking memory (until the OS kills your process and reclaims it, anyway). For the comments where state you're leaking memory, the fix is to simply modify only size and leave the actual data alone.Includes
When you start writing larger programs, you should try to break them up into a header file and an implementation file. Here, you
#include a .c file. This is very bad practice. It (probably) won't hurt you now, but if you ever move to C++, it can cause a lot of problems. Pull out all of your function prototypes and structs into a header file, and include that into the implementation.When you're doing this, you'll also want to surround your header file with an include guard.
Strings
Converting to a string with
char bi_to_string(bi a) works, but in C, it's generally better to take a char as a parameter into which you can put something, so int bi_to_string(bi a, char str). The reason for this is you dynamically (malloc) allocate a string which you then return. This places the onus on the programmer to remember to free it later. If they pass in a char , they are either already responsible for freeing it later, or can stack allocate it and have it cleaned up automatically. This does mean the user can pass in a string that is too short, however, either an assert(...) or returning an error_code (hence the int return) can catch that.A similar argument can be made for your add and multiply functions to take a 3rd parameter which is the result value.
NULL-Terminating Strings
You've been bitten by the oldest (C) bug in the book - not NULL-terminating your strings. It's an easy fix:
char *bi_to_string(bi *a)
{
//Remember to allocate an extra spot for the NULL terminator
//Also, you don't need a sizeof(char). The standard says it is guaranteed
//to be 1.
char *b = malloc(a->size + 1);
int i;
for(i = 0; i size; i++) {
b[i] = a->data[i] + '0';
}
b[a->size] = '\0'; //NULL terminate
return b;
}That fixes your first assert:
assert(strcmp(a, bi_to_string(bi_from_string(a))) == 0);. However, assert(strcmp(e, bi_to_string(bi_add(bi_from_string(c), bi_from_string(d)))) == 0); fails for me. I'd look into debugging that :)Naming
This is a minor thing. I know everything in C is generally terse, but the name
bi really isn't descriptive at all. Maybe big_int if you still want to be terse, but make it completely unambiguous as to what it is.Code Snippets
void bi_free(bi* a)
{
free(a->data);
free(a);
}char *bi_to_string(bi *a)
{
//Remember to allocate an extra spot for the NULL terminator
//Also, you don't need a sizeof(char). The standard says it is guaranteed
//to be 1.
char *b = malloc(a->size + 1);
int i;
for(i = 0; i < a->size; i++) {
b[i] = a->data[i] + '0';
}
b[a->size] = '\0'; //NULL terminate
return b;
}Context
StackExchange Code Review Q#20783, answer score: 15
Revisions (0)
No revisions yet.