使用 C++ 打印带有一个前导零的指数表示法

Print exponential notation with one leading zero with C++

提问人:rhodysurf 提问时间:1/16/2014 最后编辑:rhodysurf 更新时间:1/17/2014 访问量:2817

问:

我正在生成一个文本文件,用作 FORTRAN 输入文件。FORTRAN 程序指定它读取的值必须采用这样的格式

1.0

必须打印为

0.1000000E+01

截至目前,我最接近使用 iostream 的是

1.000000E+00

使用代码

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

有谁知道获得如上所示的前导零的最佳方法,最好使用 ostream 而不是 printf?

C C 可视化 - C++ IOstream 指数

评论

1赞 Stefano Sanfilippo 1/16/2014
如果我必须迂腐,那么您的 FORTRAN 代码需要的不是科学记数法。
0赞 rhodysurf 1/16/2014
这是真的......但这是总结问题的最简单方法
0赞 Stefano Sanfilippo 1/16/2014
恐怕你将不得不自己编写一个函数,因为不幸的是,这是非标准的。也许修补 FORTRAN 程序会更简单?或者这不是一个选择?
1赞 luk32 1/17/2014
如果我必须迂腐并且是两个不同的数字。应该是吗?我的意思是,如果你的代码放了,就会发生一些插曲。1.000000E+010.100000E+011.000000E+00cout << 1 << endl;1.000000E+01
1赞 luk32 1/17/2014
此外,您的代码无论如何都会输出,因为不会使用 . 这给了.你真的运行了这段代码还是只是为了这个问题而编造了例子,哈哈?1cout << 1 << endl;operator<<(int)floatcout << 2.0 << endl;2.000000E+00

答:

1赞 chux - Reinstate Monica 1/16/2014 #1

C心想:

这不是一个很好的答案,因为首选C++答案。

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

弱点:如果小数点不是 '.' 或 INF 附近的 x,则会出现麻烦。

1赞 Stefano Sanfilippo 1/17/2014 #2

正如我所说,你问的是非标准的,但你可以通过一个技巧来实现:

#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;
}

包装器是必需的,因为您无法重新定义 。Double<<double

我没有测试这种分离方式和浮点的几率,也许你可以想出一个更好的选择,但你明白了这个想法:)exponentbase

评论

0赞 chux - Reinstate Monica 1/17/2014
很好的C++答案。我过去曾尝试过这种方法来处理类似的问题,但在接近 10 的幂值时遇到了麻烦。像 0.9999999999e-10 或 1.00000000001-10 这样的值会得出 和 的不匹配对,尤其是零件。您可能想尝试此类测试用例。exponentbasefloor(log10...
0赞 rhodysurf 1/17/2014
我正在查看缓存的版本。出于某种原因,第一个 if 语句 where 告诉我没有运算符与这些操作数匹配x==0
0赞 Stefano Sanfilippo 1/17/2014
对不起,这是一个错别字,我在一分钟左右之前修复了它。立即尝试使用代码。
0赞 rhodysurf 1/17/2014
谢谢,它现在可以工作了。我有点生气自己没有抓住这个简单的错误。
0赞 chux - Reinstate Monica 1/17/2014
@Stefano 像 0.9999999999999 这样的值可以很好地分解:frexp10 或 frexp,但有限精度设置(此处为 6)会导致四舍五入并打印“1.000000”。 不知道这个小数点偏移,并打印其原始值。四舍五入实际上是一种数学运算,也应该影响 ,但不会,这在这种方法中留下了一个小漏洞。stream << baseexponentbaseexponent
0赞 n. m. could be an AI 1/17/2014 #3
  1. 创建一个不打印小数点的区域设置方面,并对其进行填充。
  2. cout << "0." << setprecision(6) << fixed << scientific << uppercase << number * 10;

评论

0赞 rhodysurf 1/17/2014
这可行,但为打印的每个数字设置和重置 ios 标志似乎效率较低
0赞 chux - Reinstate Monica 1/17/2014
这会在“0”之后打印“-”号吗?
0赞 n. m. could be an AI 1/17/2014
我认为您可以打电话而不是<<,而不是一直重置所有内容。.write("0.", 2)
0赞 n. m. could be an AI 1/17/2014
@chux:是的,我的错误;)因此,您也需要手动打印,并在刻面中抑制它。-