Last active
December 5, 2016 05:31
-
-
Save iamarkdev/95827fc2f49d46a42eb44dcce0c7675e to your computer and use it in GitHub Desktop.
ATOF with support for scientific notation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#define MAX_LINE_LENGTH 100 | |
enum PositionState { | |
POSITION_STATE_WHOLE, | |
POSITION_STATE_DECIMAL, | |
POSITION_STATE_NOTATION | |
}; | |
int mygetline(char line[], int max) { | |
int c, i; | |
for (i = 0; i < max && (c = getchar()) != EOF && c != '\n'; i++) { | |
line[i] = c; | |
} | |
if (i >= max) { | |
i = i - 1; | |
} | |
line[i] = '\0'; | |
return i; | |
} | |
int isDigit(char input) { | |
return (input >= '0' && input <= '9') ? 1 : 0; | |
} | |
long power(int base, int exponent) { | |
long result = (exponent > 0) ? base : 0; | |
for (int i = 0; i < exponent - 1; i++) { | |
result *= base; | |
} | |
return result; | |
} | |
float atof(char input[]) { | |
int sign = 1; | |
int whole = 0; | |
float decimal = 0; | |
int decimalCount = 0; | |
int notationExponents = 0; | |
int notationSign = 1; | |
int i = 0; | |
while ((input[i] < '0' || input[i] > '9') && input[i] != '+' && input[i] != '-') { | |
i++; | |
} | |
if (input[i] == '+' || input[i] == '-') { | |
sign = (input[i++] == '-') ? -1 : sign; | |
} | |
enum PositionState state = POSITION_STATE_WHOLE; | |
while (input[i] != '\n' && input[i] != ' ' && input[i] != '\t' && input[i] != '\0') { | |
// Return 0 for invalid characters. | |
if ((input[i] < '0' || input[i] > '9') && input[i] != 'e' && input[i] != 'E' && input[i] != '-' && input[i] != '+' && input[i] != '.') { | |
fprintf(stderr, "Invalid char \"%c\" found in atof() input.\n", input[i]); | |
return 0; | |
} | |
// Handle whole numbers | |
if (state == POSITION_STATE_WHOLE) { | |
if (isDigit(input[i])) { | |
whole = whole * 10 + (input[i] - '0'); | |
} | |
} | |
// Handle decimals | |
if (state == POSITION_STATE_DECIMAL) { | |
if (isDigit(input[i])) { | |
decimalCount++; | |
decimal = decimal * 10 + (input[i] - '0'); | |
} | |
} | |
state = (input[i] == '.') ? POSITION_STATE_DECIMAL : state; | |
// Handle notation | |
if (state == POSITION_STATE_NOTATION) { | |
if (input[i] == '-') { | |
notationSign = -1; | |
i++; | |
} | |
if (isDigit(input[i])) { | |
notationExponents = notationExponents * 10 + (input[i] - '0'); | |
} | |
} | |
state = (input[i] == 'e' || input[i] == 'E') ? POSITION_STATE_NOTATION : state; | |
// Next char in input | |
i++; | |
} | |
// Calculate result | |
double result = whole; | |
if (decimalCount > 0) { | |
result += decimal / power(10, decimalCount); | |
} | |
if (notationExponents > 0) { | |
if (notationSign > 0) { | |
result *= power(10, notationExponents); | |
} else { | |
result /= power(10, notationExponents); | |
} | |
} | |
return sign * result; | |
}; | |
int main() { | |
int c; | |
char line[MAX_LINE_LENGTH]; | |
while (mygetline(line, MAX_LINE_LENGTH)) { | |
printf("%f\n", atof(line)); | |
} | |
} |
If you expect the string to be already trimmed, this ends up being unnecessary:
while ((input[i] < '0' || input[i] > '9') && input[i] != '+' && input[i] != '-') {
i++;
}
You have an isdigit() function but you're not using it as much as you can. Instead of this,
if (input[i] < '0' || input[i] > '9')
you can use
if (!isdigit(input[i]))
The name of the position
variable name is misleading. This variable is the position state, not the position.
How about this for a power() implementation?
double power(int base, int exponent) {
if (exponent == 0) {
return 1;
}
double result = base;
int negative = 0;
if (exponent < 0) {
negative = 1;
exponent *= -1;
}
for (int i = 0; i < exponent - 1; i++) {
result *= base;
}
if (negative) {
result = 1.f / result;
}
return result;
}
then instead of this code:
if (notationExponents > 0) {
if (notationSign > 0) {
result *= power(10, notationExponents);
} else {
result /= power(10, notationExponents);
}
}
it could simply be:
result *= power(10, notationExponents);
where notationExponents can be a negative, positive, or zero value
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If i add these couple lines to the code:
I can see that mygetline() is not terminating the string at all. Output looks like:
Also, atof() is doing too much string processing. It should be able to expect that the string being passed in has already been properly trimmed and should only have to search for the terminating null character.
I'd recommend changing mygetline() to remove the newline character and null-terminate the string. If there is additional whitespace expected on either side, use some other function to remove that before passing it to atof().