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