如何设置std::scientific的格式

How to set format of std::scientific

我想将 12345.0 打印为 0.1235e+05。但是下面的代码给出了 1.23450e+04。有没有办法控制 std::scientific?

的格式
#include <iostream>
#include <string>
#include <sstream>


int main()
{
    double a = 12345.0;

    std::stringstream s;

    s.precision(3);

    s << std::scientific;

    s << a;

    std::cout << s.str() << std::endl;

    return 0;
}

输出为

1.235e+04

我也试过这里的代码Print exponential notation with one leading zero with C++但是四舍五入有问题。如果我将精度设置为 4,它不会四舍五入并给出 0.1234E5,但它应该是 0.1235E5。

有什么解决办法吗?

那不是科学形式。科学形式必须产生 1-10 之间的数字并乘以 10
的幂 资料来源:http://chemed.chem.purdue.edu/genchem/topicreview/bp/ch1/scinot.html

我通过添加两行代码更正了link Print exponential notation with one leading zero with C++中给出的解决方案中的错误。我还重复了正确格式的过程:

int p = stream.precision();
base = round(base * pow(10, p)) / pow(10, p);

它现在进行了正确的舍入并给出了 0.1235E5。这是完整的代码:

#include <iostream>
#include <string>
#include <sstream>
#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.0;
    int p = stream.precision();  // This line is added to correct the code
    base = round(base * pow(10, p)) / pow(10, p); // This line is added to correct the code
    exponent += 1;

    double x2 = base * pow(10, exponent);

    exponent = floor(log10(std::abs(x2)));
    base = x2 / pow(10, exponent);
    // Transform here
    base /= 10.0;
    base = round(base * pow(10, p)) / pow(10, p); // This line is added to correct the code
    exponent += 1;

    stream << base << "+e" << exponent;


    return stream;
}


int main()
{
    //double a = 12345.0;
    double a = 99995.01;

    std::stringstream s;

    s.precision(4);

    s << Double(a);

    std::cout << s.str() << std::endl;

    return 0;
}

重新发明 double 到字符串转换的一个问题是许多极端情况。

考虑将 99995.0 附近的值打印到 4 个位置

void foo(double value, int precision) {
  if (value == 0.) {
    printf("%g\n", 0.0);
  }

  int exponent = (int) floor(log10(fabs(value)));
  double base = value / pow(10, exponent);

  base /= 10.0;
  int p = precision;
  base = round(base * pow(10, p)) / pow(10, p);
  exponent++;

  printf("%.*fE%d\n", p, base, exponent);
  bar(value, precision);
}

foo(99994.99, 4);
foo(99995.01, 4);

版画

0.9999E5
1.0000E5

而不是希望

0.9999E5
0.1000E6

与其尝试计算浮点到字符串的库转换,不如简单地post将字符串处理为所需的格式。

下面是C,但是OP正在寻找C++,所以以此为指导。

void bar(double value, int precision) {
  precision--;
  char buf[400];
  snprintf(buf, sizeof buf, "%.*e", precision, value);

  if (isfinite(value)) {
    char *e = strchr(buf, 'e');
    char *first_digit = e - precision - 2;
    int expo = atoi(e + 1) + 1;
    printf("%.*s0.%c%.*sE%d\n", !isdigit((unsigned char )buf[0]), buf,
        *first_digit, precision, first_digit + 2, expo);
  } else {
    printf("%s\n", buf);
  }
}

bar(99994.99, 4);
bar(99995.01, 4);

版画

0.9999E5
0.1000E6

“如果我将精度设置为 4,它不会舍入并给出 0.1234E5,但它应该是 0.1235E5。”

这是因为默认舍入模式是“舍入到最近,并列到偶数”,而 OP 希望“舍入到最近,并列到远”。

base = round(base * pow(10, p)) / pow(10, p); 等代码可能会在 select 情况下实现 OP 的目标,因为此处的乘法和除法可能会由于不精确而导致舍入,有时会在所需的方向上舍入。这在所有 double 中都不可靠,无法实现“舍入到最近,关系到远”。