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;
}