C How to calculate a percentage(perthousands) without floating point precision
Solution 1
resultPercentage = (x/(x+y))*1000;
does not work as (x/(x+y))
is likely 0
or 1
before the multiplcation *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;
@H2CO3 wells points out the concerns about eating up the integer range so using wider integers (long
, long long
) are needed for the multiplication and maybe x+y
.
result = (100L*a + b/2)/b;
Of course, replace
// printf(resultPercentage);
printf("%d\n", resultPercentage);
Solution 2
A solution using long division
And now for the pencil-and-paper answer… Not sure if this will be faster than your processor's built in floating point arithmetic, but it's a fun thing to look at (and maybe improve upon). This is an implementation of long division (remember that?) - in principle it is "infinitely precise", a bit like BigDecimal math - in practice it is limited because a finite amount of space is allocated for the string (you could change that with a malloc/free
). If you have trouble with (code) space on your processor (as well as the lack of a dedicated floating point unit) then this is definitely not the way to go; also I am assuming that all division (even integer) would be slow, and using only multiplication, addition and subtraction.
Final bonus - the result comes out as a string, saving the need for a separate printf
style conversion later. There are many ways conceivable for speeding this up; for now it's just fun to see how you can solve this problem with only limited precision integers, yet get a "very good" answer. Incidentally, according to my pedestrian timing code, the result is faster than a divide-and-sprintf routine (which was pretty gratifying). Almost certainly due to the fact that the conversion to a string of digits is happening "almost for free" (if you think about how that is normally done, it takes lots of division/modulo math…).
EDIT in its current form, this code even takes account of rounding (computing one additional digit, and then making the necessary adjustments). One caveat: it only works for positive integers.
Play with it and tell me if you like it!
#include <stdio.h>
#include <time.h>
void doRound(char *s, int n) {
// round the number string in s
// from the nth digit backwards.
// first digit = #0
int ii;
int N = n;
if(s[n] - '0' < 5) {
s[N] = '\0';
return; // remove last digit
}
while (n-- > 0) {
if(s[n] == '.') {
n--;
}
if(s[n] - '0' < 9) {
s[n]++;
s[N] = '\0';
return;
}
s[n] = '0';
}
if (n == -1) {
for (ii = N-1; ii >=0; ii--) {
s[ii+1] = s[ii];
}
s[0] = '1';
}
else s[N] = '\0';
}
void longDivision(unsigned int a, unsigned int b, char* r, int n) {
// implement b / a using only integer add/subtract/multiply
char temp[20]; // temporary location for result without decimal point
char c = '0'; // current character (digit) of result
int ci = 0; // character index - location in temp where we write c
int t = n + 1; // precision - no more than n digits
int dMult = 0; // scale factor (log10) to be applied at the end
int di = 0;
// first get numbers to correct relative scaling:
while( a > b) {
dMult++;
b *=10;
}
while (10 * a < b) {
dMult --;
a*=10;
}
// now a >= b: find first digit with addition and subtraction only
while( b > a ) {
c++;
b -= a;
}
t--;
temp[ci++] = c; // copy first digit
c = '0';
// now keep going; stop when we hit required number of digits
while( b > 0 && t > 0) {
b *= 10;
t--;
while( b > a ) {
c++;
b -= a;
}
temp[ ci++ ] = c;
c = '0';
}
// copy the result to the output string:
temp[ ci ] = '\0'; // terminate temp string
if (dMult > 0) {
r[ di++ ] = '0';
r[ di++ ] = '.';
while( --dMult > 0 ) {
r[ di++ ] = '0';
}
ci = 0;
while( temp[ ci ] != '\0' ) {
r[ di++ ] = temp[ ci++ ];
}
}
else {
ci = 0;
while( temp[ ci ] != '\0') {
r[ di++ ] = temp[ ci++ ];
if( dMult++ == 0 ) r[ di++ ] = '.';
}
}
r[ di ] = '\0';
// finally, do rounding:
doRound(r, n+1);
}
int main(void) {
int a, b;
time_t startT, endT;
char result[20];
int ii;
a = 123; b = 700;
longDivision(a, b, result, 5);
printf("the result of %d / %d is %s\n", b, a, result);
printf("the actual result with floating point is %.5f\n", (float) b / (float) a );
a = 7; b = 7000;
longDivision(a, b, result, 5);
printf("the result of %d / %d is %s\n", b, a, result);
a = 3000; b = 29999999;
longDivision(a, b, result, 5);
printf("the result of %d / %d is %s\n", b, a, result);
startT = clock();
for(ii = 1; ii < 100000; ii++) longDivision(a, ii, result, 5);
endT = clock();
printf("time taken: %.2f ms\n", (endT - startT) * 1000.0 / CLOCKS_PER_SEC);
// and using floating point:
startT = clock();
for(ii = 1; ii < 100000; ii++) sprintf(result, "%.4f", (float) ii / (float) a);
endT = clock();
printf("with floating point, time taken: %.2f ms\n", (endT - startT) * 1000.0 / CLOCKS_PER_SEC);
return 0;
}
The result (without optimization turned on):
the result of 700 / 123 is 5.6911
the actual result with floating point is 5.69106
the result of 7000 / 7 is 1000.00
the result of 29999999 / 3000 is 10000.0
time taken: 16.95 ms
with floating point, time taken: 35.97 ms
Solution 3
try this:
int x = 25;
int y = 75;
int resultPercentage; // desire is 250 which would mean 25.0 percent
resultPercentage = (x*1000)/(x+y);
printf("Result= ");
printf(resultPercentage);
Solution 4
A slight modification of expression can do the trick. Like in this case :
resultPercentage = (x*1000)/(x+y); should do the job.
Solution 5
Why dont you use
resultPercentage = (x*1000)/(x+y);
Ashitakalax
University of Utah graduate offering a strong academic background in computer engineering combined with excellent internship experience and three years in industry. Architected a new software infrastructure utilizing the latest programming practices held to a high coding standard. Currently leading an R&D team of developers in creating a new line of fitness equipment.
Updated on June 18, 2022Comments
-
Ashitakalax almost 2 years
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.