【问题标题】:Print exponential notation with one leading zero with C++使用 C++ 打印带有前导零的指数符号
【发布时间】:2014-02-05 14:35:52
【问题描述】:

我正在生成一个用作 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?

【问题讨论】:

  • 如果我必须迂腐,你的 FORTRAN 代码需要的不是科学记数法。
  • 没错……但这是总结问题的最简单方法
  • 我担心你将不得不自己编写一个函数,因为那是非标准的,不幸的是。也许修补 FORTRAN 程序可能更简单?或者那不是一个选择?
  • 如果我必须学究,1.000000E+010.100000E+01 是两个不同的数字。应该是1.000000E+00?我的意思是,如果你的代码 cout &lt;&lt; 1 &lt;&lt; endl; 放入 1.000000E+01,就会发生一些有趣的事情。
  • 此外,您的代码将只输出1,因为cout &lt;&lt; 1 &lt;&lt; endl; 将使用operator&lt;&lt;(int) 而不是floatcout &lt;&lt; 2.0 &lt;&lt; endl; 这给出了2.000000E+00。为了这个问题,你真的运行了这段代码还是只是编造了一个例子,哈哈?

标签: c++ c visual-c++ iostream exponential


【解决方案1】:

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

#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 重新定义&lt;&lt;

我没有测试将 exponentbase 与浮点数分开的方法,也许你可以想出一个更好的选择,但你明白了 :)

【讨论】:

  • 良好的 C++ 答案。我过去曾尝试过这种方法来处理类似的问题,但在值 close 接近 10 的幂时遇到了问题。像 0.9999999999e-10 或 1.00000000001-10 这样的值会出现不匹配exponentbase 的配对,尤其是 floor(log10... 部分。您可能想尝试这样的测试用例。
  • 我正在查看缓存的版本。出于某种原因,x==0 的第一个 if 语句告诉我没有运算符匹配这些操作数
  • 对不起,这是一个错字,我大约在一分钟前修正了它。立即尝试使用代码。
  • 谢谢,它现在可以工作了。我对自己没有发现那个简单的错误有点生气。
  • @Stefano 像 0.999999999999 这样的值可以很好地分解:frexp10 或 frexp,但有限精度设置(此处为 6)导致 stream &lt;&lt; base 舍入并打印“1.000000”。 exponent知道这个小数点移位并打印其原始值。 base四舍五入,实际上是一种数学运算,应该也会影响exponent,但不会,这会在这种方法中留下一个小漏洞。
【解决方案2】:

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

弱点:如果小数点不是'.'会出现问题或 x 在 INF 附近。

【讨论】:

    【解决方案3】:
    1. 创建一个不打印小数点的语言环境构面,并灌输它。
    2. cout &lt;&lt; "0." &lt;&lt; setprecision(6) &lt;&lt; fixed &lt;&lt; scientific &lt;&lt; uppercase &lt;&lt; number * 10;

    【讨论】:

    • 这行得通,但为每个打印的数字设置和重置 ios 标志似乎效率较低
    • 这会在“0”之后打印“-”符号吗?
    • 我认为您可以致电 .write("0.", 2) 而不是
    • @chux: 是的,我的错误 ;) 所以你也需要手动打印-,并在侧面隐藏它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-24
    • 1970-01-01
    • 2022-01-26
    • 2017-11-16
    相关资源
    最近更新 更多