【问题标题】:How to control the number of exponent digits after 'e' in C printf %e?如何控制 C printf %e 中 'e' 之后的指数位数?
【发布时间】:2015-09-28 16:03:15
【问题描述】:

我想控制 C printf %e 中 'e' 之后的指数位数?

比如Cprintf("%e")结果2.35e+03,但是我想要2.35e+003,我需要3位指数,怎么用printf?

代码:

#include<stdio.h>
int main()
{
    double x=34523423.52342353;
    printf("%.3g\n%.3e",x,x);
    return 0;
}

结果: http://codepad.org/dSLzQIrn

3.45e+07
3.452e+07

我想要

3.45e+007
3.452e+007

但有趣的是,我在 Windows 中使用 MinGW 得到了正确的结果。

【问题讨论】:

  • “Windows”行为不符合标准,VS2015 默认使用至少 2 个指数位(参见exponent formatting)。
  • @cremno 关于指数宽度的“Windows”不符合标准是什么?除了符合或不符合OP的目标吗?
  • @cremno 我看到“...指数始终包含至少两位数字,并且仅包含表示指数所需的更多数字。...”§7.21.6.1
  • @chux:是的,这就是它不符合 C99/C11 的原因。理由还包括:“对于%e,C99 阐明了小指数至少包含 2 位数字,但不超过表示值所必需的数字。在 C99 之前,不清楚是否允许任意数量的前导零。”
  • @cremno 太糟糕了,在 C99 的时候没有定义一个控制机制来解决这个问题。 IMO,至少 2 是一个奇怪的选择 - 可能是现有技术。 至少,规范应该定义一个预期最大宽度的常数。这将有助于调整"%e" 缓冲区的大小。其他代码需要ilog10(DBL_MAX_10_EXP)+1 之类的东西。感谢您的洞察力。

标签: c printf


【解决方案1】:

“...指数始终包含至少两位数字,并且仅包含表示指数所需的更多数字。...”C11dr §7.21.6.1 8

所以3.45e+07 是合规的(OP 不想要的),3.45e+007 是不合规的(OP 想要的)。

由于 C 没有为代码提供更改指数位数的标准方法,因此代码只能自生自灭。

各种编译器都支持一些控制。

visual studio _set_output_format

为了好玩,下面是DIY代码

  double x = 34523423.52342353;
  //                    - 1 . xxx e - EEEE \0
  #define ExpectedSize (1+1+1 +3 +1+1+ 4 + 1)
  char buf[ExpectedSize + 10];
  snprintf(buf, sizeof buf, "%.3e", x);
  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), "%05d", expo);  // 5 more illustrative than 3
  }
  puts(buf);

  3.452e00007

另见c++ how to get "one digit exponent" with printf

【讨论】:

  • 谢谢,我终于用_set_output_format 得到了同样的结果。
  • 首先,很好的答案!关于here这个问题,这个答案也很合适。我只是不确定它是如何工作的。 e in snprintf 是目标缓冲区,但 buf 被结果修改。怎么样?
  • @ryyker snprintf(buf, sizeof buf, "%.3e", x); 形成x 的指数字符串版本。所以buf 应该在指数数字之前有一个'e'。然后在'e' 之后从buf 读取代码以获取指数。然后它用snprintf(e, sizeof buf ... 将它返回 打印到buf,这次使用所需的格式。在这种情况下"%05d",在that case 中,使用"%d"。
  • 这个答案说服了我只使用搜索和替换来获得我需要的东西......谢谢!
【解决方案2】:

printf格式化标签原型:

%[flags][width][.precision][length]specifier

精度

... 这给出了...之后出现的位数 用于 a、A、e、E、f 和 F 转换的 radix 字符...。

您正确使用了转换和精度说明符,区别在于 C 库函数的实现和不同系统上的环境。 precision 指定'.' 之后的位数(点、句点等)。它不设置表示幂的字符数。它在 windows 上提供 3 digits 的事实只是 windows 指定格式的方式,而不是 C 标准库指定 printf 将起作用的方式。

需要比较源实现的不同之处才能了解该格式字符串所依赖的内容。 (它可能会归结为 windows v. linux/unix 环境/语言环境/等的定义或指定方式的一些模糊差异)

【讨论】:

  • 哦,我明白了,糟糕的 c/c++,它不是真正的跨平台。是由于操作系统或编译器的差异吗?是否有任何编译器与 win mingw 得到相同的结果?
  • 不是“糟糕的 C++”,而是“糟糕的微软”。它明确指定了 %e 等应该做什么,但 MS 给出了一个 sh*(在 VS 中有很多这样的事情,这只是悲伤)
  • 嗯,这就是与 实现定义 准相关的讨厌的事情发挥作用的地方。由于指数运算中的位数未指定为标准的一部分,因此不同的编译器可以随意实现该部分。甚至可能 mingw 实现与普通的 unix/linux glibc 实现完全相同,但是在 windows/mingw/linux/ 之间形成该部分所依赖的操作系统环境部分是不同的等等......这是一个艰难的。这就是为什么您需要弄清楚最终输出中实际使用了什么。 有趣...
  • @DavidC.Rankin:这根本不是实现定义的。 ISO C(至少 C99 和 C11,我相信还有 C89)指定了浮点格式的确切格式,而 MS 库是错误的。
  • 我的立场是正确的。我查看了手册页并浏览了标准,但一定错过了求幂的数字规范。老眼睛....
【解决方案3】:
char *nexp(double x, int p, int n) // Number with p digits of precision, n digits of exponent.
{
 const int NN=12;
 static char s[NN][256];//(fvca)
 static int i=-1;
 int j,e;

 i=(++i)%NN; // Index of what s is to be used...
 sprintf(s[i],"%.*lE", p,x); // Number...
 for(j=0; s[i][j]; j++) if(s[i][j]=='E') break; // Find the 'E'...
 if(s[i][j]=='E') // Found!
  {
   e= atoi(s[i]+j+1);
   sprintf(s[i]+j+1, "%+0*d", n+1,e);
   return s[i];
  }
 else return "***";
}


// Best Regards, GGa
// G_G

【讨论】:

  • i=(++i)%NN; - 请不要编写这样的代码(即在表达式中递增)。养成这种习惯会在未来导致问题。
  • 好的,改成:++i%=NN;
  • 同时使用 static 是个坏主意 - 因为它会阻止代码在 MT 环境中使用
  • i=(++i)%NN; 的行为未由 C 标准定义:“如果标量对象的副作用相对于同一标量对象的不同副作用或使用相同标量对象的值,行为未定义”(C 2018 6.5 2)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-30
  • 1970-01-01
  • 1970-01-01
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多