Search code examples
cfloating-pointembeddedintpercentage

C How to calculate a percentage(perthousands) without floating point precision


How do you calculate a percentage from 2 int values into a int value that represents a percentage(perthousands for more accuracy)?

Background/purpose: using a processor that doesn't have a FPU, floating point computations take 100's of times longer.

int x = 25;
int y = 75;
int resultPercentage; // desire is 250 which would mean 25.0 percent

resultPercentage = (x/(x+y))*1000; // used 1000 instead of 100 for accuracy
printf("Result= ");
printf(resultPercentage);

output:

Result= 0

When really what I need is 250. and I can't use ANY Floating point computation.

Example of normal fpu computation:

int x = 25;
int y = 75;
int resultPercentage; // desire is 250 which would mean 25.0 percent

resultPercentage = (int)( ( ((double)x)/(double(x+y)) ) *1000); //Uses FPU slow

printf("Result= ");
printf(resultPercentage);

output:

Result= 250

But the output came at the cost of using floating point computations.


Solution

  • resultPercentage = (x/(x+y))*1000; does not work as (x/(x+y)) is likely 0 or 1 before the multiplication *1000 occurs. Instead:

    For a rounded unsigned integer calculation of x/(x+y), let a = x and b = x+y then to find a/b use:

    result = (a + b/2)/b;
    

    For a rounded unsigned integer percent % calculation of a/b use

    result = (100*a + b/2)/b;
    

    For a rounded unsigned integer permil ‰ calculation of a/b use

    result = (1000*a + b/2)/b;
    

    For a rounded unsigned integer permyriad ‱ calculation of a/b use

    result = (10000*a + b/2)/b;
    

    Concerns about eating up the integer range: use wider integer math (unsigned long long) for the multiplication and maybe x+y.

    result = (100ULL*a + b/2)/b;
    

    For signed a, b, it is more complicated as b/2 needs to match the sign of a/b.

    if (b > 0) {
      if (a >= 0) { 
        result = (a + b/2)/b;
      } else {
        result = (a - b/2)/b;
      }
    } else {
      if (a >= 0) { 
        result = (a - b/2)/b;
      } else {
        result = (a + b/2)/b;
      }
    }
    

    Possible to code a one liner:

    result = (a + (((a < 0)==(b < 0)) ? b/2 : b/-2))/b;
    

    b/-2 better than -b/2 to prevent UB with b == INT_MIN. Or use -(b/2).


    Of course, replace

    // printf(resultPercentage);
    printf("%d\n", resultPercentage);
    // or for unsigned
    printf("%u\n", resultPercentage);