Skip to content

Instantly share code, notes, and snippets.

@iamarkdev
Last active December 5, 2016 05:31
Show Gist options
  • Save iamarkdev/95827fc2f49d46a42eb44dcce0c7675e to your computer and use it in GitHub Desktop.
Save iamarkdev/95827fc2f49d46a42eb44dcce0c7675e to your computer and use it in GitHub Desktop.
ATOF with support for scientific notation
#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));
}
}
@bradley219
Copy link

If i add these couple lines to the code:

    memset(line, 0xff, sizeof(line));

    while (mygetline(line, MAX_LINE_LENGTH)) {
        printf("line=%s\n", line);

I can see that mygetline() is not terminating the string at all. Output looks like:

$ gcc scientificatof.c && echo -en '1.2e1' | ./a.out
line=1.2e1�����������

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().

@bradley219
Copy link

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++;
    }

@bradley219
Copy link

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]))

@bradley219
Copy link

The name of the position variable name is misleading. This variable is the position state, not the position.

@bradley219
Copy link

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