I have written a small program that converts a decimal number into a reduced fraction representation. I also designed this little program to be fairly robust, so that it can give the correct output for various inputs.
For example:
./a.out 3,000.375 24003/8 ./a.out "3 000,375" 24003/8 ./a.out 3.000,375 24003/8 ./a.out 3000.375 24003/8
Here is the complete code for the program:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int isDigit(char);
void simplify(unsigned long long *, unsigned long long *);
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Please enter a number to fractionize.\n");
return EXIT_FAILURE;
}
char *input = argv[1];
int i;
char c;
unsigned long long previous, denominator = 1, numerator = 0;
for (i = strlen(input) - 1; i >= 0; i--, denominator *= 10) {
c = input[i];
if (!isDigit(c)) {
break;
}
previous = numerator;
numerator = (c - '0') * denominator + numerator;
if (previous > numerator) {
return EXIT_FAILURE;
}
}
if (c == '-' || isDigit(c)) {
printf("%llu/1\n", numerator);
return EXIT_SUCCESS;
}
simplify(&numerator, &denominator);
int j, negative = 0;
unsigned long long whole = 0;
for (j = 0; j < i; j++) {
char c = input[j];
if (c == '-') {
negative = 1;
} else if (isDigit(c)) {
previous = whole;
whole = whole * 10 + (c - '0');
if (previous > whole) {
return EXIT_FAILURE;
}
}
}
previous = numerator;
numerator += whole * denominator;
if (previous > numerator) {
return EXIT_FAILURE;
}
simplify(&numerator, &denominator);
if (negative) {
printf("-");
}
printf("%llu/%llu\n", numerator, denominator);
return EXIT_SUCCESS;
}
int isDigit(char c) {
return c >= '0' && c <= '9';
}
void simplify(unsigned long long *numerator, unsigned long long *denominator) {
while (1) {
if ((*numerator % 2 == 0) && (*denominator % 2 == 0)) {
*numerator /= 2;
*denominator /= 2;
} else if ((*numerator % 5 == 0) && (*denominator % 5 == 0)) {
*numerator /= 5;
*denominator /= 5;
} else {
break;
}
}
}
My algorithm is basically to first process the number right to left. As soon as a non-digit is detected, the part of the input that has been processed is deemed the "fractional part". This fractional part is then simplified (when possible). For example "12.34"
would be processed as 34
and 100
, which would then be simplified to 17
and 50
.
Then, the input is processed left to right up to the point where the first scan terminated. Any non-digit characters are ignored (except the minus sign), and digits are accumulated (12
in this case). This is deemed the "whole part".
Then, the whole part is multiplied by the (simplified) denominator and it is added to the (simplified) fractional part (in this case, the fraction would be 617
over 50
). The fraction is then again simplified (when possible) and the result is printed out.
My main concern is how I handle overflow. Because I am using unsigned
types, overflow would simply cause the number to wrap around towards 0, so I simply check if the number before the computation (stored in previous
) is larger than the number after the computation. If it is, then overflow has occurred. This seems like a fairly naive way to do it, but it seems to work for the inputs that I have tried.
Otherwise, any stylistic or performance-related inputs are always appreciated.