-1
#include <stdio.h>
#include <math.h>
int main(void) {
    int a[100], carry,i,j=0,length=0,temp,leftcarry=0,l,n;
    clrscr();
    scanf("%d",&n);
    for(i=0;i<100;i++) a[i]=0;
    for(i=1;i<=n;i++){
        a[j]+=i;
        while(a[j]>=10){
            carry=a[j]%10;
            if(a[j]>=pow(10,(j+1))){
                if(leftcarry==0){
                    a[j]=a[j]/10;
                    j++;
                    a[j]+=carry;
                    if(j>length)length=j;
                }
                else{
                    for(l=j+1;l<=length;l++){
                        temp=a[l+1];
                        a[l+1]=a[l];
                        a[l+2]=temp;
                    }
                    a[j+1]=carry;
                    leftcarry=0;
                    length=length+1;
                }
            }
            else{
                a[j]=a[j]/10;
                a[j-1]+=a[j];
                a[j]=carry;
                j--;
                if(a[j]>=10) leftcarry=1;
            }
           }
           j=length;
    }
    for(i=0;i<=length;i++){
        printf("%d",a[i]);
    }
    return 0;
}

I wanted to get some experience handling big integers using arrays, so I wrote this code to find sum of first n natural numbers. I got the right answer for given number<45. But for given number>=45, I get the answer which is less than the correct answer by 2. I would like to know why it is so. And I would also like to know of other simpler methods of handling big integers. Thank you.

Edit: Thank you all for answering. The question is now solved.

11
  • this looks overly complicated Commented Jan 9, 2015 at 6:31
  • sum(9) = 45. So maybe the fault is then you add with a number with more than 1 digit Commented Jan 9, 2015 at 6:34
  • It does look complicated, I know. Probably not efficient at all. I'm just learning though. And that wasn't what I meant. I can get it right up to sum(44). Commented Jan 9, 2015 at 6:41
  • I got misled by the printing code; it produces an answer, and the answer is 'correct', but it is correct because a[0] contains the complete sum, not because there are a number of digits. Ignore my previous comments — which I'm about to delete. Commented Jan 9, 2015 at 7:14
  • 2
    Why so many down votes people? Seems harsh. He's had a reasonable go and described the condition that falls and expected results. Commented Jan 9, 2015 at 8:44

3 Answers 3

1

I think this line is an error:

    if(a[j]>=pow(10,(j+1))){

I don't know if it's the error or the only error.

This says 'If the j-placed digit is greater than or equal to 10^(j+1)'.

I think the carry test is just 'is the j-place digit greater than or equal to 10.

The 'order' of that digit is identified by it's place. It's sometimes called place value notation! It's as though the 'digit' in the 'tens' column can go up to 99 and the 'digit' in the thousands column can go up to 999. I don't think that's what you want.

{999,99,9} isn't a decimal number! That ought to be {9,9,9,9,9,9}.

As others have also suggested I very strongly suggest you implement a little-endian scheme where the least significant digit is at the start of the array.

Then it becomes a whole load easier because you don't need all that shuffling down code to make space.

Then the algorithm as you've implemented becomes:

  1. Add i to the units (which are housed at a[0]).
  2. If a[0]>9, overflow! Take the 'excess' and iteratively move up the number (up the array) adding the excess (divided by 10) looking for further overflow at each step.

You should keep track of the 'order' of the number (largest i for which a[i]!=0) to detect overflow of the fixed-length array.

Your next challenge is to write a function int add_small(int a[100],int d,int p){}. That adds the number d*10^p to a where 0<=d<=10 and 0

After that int add_big(int a[100],int b[100]) calling add_small in a loop.

In case it helps your Googling (and you don't already know) what you're doing here is called 'binary coded decimal'. It's a very natural way to translate base 10 into a computer program but not (in fact) a very efficient way to handle big integer.

It's considered a bit hardcore but you should refer to The Art of Computer Programming Vol. 1 Chapter 4.3.1 'The Classical Algorithms'. If you're not a computer science undergraduate you're allowed to give that a miss. If you are a computer science undergraduate you're obliged to go to the library immediately and read it.

EDIT: I just looked at your profile. "CSE Undergraduate Student, First Year". Library it is.

EDIT: Hints if you insist on your heretical big-endian implementation!

  1. Set j=length-1
  2. Set a[j]+=i (the addition)
  3. If a[j]>=10 divide it by 10 and take the remainder.
  4. Set the remainder in a[j]
  5. If j is zero, go to Full Overflow!
  6. Otherwise, reduce j and add the division result in step 3 to a[j] and go back to step 3 again.
  7. Finished!
  8. Full Overflow: If j reached zero, shuffle everything up a slot, increment length and put the underflow division from step 3 in to a[0] like step 3.

Given your implementation you might full underflow multiple times so that needs to be a loop.

I would say that there's a structural error in your program more common for novices than experienced programmers. You're just trying to do too much in one loop. Divide and conquer! That's how we solve computing problems. Divide you function into an ordinary addition loop and then this shuffling loop that lays the overflowing digits out at the end of the number.

As I keep pointing out it would be simpler if you were little-endian!

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for the time and the support. Appreciate it. As for 'if(a[j]>=pow(10,(j+1))){' there is also an else block.. What I think the if-else do here is determine whether it is a left-side carry, meaning the normal carry from units to tens place and so on, or if it is the transition from a 1 digit number to a 2 digit number, or from 2 to 3 or so on.. i.e., a new digit is being introduced into the number. I found errors when that statement was not present.
As for making the last element the units place, I find trouble in checking both the present place and the carry simultaneously for being greater than 10. Maybe I should have a for loop in place of the while? I'm working it out, and will hopefully be able to get it soon. But what is frustrating is that the present code works fine till the number 44, by which time it has already done most of the steps (answer is 990) and then on, gives me an answer which is two less than the correct one, consistently.
As for the advice on the algorithms, I have bought a book, and am giving it a go, but as a beginner, it is a bit hard to follow. I'll stay at it though. Thanks :)
@GaneshPg TAoCP by Knuth is actually at various levels. Some of it is elementary some of it is unsolved research challenges! The section on 'The Classical Algorithms' is actually reasonably accessible. Don't be put off and try that after whatever it is you bought.
That was strangely simple. Thanks a lot. I feel like a fool indeed. Hopefully, I'll get the hang of things. Divide and conquer hasn't really sunk in, yet. Still got some getting used to do.
|
1

This is the code I wrote to reimplement the solution, using the 'litle-endian' approach with a[0] containing the units digit, a[1] containing the tens digit, etc.

#include <stdio.h>

int main(void)
{
    int a[100];
    int length = 0;
    int n;
    if (scanf("%d", &n) != 1 || n < 0)
        return 1;
    for (int i = 0; i < 100; i++)
        a[i] = 0;
    for (int i = 1; i <= n; i++)
    {
        int carry = 0;
        int number = i;
        int j;
        for (j = 0; carry > 0 || number > 0; j++)
        {
            int digit = number % 10;
            number /= 10;
            a[j] += digit + carry;
            carry = 0;
            if (a[j] >= 10)
            {
                a[j] -= 10;
                carry = 1;
            }
        }
        if (j > length)
            length = j;
    }
    printf("%d   ", n);
    for (int i = length; i > 0; i--)
        printf("%d", a[i-1]);
    long l = n;
    printf("      %ld\n", (l * (l + 1)) / 2);
    return 0;
}

Its output is the input value, the series of digits printed from the 'big number' array, and the result of the formula as a direct calculation (since ∑x = n·(n+1)÷2, for x in 1..n). I tested it with variations on this script:

$ for i in $(range 40 50); do bn <<< $i; done
'bn' is up to date.
40   820      820
41   861      861
42   903      903
43   946      946
44   990      990
45   1035      1035
46   1081      1081
47   1128      1128
48   1176      1176
49   1225      1225
50   1275      1275
$

And then more comprehensively with variations on this script:

$ for i in $(random -n 10 100000 999999 | sort -n)
> do
>     bn <<< $i
> done |
> awk '{ print; if ($2 != $3) print "BUG: " $1 "  --  " $2 " != " $3 }'
291478   42479857981      42479857981
393029   77236093935      77236093935
396871   78753493756      78753493756
490344   120218864340      120218864340
577519   166764386440      166764386440
580196   168313989306      168313989306
640090   204857924095      204857924095
876878   384457951881      384457951881
892825   398568686725      398568686725
974712   475032228828      475032228828
$

I actually used a repeat of 1000 instead of just 10, and the ranges moved up 10,000..99,999 and then 100,000..999,999; prior to that, I'd done similar proving with lower ranges and sequential numbers.

And I extended the tested range upwards:

$ for i in $(random -n 10 1000000 9999999 | sort -n); do bn <<< $i; done | awk '{ print; if ($2 != $3) print "BUG: " $1 "  --  " $2 " != " $3 }'
1291994   834624894015      834624894015
2032157   2064832052403      2064832052403
2266405   2568296945215      2568296945215
3187934   5081463188145      5081463188145
6045841   18276099721561      18276099721561
7248630   26271322062765      26271322062765
8604056   37014894127596      37014894127596
9095266   41361936353011      41361936353011
9533328   45442176144456      45442176144456
9543073   45535125913201      45535125913201
$ for i in $(random -n 10 10000000 99999999 | sort -n); do bn <<< $i; done | awk '{ print; if ($2 != $3) print "BUG: " $1 "  --  " $2 " != " $3 }'
11451834   65572256707695      65572256707695
44931846   1009435414949781      1009435414949781
55847914   1559494776999655      1559494776999655
72229304   2608536214276860      2608536214276860
81242212   3300148545947578      3300148545947578
88702606   3934076199946921      3934076199946921
89386055   3994933458924540      3994933458924540
93246667   4347470499927778      4347470499927778
95651750   4574628686857125      4574628686857125
97417038   4745039695055241      4745039695055241
$

(And yes, while I was testing early versions of the code, I did get some broken outputs.)

7 Comments

I would keep length=j+1 for consistency & add a bounds check for exceeding a[100].
@DanAllen: With the code as it stands, there's no point in checking the array bounds; the maximum value of n is 2^31-1, or 10 digits (assuming int is a 32-bit value, of course) and hence the sum doesn't get beyond 21 digits. I'm not sure about length = j + 1; — I don't immediately see an advantage to that.
I realised the array bounds are safe but I think examples on SO should exhibit best practice. My only point about 'length' is that the overwhelming convention is that length is (well) the length and you have it as maximum index (one under). It's a maintenance bug waiting to happen. Again I'm just pointing out best practice. What you've done is fine.
On bounds: if the array is bigger than could be needed, there's no need to check bounds. There's no need for such an exorbitantly oversize array (wasting 240-odd bytes), but there's no need to check when the code can't overflow. As to length, I believe it already is a length — one larger than the maximum valid index. See the printing loop: for (int i = length; i > 0; i--) printf("%d", a[i-1]); which first prints a[length-1] and works down to a[0]. Isn't that what you're asking for — and have, in fact, already got? There might be a problem with printing 0 if that's the input.
Saw all that. This guy is real novice. I still think examples on SO should exhibit best practice. You could just add one to length at the end of the loop if you're begrudging the in-loop Ops. Call it maxj if you're really bothered. What it isn't is a length.
|
0

Simple implementation.
(this small one cell is to ensure that the carry happen.)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

//range of  one cell  represent : 00-99
#define Base 100
#define Width 2
#define PRN PRIu8
typedef struct _unums {
    size_t size;
    uint8_t *nums;
} UNums;

void UNums_init(UNums *num){
    num->nums = malloc(sizeof(*num->nums));
    num->nums[0] = 0;
    num->size = 1;
}

void UNums_print(UNums *num){
    size_t i = num->size;
    int w = 0;
    do{
        --i;
        printf("%0*" PRN, w, num->nums[i]);
        if(!w) w = Width;
    }while(i!=0);
}

void UNum_drop(UNums *num){
    free(num->nums);
    num->nums = NULL;
}

//num += n. (n + num->nums[0] <= UINT_MAX) to work properly.
void UNums_add1(UNums *num, unsigned n){
    unsigned carry = n, wk;
    size_t i;

    for(i=0;i<num->size;++i){
        wk = num->nums[i] + carry;
        num->nums[i] = wk % Base;
        carry = wk / Base;
    }
    while(carry){
        num->size += 1;
        num->nums = realloc(num->nums, num->size * sizeof(*num->nums));
        num->nums[i++] = carry % Base;
        carry /= Base;
    }
}

int main(void){
    UNums num;
    unsigned i, n;

    UNums_init(&num);
    scanf("%u", &n);
    for(i=1;i<=n;++i)
        UNums_add1(&num, i);
    UNums_print(&num);
    UNum_drop(&num);
    return 0;
}

1 Comment

A little too hard for me to follow. I'm just a beginner. Appreciate you taking the time though. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.