jlconlin
jlconlin

Reputation: 15074

How to read FORTRAN formatted numbers in C++

I have a file with lines of numbers that look like this:

1.000000-5 2.436700+0 2.530000-2 2.436700+0 5.000000-2 2.436700+0
1.000000+1 2.436700+0 1.000000+2 2.433800+0 1.000000+3 2.433800+0

I need to read this with C++ to get numbers

1.0E-5 2.4367E0 2.53E-2 2.4367E0 5.0E-2 2.4367E0
1.0E1 2.4367E0 1.0E2 2.4338E0 1.0E3 2.4338E0

The challenge is that there is no E in the numbers of the file; the E indicates the exponential notation.

How can I read something like this into a float? It needs to be very efficient because I have to read such a number hundreds of thousands or millions of times for each file.

Any suggestions on how to make this happen?

Upvotes: 1

Views: 858

Answers (4)

user2249683
user2249683

Reputation:

You should write a parser. A simple implementation would be:

#include <iostream>
#include <sstream>
#include <vector>

int main() {

    // Signed data set
    std::istringstream input(
        "+1.000000-5 -2.436700+0 +2.530000-2 -2.436700+0 +5.000000-2 -2.436700+0\n"
        "+1.000000+1 -2.436700+0 +1.000000+2 -2.433800+0 +1.000000+3 -2.433800+0");

    std::vector<double> result;

    std::string in;
    while(input >> in) {
        auto e = in.find_last_of("+-");
        if(0 < e && e != std::string::npos) {
            in.replace(e, 1, std::string("E") + in[e]); // this might get a optimization
        }
        std::istringstream number(in);
        double d;
        number >> d;
        result.push_back(d);
    }
    for(auto d: result) std::cout << std::fixed << d << std::endl;
    return 0;
}

Upvotes: 3

Tawnos
Tawnos

Reputation: 1887

This is something I just hacked together quickly, you will need to test if it works for all your cases:

ifstream file;
file.open("f:\\stackoverflow\\fortranfloat\\fortranfloats.txt");

string line;
if (file.is_open())
{
    while (file.good())
    {
        getline(file, line);
        for (int i = 0; i < line.length(); i++)
        {
            int j = 0;
            char buf[10];
            while (i < line.length() && line[i] != ' ')
            {
                if (line[i] == '-' || line[i] == '+')
                    buf[j++] = 'e';

                buf[j++] = line[i++];
            }
            float number = atof(buf);
            cout << "Number: " << number << endl;
        }
    }
    file.close();
}
else
{
    cout << "Failed to open file" << endl;
}

Upvotes: 0

Pete
Pete

Reputation: 4812

Scan the string up to the + or - (or space) character and store that. replace the + or - with a null and pass the string to atof to get the first part. then if you had a + or a - scan in the exponent. once you have that in an int or a double, use value * pow(10.0, exponent). Or you can use strtod and avoid the null modification.

Or as mentioned, modify an existing atof implementation.

Upvotes: 0

zmbq
zmbq

Reputation: 39023

I'd simply add the E before every - or + and use atof or strtof on it.

If this isn't fast enough for you, check out your favorite implementation of atof (I couldn't find one with a simple search, but it shouldn't be too hard) and change it so that it doesn't look for the E, but just for - or +.

Upvotes: 3

Related Questions