1

I am generating a text file to be used as a FORTRAN input file. The FORTRAN program specifies that the values it reads must be in a format such that

1.0

must be printed as

0.1000000E+01

As of right now the closest I have gotten in using iostream is

1.000000E+00

with the code

cout << setprecision(6) << fixed << scientific << uppercase;
_set_output_format(_TWO_DIGIT_EXPONENT);
cout << 1.0 << endl;

Does anyone know the best way to get a leading zero as shown above, preferably using ostream instead of printf?

rhodysurf
  • 357
  • 1
  • 4
  • 12
  • 1
    If I have to be pedantic, what your FORTRAN code requires is not scientific notation. – Stefano Sanfilippo Jan 16 '14 at 15:31
  • That is true... but that was the easiest way to summarize the problem – rhodysurf Jan 16 '14 at 15:33
  • I fear you will have to write a function by yourself, since that is non-standard, unfortunately. Maybe patching the FORTRAN program could be simpler? Or that's not an option? – Stefano Sanfilippo Jan 16 '14 at 15:34
  • Patching the FORTRAN is not an option because it is optimized for CFD processing. – rhodysurf Jan 16 '14 at 15:37
  • 1
    If I have to be pedantic `1.000000E+01` and `0.100000E+01` are two different numbers. Should it be `1.000000E+00`? I mean if your code `cout << 1 << endl;` put `1.000000E+01`, there is something intersting going on. – luk32 Jan 16 '14 at 16:16
  • 1
    Moreover your code will output just `1` anyways because `cout << 1 << endl;` will use `operator<<(int)` not `float`. `cout << 2.0 << endl;` this gives `2.000000E+00`. Did you really run this code or just made up example, for the sake of the question, lol ? – luk32 Jan 16 '14 at 16:30
  • As @luk32 points out, the code in your question prints just `1`. Please update your question to show code that actually illustrates the problem. – Keith Thompson Jan 16 '14 at 16:36
  • I really ran it.. just with 1.0. I edited the question. The actual impementation used all precise (6 digits) numbers so this was just oversight. @luk32 – rhodysurf Jan 16 '14 at 16:41

3 Answers3

1

C thought:

Not a great answer because C++ answer preferred.

char buf[20];
buf[0] = ' ';
double x = -1.234567;
sprintf(&buf[1], "% .6E", x*10);
if (buf[3] == '.') {  // detect if x was INF or NAN
  buf[0] = buf[1];
  buf[1] = '0';
  buf[3] = buf[2];
  buf[2] = '.';
}

// Cope with leading potential space if needed
if (buf[0] == ' ') memmove(&buf[0], &buf[1], strlen(buf));

printf("%s\n", buf);
// -0.1234567E+00

Weakness: Trouble if decimal point is not '.' or x near INF.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

As I said, what you ask is non-standard, but you can achieve that with a trick:

#include <iostream>
#include <iomanip>
#include <cmath>

class Double {
public:
    Double(double x): value(x) {}
    const double value;
};

std::ostream & operator<< (std::ostream & stream, const Double & x) {
    // So that the log does not scream
    if (x.value == 0.) {
        stream << 0.0;
        return stream;
    }

    int exponent = floor(log10(std::abs(x.value)));
    double base = x.value / pow(10, exponent);

    // Transform here
    base /= 10;
    exponent += 1;

    stream << base << 'E' << exponent; // Change the format as needed

    return stream;
}

int main() {
    // Use it like this
    std::cout << std::setprecision(6) << std::fixed;
    std::cout << Double(-2.203e-15) << std::endl;
    return 0;
}

The Double wrapper is needed because you cannot redefine << for double.

I did not test that way of separating exponent and base against the odds of floating point, maybe you can come up with a better alternative, but you get the idea :)

Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80
  • Good C++ answer. I've tried such an approach in the past to deal with similar problems but had trouble with values _close_ to powers of 10. Values like 0.9999999999e-10 or 1.00000000001-10 come up with a mis-match pair for `exponent` and `base`, especially the `floor(log10...` part. You may want to try such test cases. – chux - Reinstate Monica Jan 16 '14 at 16:21
  • I was viewing a cached verison. For some reason the first if statement where `x==0` is telling me that no operator matches these operands – rhodysurf Jan 16 '14 at 16:27
  • Sorry, it was a typo, I fixed it a minute ago or so. Try with the code now. – Stefano Sanfilippo Jan 16 '14 at 16:28
  • Thank you, it now works. I'm a little mad at myself for not catching that simple error. – rhodysurf Jan 16 '14 at 16:31
  • @Stefano Values like 0.999999999999 get broken up nicely either way: frexp10 or frexp, but the finite precision setting (6 here), causes `stream << base` to round and print "1.000000". `exponent` does not _know_ of this decimal point shift and prints its original value. It is the _rounding_ of `base`, effectively a math operation, that should also affect `exponent`, but doesn't, that leaves a small hole in this approach. – chux - Reinstate Monica Jan 16 '14 at 16:42
  • The exponent value is not the desired format either but this is a good starting point. – rhodysurf Jan 16 '14 at 16:46
  • Yes, you can adjust the `stream << base << 'E' << exponent;` part to suit your needs. @chux objection is correct, unfortunately I couldn't come up with any simple method to perform such an adjustment. – Stefano Sanfilippo Jan 16 '14 at 16:46
  • Please don't edit answers by other users with radical changes like that one :) Besides, you can apply modifiers to the line in the comment above, without those `if`s. Just use `std::setfill`. – Stefano Sanfilippo Jan 16 '14 at 17:04
  • Sorry i'm new here, will remember for future :) – rhodysurf Jan 16 '14 at 17:08
0
  1. Create a locale facet that prints NO decimal point, and imbue it.
  2. cout << "0." << setprecision(6) << fixed << scientific << uppercase << number * 10;
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243