C++如何使用printf获取“一位数指数”

13

有没有一种方法可以以科学计数法打印指数部分少于3位的数字?格式化字符串6.1只影响数字部分而不影响指数部分:

var=1.23e-9;
printf ("%e\n", var);
printf ("%6.1e\n", var);

提供

1.230000e-009
1.2e-009

我也尝试过在wxWidgets中使用字符串格式化,但行为是相同的。

m_var->SetLabel(wxString::Format(wxT("%6.1e"),var));

我想要的是1.2e-9


7
根据标准和g++的要求,指数部分应该呈现为“两位数字”。 - Matteo Italia
5个回答

12
根据维基百科:Printf格式字符串
引用:

指数始终包含至少两个数字;如果值为零,则指数为00。在Windows中,默认情况下,指数包含三个数字,例如1.5e002,但这可以通过Microsoft特定的_set_output_format函数进行更改。

_set_output_format

7
我经常需要这样做(我编写文件解析器,一些文件格式如NITF要求您将数值存储为字符串)。
您所做的是基于十进制数学(科学计数法)实际含义的利用:对于所有实数y,y = (x) * 10^(N),其中N是某个整数,x在区间(-1, 1)中(不包括-1和1)。
因此,您可以执行以下操作:
void PrintScientific(double d)
{
   int exponent  = (int)floor(log10( fabs(d)));  // This will round down the exponent
   double base   = d * pow(10, -1.0*exponent);

   printf("%lfE%+01d", base, exponent);
}

您可以添加所有格式说明符,以控制“.”小数点前后的字符数。不要忘记四舍五入步骤!这是如何工作的,使用基数10和对数(在此处为基数10)的属性:

让y = x * 10^N =>
log(y) = log(x*10^N) =>
log(y) = log(x) + log(10^N) => // 来自Log "product"规则
log(y) = log(x) + N
由于x的范围为(-10, 10) -“()”表示排除(排除),这意味着log(x)的范围为(-1, 1)。因此,当我们向下取整进行整数转换时,我们会丢弃"log(x)"的贡献。然后,您可以从原始数字中获取“x”部分,从而使您可以以任何想要使用的科学记数法输出原始数字。

在处理靠近10的幂和次正规数的边角情况时,数学计算会导致不一致的结果,并且在d为0、INF、NAN时会出现问题。这也会给base带来不准确性。 - chux - Reinstate Monica

4
使用标准C的printf()无法完成此操作(默认使用三个数字似乎也是错误的),至少在C99中是这样(我手头没有更新版本)。来自C99标准的相关引用在7.19.6.1第8段,格式e,f:

...指数始终包含至少两位数字,以及必要的其他位数以表示指数。如果值为零,则指数为零。...

最好的方法是将其[可移植地]适应于使用这些输出的代码是使用C ++ IOStreams:虽然默认格式与C相同,但可以将自定义facet安装到流的std::locale中,以按您需要的方式进行格式化。话虽如此,编写格式化代码可能并不完全简单。尽管我可能只是基于标准转换然后删除e字符后多余的零。

1
我觉得Zach的回答是最快和最简单的方法,而且适用于任何操作系统。 但我发现,在“base =”行上需要进行两个修改,才能让它对所有数字起作用。(否则,在Cygwin中指数为负数时会出现nan)。额外的打印语句只是为了与patran中性文件兼容。如果我有足够的“声望”,我会投票赞成他的回答。
void PrintScientific(double d)
{
   int exponent  = (int)floor(log10( fabs(d)));  // This will round down the exponent
   double base      = (d * pow(10.0,  -1*exponent));

if(abs(exponent)<10)
    printf("%13.9lfE%+01d", base, exponent);
else
    printf("%12.9lfE%+01d", base, exponent);
}

0

C/C++在使用printf("%e",...)时至少需要两个指数数字。如果只想打印一个数字,并且要处理默认情况下至少打印3个数字的Visual Studio,则需要额外的代码。

考虑使用IOStreams @Dietmar Kühl

如果C++代码仍然想使用printf()样式的格式:

在调用printf()之前调整double的值往往会导致舍入问题、范围缩短以及一般的边角案例失败,如处理log10(0.0)。还要考虑到接近10的幂的大型double,其中log10()可能会不足,-0.0INFNAN

在这种情况下,最好对字符串进行后处理。

  double var = 1.23e-9;
  //                    - 1 . x e - EEEEE \0
  #define ExpectedSize (1+1+1+1+1+1+  5 + 1)
  char buf[ExpectedSize + 10];
  snprintf(buf, sizeof buf, "%.1e", var);
  char *e = strchr(buf, 'e');  // lucky 'e' not in "Infinity" nor "NaN"
  if (e) {
    e++;
    int expo = atoi(e);
    snprintf(e, sizeof buf - (e - buf), "%1d", expo);
  }
  printf("'%6s'\n", buf);  // '1.2e-9'

注意:%e 可以进行后处理,因为它的宽度不像 "%f" 那样难以处理。 sprintf(buf, "%f", DBL_MAX) 可能会有数千个 char

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接