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