0

I'm trying to work on addition of about 25 digit numbers in C. The result I am getting is a little different from the expected, possible cause datatype.

/* Online C Compiler and Editor */
#include <stdio.h>

int main()
{
    long double result;
    long double a;
    long double b;
    a = 51680708854858333333;
    b = 83621143489848333333,
    result = a + b;
    printf("Hello, World!\n");
    printf("can be written %.0Lf\n", result);

    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 5
    If you would do it using pen and paper, how would you do it then? You could basically do the exact same thing in your program. – Some programmer dude Jun 27 '23 at 10:35
  • 4
    Your question is tagged with `biginteger`, but the actual types in your code are all floating point types. Can you clarify what it is you actually need, as well as the output you get and how it differs from what you expected. – wohlstad Jun 27 '23 at 10:39
  • 1
    You wrote "The result am getting is a little different from the expected". Please [edit] your question and copy&paste the actual output and show the expected result. – Bodo Jun 27 '23 at 10:40
  • 1
    If you're using floating point numbers be aware of approximations. If you need a correct calculation use a BIGNUM library, in case using fixed point technique. – Frankie_C Jun 27 '23 at 10:41
  • 2
    @wohlstad: *the actual types in your code are all floating point types* not even: `51680708854858333333` and `83621143489848333333` are integers, not floating point constants. – chqrlie Jun 27 '23 at 10:42
  • @Kester George, Compiler is going beyond minimum standard C as `51680708854858333333` is outside most compiler's integer range. – chux - Reinstate Monica Jun 27 '23 at 10:43
  • @chqrlie sure. I meant the types of the variables used for the calculation. I thought they represent the problem the OP is facing. – wohlstad Jun 27 '23 at 10:43
  • @Kester George, " addition of about 25 digit numbers in C" --> how are these numbers determined in the real application? By user input or by recoding the source code each time, or what? – chux - Reinstate Monica Jun 27 '23 at 10:46
  • 1
    You could use a library that supports big integers, e.g. [The GNU Multiple Precision Arithmetic Library](https://gmplib.org/) – Bodo Jun 27 '23 at 10:50
  • Does this answer your question? [Adding large integer values in C](https://stackoverflow.com/questions/61120762/adding-large-integer-values-in-c) – phuclv Jun 27 '23 at 10:56
  • Just use __int128 integer type. To put constants in them of that size, you have to use longs. e.g. bg = c1 << 64 + c2. – Simon Goater Jun 27 '23 at 11:58

2 Answers2

6

You can do it the same way taught in elementary school:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/*  Add two numbers represented by strings of digit characters.  The result is
    returned in dynamically allocated memory, which the caller is responsible
    for freeing.  The strings are not tested to ensure they contain only
    digits.

    The result may contain a leading zero.  This could be eliminated by testing
    after the addition, shifting all the characters left one space, and
    reallocating if desired.
*/
static char *add(const char *a, const char *b)
{
    //  Measure lengths of strings.
    size_t la = strlen(a);
    size_t lb = strlen(b);

    //  Plan space for result, allowing an extra character in case of carry.
    size_t lc = (la < lb ? lb : la) + 1;

    //  Allocate space for result digits plus a terminating null character.
    char *c = malloc(lc+1);
    if (!c)
    {
        fprintf(stderr, "Error, unable to allocate %zu bytes for sum.\n", lc+1);
        exit(EXIT_FAILURE);
    }
    c[lc] = '\0';

    /*  Add digits from right to left.  i counts positions from the right of
        the numerals.
    */
    int carry = 0;
    for (size_t i = 0; i < lc; ++i)
    {
        /*  Get digit from each addend.  While i is within a numeral, get its
            digit character and subtract '0' to convert it from a digit
            character ('0' to '9') to a plain number (0 to 9).  When i is
            outside the numeral, use zero.
        */
        int da = i < la ? a[la-1-i] - '0' : 0;
        int db = i < lb ? b[lb-1-i] - '0' : 0;
        
        /*  Add the digits, record the low digit of the sum in c, and calculate
            the carry to the next column.  The digit for c is converted from a
            plain number to a digit character.
        */
        int sum = da + db + carry;
        c[lc-1-i] = sum % 10 + '0';
        carry = sum/10;
    }

    return c;
}


int main(void)
{
    const char a[] = "51680708854858333333";
    const char b[] = "83621143489848333333";

    char *c = add(a, b);
    
    printf("sum = %s.\n", c);

    free(c);
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Nice but you might want to avoid producing a leading zero. – chqrlie Jun 27 '23 at 11:05
  • @chqrlie: It would clutter the code. The answer presents the critical information to the reader with a clear and compact example solving the original request, notes the potential aesthetic improvement, instructs the user how to resolve that if they wish, and leaves it as an exercise for the reader. Should the reader attempt the exercise, it is likely to improve their understanding of the code since they will be working with it instead of merely reading it. – Eric Postpischil Jun 27 '23 at 11:12
3

There is a subtile problem in your code: the literals 51680708854858333333 and 83621143489848333333 used to initialize the long double variables are actually parsed as integers and they both exceed the maximum value of the largest integer type long long int, which is 9223372036854775807.

You must add a trailing . to make them floating point constants and an L for long double constants. You will then get a result closer to the precise value, limited by the precision of the long double type:

#include <stdio.h>

int main(void)
{
    long double a = 51680708854858333333.L;
    long double b = 83621143489848333333.L;
    long double result = a + b;
    printf("%.0Lf + %.0Lf = %.0Lf\n", a, b, result);

    return 0;
}

The output on my macbook shows the limited precision of type long double with the default compiler chain for the M2 chip:

51680708854858334208 + 83621143489848328192 = 135301852344706662400

For better precision for 25 digit numbers, you can get away with 128-bit integers if your compiler supports them:

#include <stdio.h>

int main(void)
{
    long double a = 51680708854858333333.L;
    long double b = 83621143489848333333.L;
    long double result = a + b;
    printf("%.0Lf + %.0Lf = %.0Lf\n", a, b, result);

    __int128 e15 = 1000000000000000;
    __int128 aa = 51680 * e15 + 708854858333333;
    __int128 bb = 83621 * e15 + 143489848333333;
    __int128 cc = aa + bb;

    printf("%lld%015lld + %lld%015lld = %lld%015lld\n",
           (long long)(aa / e15), (long long)(aa % e15),
           (long long)(bb / e15), (long long)(bb % e15),
           (long long)(cc / e15), (long long)(cc % e15));

    return 0;
}

Output:

51680708854858334208 + 83621143489848328192 = 135301852344706662400
51680708854858333333 + 83621143489848333333 = 135301852344706666666

For a general solution, you can use a bignum package such as GMP, the GNU multiprecision library or implement the addition as a simplistic string based operation:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static char *add_strings(const char *s1, const char *s2) {
    size_t len1, len2;
    /* ignore leading spaces */
    while (isspace((unsigned char)*s1))
        s1++;
    while (isspace((unsigned char)*s2))
        s2++;
    /* reject negative numbers. */
    if (*s1 == '-' || *s2 == '-') {
        fprintf(stderr, "negative numbers not supported\n");
        exit(1);
    }
    /* ignore '+' sign */
    if (*s1 == '+')
        s1++;
    if (*s2 == '+')
        s2++;
    /* reject invalid arguments (could just use 0) */
    if (!isdigit((unsigned char)*s1) || !isdigit((unsigned char)*s2)) {
        fprintf(stderr, "invalid argument\n");
        exit(1);
    }
    /* skip the leading zeroes */
    while (*s1 == '0')
        s1++;
    while (*s2 == '0')
        s2++;
    /* count the digits */
    for (len1 = 0; isdigit((unsigned char)s1[len1]); len1++)
        continue;
    for (len2 = 0; isdigit((unsigned char)s2[len2]); len2++)
        continue;
    /* result has at most one more digit than the longer argument */
    size_t len3 = 1 + (len2 > len1 ? len2 : len1);
    char *s3 = malloc(len3 + 1);
    if (s3 == NULL) {
        fprintf(stderr, "cannot allocate %zu bytes\n", len3 + 1);
        exit(1);
    }
    /* compute the result starting from the least significant digits */
    int carry = 0;
    for (size_t i = len3; i --> 0;) {
        int digit1 = (len1 > 0) ? s1[--len1] - '0' : 0;
        int digit2 = (len2 > 0) ? s2[--len2] - '0' : 0;
        carry += digit1 + digit2;
        s3[i] = '0' + carry % 10;
        carry /= 10;
    }
    /* remove leading zero if any */
    if (s3[0] == '0' && len3 > 1) {
        memmove(s3, s3 + 1, --len3);
    }
    s3[len3] = '\0';   /* set the null terminator */
    return s3;
}

int main(int argc, char *argv[]) {
    /* default values for testing */
    const char *a = "51680708854858333333";
    const char *b = "83621143489848333333";

    if (argc > 2) {
        /* use command line arguments provided */
        a = argv[1];
        b = argv[2];
    }

    char *sum = add_strings(a, b);
    printf("%s + %s = %s\n", a, b, sum);

    free(sum);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189