Home > other >  trying to sum two strings and store the result in another string?
trying to sum two strings and store the result in another string?

Time:02-16

I was trying to sum two strings together, this is my attempt:

  • convert strings to numbers using strtol, because it's safer than atoi.
  • compute sum.
  • find number of digits using log (base 10) rounded using floor 1. (it's the fastest method to do that task). (I've used floor, because floor rounds number to the nearest integer at the left hand side).
  • I've allocated enough memory in the heap to store each digit.
  • I've used snprintf to store the number in the string.

the problem is that the function returns only "4", and not "42". I think the delicate part is the last step.

another thing is: "possible loss of data: from double to size_t". but it's rounded, therefore it shouldn't be a problem, but I still have it in the error/warning list. (I use visual studio).


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

char* sum_of_strings(const char* a, const char* b) {
    int x = strtol(a, NULL, 10); int y = strtol(b, NULL, 10); 
    int sum = x   y; 
    size_t length = floor(log10(sum))   1; 
    char* str = malloc(length  1); 
    if (str == NULL) {
        return NULL; 
    }
    snprintf(str, length, "%d", sum); 
    return str; 
}    



int main(void) {
    char a[] = "34"; 
    char b[] = "8";
    char* str = sum_of_strings(a, b); 

    free(str); 
    return 0; 
}

CodePudding user response:

You may be overthinking the problem.

The maximum size of the output string is [considerably] less than 64 bytes.

So, if we use a fixed size buffer to do sprintf on, we can eliminate all the floating point rounding altogether:

char *
sum_of_strings(const char *a,const char *b)
{
    int x = strtol(a,NULL,10);
    int y = strtol(b,NULL,10);

    int sum = x   y;

    char str[64];
    sprintf(str, "%d", sum);

    return strdup(str);
}

The above code is pretty simple and fast.

However, strdup has to [effectively] scan the string twice: once to get the length and a second time to copy it to the allocated buffer.

We can [probably] make this slightly faster:

char *
sum_of_strings(const char *a,const char *b)
{
    long x = strtol(a,NULL,10);
    long y = strtol(b,NULL,10);

    long sum = x   y;

    char str[64];
    ssize_t len = sprintf(str, "%ld", sum);

    // add space for EOS
      len;

    char *ret = malloc(len);
    if (ret != NULL)
        memcpy(ret,str,len);

    return ret;
}

Rather than using floating point, the usual way to get the number of digits is something like:

size_t len = (sum != 0) ? 0 : 1;
for (long tmp = sum;  tmp != 0;  tmp /= 10,   len);

Arguably, this may be slower than the floating point [on systems that have fast H/W FPUs]. I presume you've benchmarked both ways [to be able to state your F.P. method is fastest]. If not, the above may be a drop in replacement for your F.P. code.

Is floor(log10(sum)) really faster [on average] than 5 fixed point divides?


But even that doesn't account for hidden costs.

sprintf et. al. has to do much the same when generating its output (e.g. sum % 10 and sum /= 10).

If we're "crazy" for speed, and if we do our own limited version of what sprintf does, we can do the length calculation in the same loop as the digit stores:

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

#define MAXBUF  64

char *
sum_of_strings(const char *a,const char *b)
{
    long x = strtol(a,NULL,10);
    long y = strtol(b,NULL,10);

    long sum = x   y;

    char str[MAXBUF];
    char *lhs = &str[MAXBUF];

    // store digits in right-to-left order
    for (;  sum != 0;  sum /= 10)
        *--lhs = (sum % 10)   '0';

    // get number of digits
    size_t len = &str[MAXBUF] - lhs;

    // handle special case of sum being zero
    if (len == 0) {
        *--lhs = '0';
          len;
    }

    // allocate the space
    char *ret = malloc(len);

    // copy out the data
    if (ret != NULL) {
        memcpy(ret,lhs,len);
        ret[len] = 0;
    }

    return ret;
}

Of course, which method is "best" depends on simplicity/complexity vs. [benchmarked] speed. That is, is added complexity for [possible] speed gains worth it?

YMMV ...

CodePudding user response:

I've modified your sum_of_strings code and it seems to now be working based on your example.

Changes include the following:

  • Data types are now longs instead of ints
  • I've added two bytes to the length to account for the floor and the null byte
  • The last byte of the string is set to the null byte which makes printing work correctly
char* sum_of_strings(const char* a, const char* b) {
    long const x = strtol(a, NULL, 10);
    long const y = strtol(b, NULL, 10);
    long const sum = x   y;
    size_t const length = floor(log10(sum))   2;
    char * const str = malloc(length);
    if (str == NULL) {
        return NULL;
    }
    snprintf(str, length, "%ld", sum);
    str[length - 1] = '\0';
    return str;
}
  • Related