1

I need to convert data from a file to a double and sometimes the data is in the form:

0.3387000000D+02  0.6067999217D-02
0.5095000000D+01  0.4530799416D-01
0.1159000000D+01  0.2028219738D+00
0.3258000000D+00  0.5039029350D+00
0.1027000000D+00  0.3834209505D+00

How would you tackle handling the D here?

This is scientific notation just with a D instead of an E.

I am thinking using std::regex here but am hoping for a more elegant strategy.

Something like the following:

std::regex rr( "((\\+|-)?[[:digit:]]+)(\\.(([[:digit]]+)?))?(d|D)((\\+|-)?)[[:digit:]]+)""?"); 
anastaciu
  • 23,467
  • 7
  • 28
  • 53
zstreet
  • 126
  • 9
  • Are you sure that the D means the same as E? Where did you get this data? – JohnFilleau Jun 25 '20 at 22:35
  • Confirmed, it does mean E. This data is from a quantum chemistry package MOLPRO. I could do a replace D with E and let the standard conversion functions handle the rest... – zstreet Jun 25 '20 at 22:37
  • @JohnFilleau Fortran uses D instead of E. See [Scientific `d` notation not read in C++](https://stackoverflow.com/q/24527075/995714) – phuclv Jun 26 '20 at 02:14
  • yup that's exactly right. MOLPRO is written in FORTRAN and thank you for the link! – zstreet Jun 26 '20 at 03:58
  • @phuclv -- FORTRAN uses both. `E` is used in a single-precision literal, and `D` is used in a double-precision literal. – Pete Becker Jun 26 '20 at 12:53
  • 2
    Re: "hoping for a more elegant strategy" -- good instinct. Regular expressions are only rarely appropriate. I can count on the fingers of one hand the number of questions about regular expressions I've seen on StackOverflow where a regular expression was actually the best solution. – Pete Becker Jun 26 '20 at 12:55

2 Answers2

3

Replace the D, with an E with std::replace, after that it's straight forward:

Live demo

std::string s = "0.3387000000D+02";
std::replace( s.begin(), s.end(), 'D', 'E');
std::cout << std::stod(s);

Or std::replace_if:

Live demo

bool isD(char c) { 
    return c == 'D'; 
}
std::string s = "0.3387000000D+02";
std::replace_if( s.begin(), s.end(), isD, 'E');
std::cout << std::stod(s);

Output:

33.87
anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • very nice! Now depending on the version of the program, only sometimes will the string have a D. So can you modify your code to include an ```if``` statement if the replace function actual replaced anything? – zstreet Jun 25 '20 at 22:45
  • @zstreet, sure. – anastaciu Jun 25 '20 at 22:49
  • @zstreet, it's done, the first one will only replace if it finds a `D` otherwise it does nothing, but you have that second option. – anastaciu Jun 25 '20 at 22:53
  • Oh the first option with ```std::replace``` does nothing if it doesn't fine ```D```? I like that! Thank you! – zstreet Jun 25 '20 at 22:57
-1

you can do it like this.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cmath>

double string2double(std::string s)
{
    // Find the index of scientific D
    int indexD = s.find('D');
    // separate the base from rest, start at 0 and go till index of D, without D
    std::string number = s.substr(0, indexD);
    //indexD+2 to ignore D and plus 
    std::string power = s.substr(indexD+2);
    
    // do conversion from string to number
    double rawNumber = std::stod(number);
    double powerNumber = std::stod(power);
    //test
    std::cout << rawNumber << std::endl;
    std::cout << powerNumber << std::endl;
    //return
    return rawNumber * std::pow(10, powerNumber);
}



int main()
{   

    std::string s = "0.3387000000D+02";
    std::cout << string2double(s) << std::endl;
    return 0;
}
Berkay Berabi
  • 1,933
  • 1
  • 10
  • 26
  • 1
    Parsing floating-point literals has several subtle issues. This kind of brute force code will work most of the time, but it will get many edge cases wrong. For example, for typical 64-bit doubles, the maximum exponent is 37. But `.00001e40` is a valid, finite value; it's just written funny. `std::stod` can handle that; it produces the same result as `std::stod(1.0e35)`. However, if you split off the exponent, `std::pow(10, 40)` will give infinity. Multiplying infinity by any non-zero value gives you infinity, which is what this function will return for `.00001D40`. – Pete Becker Jun 26 '20 at 13:06
  • Note also that there is a typo: `s.substr(indexD+2)` should be `s.substr(indexD+1)`. The code works with the test value in `main` because the exponent is `02`; if it was `12` the result would be wrong. – Pete Becker Jun 26 '20 at 13:09
  • 3
    The moral here is: don't trust your intuition when you're dealing with floating-point math. Your intuition comes from experience with real numbers; floating-point numbers are not real numbers, and your intuition (and mine!) will often be wrong. – Pete Becker Jun 26 '20 at 13:10