You are here:

C++/C++ and FORTRAN communication?

Advertisement


Question
Hi:

I have a computer program written in FORTRAN which outputs very small or very large numbers using the D-format. I.e. 1.5E-5 is output as 1.5D-5. My C++ program which is supposed to read the output data crashes upon encountering the character "D" in what should be a double. What can I do to my C++ code so it can read such numbers? Your response will be of great help.

Answer
I should check your code if it is crashing. I suspect this is because your stream state is getting in a failed or maybe even a fatel error state (the stream state failbit or badbit are being set), and then not reading what you think it is. Failbit indicates an operation failure such as a read format error, which I suspect to be a likely condition in this case. The other possibilities are that you have stream exception raising enabled for failed operations and/or errors and you are not catching and handling them, or your standard library IOStreams implementation is not very good.

Note: I am assuming _standard_ C++ behaviour, not pre-standard IOStreams behaviour. You can test for failed or bad stream state using the fail() member function. Other stream state checking functions include: good() (stream goodbit set), bad() (stream badbit set), eof() (the stream eofbit (end of file bit) set). I leave it to you to look up the details in your C++ IOStreams reference.

For your information I tried reading a D format value into a double and then another double from std::cin. The second read caused the stream to be placed into a failed state (failbit set). I used Microsoft Visual C++ 8 (.NET 2005).

As to how to overcome this data format incompatibility, the short answer is that you either convert the FORTRAN D format to C++ scientific format before you try to convert the data to a double, or you write code to parse D format values.

The most obvious way to do convert to C++ scientific format is to read each D format value as a string, manipulate that string to replace the D with an E and then convert the modified value string to a double. The latter part can be done using a std::istringstream (include the <sstream> header). Here is a simple example with minimal error checking for brevity. Is assumes that there are no spaces in the D format values:

#include <string>
#include <iostream>
#include <sstream>

namespace
{
       const char DFormatExponentChar( 'D' );
       const char CppFormatExponentChar( 'E' );
}

int main()
{
       // read D format value as a string
       std::string doubleString;
       std::cout << "Enter FORTRAN D format value: ";
       std::cin >> doubleString;

       double value(0.0);

       // Convert D format string to C++ scientific notation:

       // First locate the D format exponent character
       std::string::size_type exponentCharPosition
         ( doubleString.find
         ( DFormatExponentChar
         )
         );

       if ( std::string::npos == exponentCharPosition )
       { // Did not find the D format exponent character...
         std::cerr << "ERROR! Not a D Format value." << std::endl;
       }
       else // assume we have a D Format value...
       {
       // replace the D format exponent character
       // with a C++ exponent character
         doubleString.replace( exponentCharPosition
         , 1
         , 1
         , CppFormatExponentChar
         );
         std::cout << "C++ Format value: " << doubleString << '\n';

       // Read the converted value string into a double:
         std::istringstream istrstrm( doubleString );
         istrstrm >> value;
         std::cout << "Double value: " << value << std::endl;
       }
}

As with all code it is best viewed with a mono-spaced font such as Courier. I have used a large indent value of 8 as I am not sure how leading spaces are treated when posted through AllExperts. I have also tried to keep long lines from wrapping by folding them across several lines.

The example just reads one value from the console using std::cin into a std::string then searches for and replaces the D format exponent character (D) with a C++ scientific notation exponent character (E). The modified scientific notation value string is then associated with a std::istringstream which is used to convert it to a double.

I'll leave it to you to clean up the code and form it into a useful function if you so desire.

The other option is that we parse the D format characters ourselves. This could of course be done by processing the stream one character at a time.

More interesting however is the idea that we replace the code that parses the doubles as they are read from the stream. To do this we have to mess about with locales and facets. I am not going into great detail here on locales and facets as it is a large and complex topic. I shall just go through the very basics as they concern this particular question and answer.

Locales are used to customise streams for internationalisation purposes. A locale consists of a number of facets. These dictate various areas that may need to be localised such as date, time and money formatting. In amongst the standard facets are ones that govern number formatting conventions. Of particular interest here is one called num_get (or std::num_get) which takes sequences of characters, and formatting flags as input and returns error and numeric values as outputs (numeric here includes bool, integer, and floating point types). It does this by a set of overloaded public get member functions which forward onto a set of virtual, protected do_get member functions.

So to parse D format double values we need to replace the num_get function that parses doubles. You can of course extend this to float and long double types, but for simplicity I shall just look at the case for doubles.

Of course we would like to write as little code as possible, leveraging as much existing code as we can. This is where the fact that the real work is done by the virtual do_get functions looks good. All we need to do is derive from std::num_get and override the do_get virtual member function, replacing the base implementation with our own parsing logic. We then arrange for this to be used by the input streams reading the D format values.

In fact we can pass work onto the base num_get or another num_get facet to do as much of the parsing as possible. In my example I obtain a reference to the num_get facet in use when an instance of the custom derivation is created - you may or may not decide this is a good idea.

The example I show below does the following:

- uses the referenced num_get facet to parse as much of the double value as possible. This should get the value up to the D exponent character.

- if the next character in the character sequence is the D format exponent character (i.e. D) then handle the exponent. This is done by skipping over the D and again using the referenced num_get facet to get the integer exponent. Having got the exponent the value is applied to the mantissa part previously acquired.

The error value is modified to reflect the actual state based on the extended format handling. The example performs simple range checking on the exponent value. When combined with the mantissa the exponent value may still render a value that is too small or too large, as we are working in base 10 and computer floating point formats use binary.

One oddity is that there is no get (or do_get) for int. Instead this is handled by the overloads for long. This is why I obtain the exponent as a long. Obtaining this value as a short may give earlier range error detection for more outlandish exponent values.

The other thing to note about std::num_get is that like a lot of the standard C++ library, it is a class template. This means that to be as of a drop in replacement for the standard num_get as possible our derivation is a class template as well. This adds a lot of template obfuscation to the code, sorry.

Having written the replacement num_get facet we have to use it. To do this we create an instance of the replacement class template. In fact we have to create an instance of a specific specialisation of the facet class template. In my example I specify char as the first template parameter and leave the input iterator type as the default.

The only other point to note about creating the facet instance is that I pass the facet constructor a 1 value to prevent the locale logic from trying to delete it when cleaning up - a bad idea as my example uses local instances on the stack!

Having created our replacement facet we next have to create a locale, specifying the locale to base the new instance on and the facet we wish to replace.

Now we have a locale that uses our replacement num_get facet, we can make our input stream (std::cin in the example) use it by passing it to the stream's imbue member function.

Right about now I expect your head is reeling a bit, so I shall show you the code and let you consider it at your leisure. I developed it using Microsoft Visual C++ 8 (.NET 2005).

#include <iostream>   // for std:cin, std::cout etc.
#include <locale>     // For std::locale and facets
#include <limits>     // For std::numeric_limits
#include <cmath>      // For pow

// Local definitions
namespace
{
       const char DFormatExponentChar( 'D' );
       const char CppFormatExponentChar( 'E' );
}

// Helper function to display error message if
// passed in input stream is in a bad state
void CheckIStrm( std::istream const & in)
{
       if ( in.fail() )
       {
         if ( in.bad() )
         {
         std::cerr << "Cin in fatal error state\n";
         }
         else
         {
         std::cerr << "Cin operation failed\n";
         }
       }
}

// Sub-class template of the std::num_get facet class template
// used to parse numeric input character sequences
template < class charT, class InputIterator=std::istreambuf_iterator<charT> >
class DFormatNumGet : public std::num_get< charT, InputIterator >
{
public:
// Ensure we have base types available
       typedef typename std::num_get< charT, InputIterator >   base_type;
       using base_type::iter_type;
       using base_type::char_type;

// Replicate the base constructor.
// Pass on most of the work to the base constructor
// Get and save reference to current global num_get facet
       DFormatNumGet( size_t refs = 0 )
       : base_type( refs )
       , iPrevNumGet( std::use_facet<base_type>(std::locale()) )
       {
       }

protected:
// Override the base class virtual member function
// that handles parsing doubles type character sequences
       virtual iter_type do_get( iter_type in
         , iter_type end
         , std::ios_base & ib
         , std::ios_base::iostate & err
         , double & v
         ) const;

private:
// Reference to global num_get facet in effect when
// instance created
       base_type const &     iPrevNumGet;
};

// Implementation of overriden double value parsing function
template < class charT, class InputIterator >
typename DFormatNumGet<charT,InputIterator>::iter_type
DFormatNumGet<charT,InputIterator>::do_get
( iter_type in
, iter_type end
, std::ios_base & ib
, std::ios_base::iostate & err
, double & v
) const
{
// Initially parse sequence using previous num_get facet
       iter_type next( iPrevNumGet.get(in, end, ib, err, v) );

// Continue if there was no error
       if ( 0==err || std::ios::goodbit==err )
       {
       // If we are not at the end of the sequence and the
       // current character is the D format exponent character
       // then we have some work to do...
         if ( next != end && *next==DFormatExponentChar )
         {
         ++next; // skip exponent character

         // Get the exponent value:
         // This is a integer value so we use the
         // stashed num_get facet reference to parse
         // the integer value as a long:
         err = std::ios::goodbit;// may need to set to 0
         long exponent;
         next=iPrevNumGet.get(in,end,ib,err, exponent);

         // may need to compare against 0
         if ( std::ios::goodbit==err )
         {
         // If all well check exponent value is in range
         // Note: this is not a water tight error
         //       checking strategy
         if (  std::numeric_limits<double>::
         min_exponent10 <= exponent
         && std::numeric_limits<double>::
         max_exponent10 >= exponent
         )
         {
         // If the exponent value is in
         // range apply it to the double value
         // we already obtained:
         v *= pow(10.0, exponent);
         }
         else // exponent out of range
         {    // set err to failbit
         err = std::ios::failbit;
         }
         }
         }
       }

       return next; // next character position in sequence
}

// Main: TRy out our num_get facet:
// Creates DFormatNumGet facet instance for char character types
// Creates locale based on global local, replacing the num_get
// facet with the instance of DFormatNumGet<char>
// Inbues std::cin with this locale so it uses it in preference to
// the default global locale
// Reads in a double value from std::cin using the DFormatNumGet
// facet.
int main()
{
       // Create instance of our num_get facet for char types
       // The 1 passed to the constrcutor means we keep ownership of
       // of the facet for memory management purposes. If we pass 0
       // the the locale will try to clean it up for us on exit with
       // nasty result.
       DFormatNumGet<char> dFormatNumGet(1);

       // Create a locale copy of the global locale replacing the
       // num_get facter with our own
       std::locale dFormatLocale( std::locale(), &dFormatNumGet );

       // Apply this locale to std::cin
       std::cin.imbue( dFormatLocale );

       // Read a double value from std::cin
       double value(0.0);
       std::cin >> value;

       // Report fail or bad bit stream states
       CheckIStrm( std::cin );

       // Report on the value read from std::cin
       std::cout << "Double value: " << value << std::endl;
}

Again, the code is best viewed with a mono-spaced font such as Courier and I have used a very wide indent of 8 and wrapped some lines so they are not too long which may make the code formatted not as you like it. You will note that I am not sure whether a good value for err is 0 or std::ios::goodbit. Logic would indicate the latter. Trial and error suggests the former, at least until the value is changed. It seems a good value may be the value passed in, meaning that err is not modified unless an error occurs. Whether this is standard behaviour or just the VC 8 implementation's behaviour I am unsure as I have not yet managed to track down the final (standard) word on the matter!

Hope this helps.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.