【问题标题】:Is there a format string with sprintf that pads unless a float is whole?除非浮点数是完整的,否则是否存在带有 sprintf 的格式字符串?
【发布时间】:2021-07-18 11:47:56
【问题描述】:

我希望使用sprintf 来格式化一些浮点值,但我似乎无法正确格式化。 我试过“%1.6f”作为格式字符串,但这不是我想要的。 带有有效数字的“g”说明符似乎对我需要填充的其他情况也没有帮助。

"1.6f"
Actual output: 1.000000
Expected Output: 1

"g"
Actual output: 1.1
Expected Output: 1.100000

【问题讨论】:

  • 对我来说(由于浮点数在计算机上的工作方式),11.000 传达不同的信息:1 完全是 1 毫无疑问; 1.000 1 到 3dp 附近的某个数字,但不完全是 1。现在加上Is floating point math broken?,得到一个整数的机会就很小了。获得所需内容的最佳机会是打印到缓冲区并检查内容,将缓冲区修改为所需的表示形式。
  • 您想要"%g" 格式说明符...
  • @RichardCritten "获得整数的机会非常小" --> 不算小,40% 以上的float 是整数。
  • @chux-ReinstateMonica 假设 32 位整数和浮点数。那么任何小于 1 或大于 2^32 的浮点数都不能有整数表示,我认为这远大于所有浮点数的 40%。
  • @RichardCritten OP 的问题是关于整数:“...除非浮点数是整数?”。在那种情况下,integer 不限于 32 位 int,IAC,因为常见的float 1,778,384,894 是整数。大约 3% 的 float 具有 32 位 int 值。

标签: c++ floating-point format-specifiers


【解决方案1】:

由于没有打印说明符来满足 OP 的详细需求("%g" 已接近),因此需要进行一些测试。

要满足“除非浮点数是完整的”,请使用modf()(或float 在C 中的modff())测试值是否完整。该函数返回小数部分。

// Illustrative code with printf vs sprintf
void mzkoops56_print(float x) {
  float ipart; 
  if (modf(x, &ipart) == 0.0) {
    printf("%.0f\n", x);
  } else {
    printf("%.6f\n", x);
  }
}

这将打印非整数值,例如 0.999996、1.0000004,它们接近带有小数的整数值。

mzkoops56_print(nextafterf(1.0,0));
mzkoops56_print(1.0);
mzkoops56_print(nextafterf(1.0,2));
mzkoops56_print(1.1f);

1.000000
1
1.000000
1.100000

【讨论】:

    【解决方案2】:

    是否有一个带有 sprintf 的格式字符串,除非浮点数是完整的?

    不,没有。您必须自己编写此类功能。该算法基本上是:

    • 使用%.6f 打印到临时字符串
    • 检查该字符串是否以.000000 结尾
      • 如果是这样,删除.000000

    【讨论】:

    • 如果是 0.9999996 这样的非整数值,那么这将导致 "1" 而不是 "1.000000"
    • @chux-ReinstateMonica 这可能是也可能不是 OP 想要的。不太清楚。
    【解决方案3】:

    没有标准的说明符,但 GNU C 支持自定义说明符。这是一个示例实现:

    #include <printf.h>
    
    #include <cmath>
    #include <cstdio>
    
    int double_format(FILE *stream, const printf_info *info,
                      const void *const *args) {
      const double d = **reinterpret_cast<double const *const *>(args);
    
      if (std::trunc(d) == d) {
        return std::fprintf(stream, "%g", d);
      }
    
      int width = info[0].width;
    
      if (width == -1) width = 6;  // defaults to 6
    
      return std::fprintf(stream, "%.*f", width, d);
    }
    
    int double_format_arg_info(const struct printf_info *, size_t n, int *argtypes,
                               int *size) {
      if (n > 0) {
        argtypes[0] = PA_DOUBLE;
        size[0] = sizeof(double);
      }
      return 1;
    }
    
    int main() {
      register_printf_specifier('F', double_format, double_format_arg_info);
      std::printf("%4F && %4F\n", 1.0, 1.1);
    }
    

    输出是1 &amp;&amp; 1.1000

    Demo

    【讨论】:

    • but GCC and Clang support 这不是编译器的特性,而是标准库的特性。 GNU C 库支持覆盖 printf 说明符,与编译器无关。
    • @KamilCuk 感谢您的反馈。我编辑了答案。
    猜你喜欢
    • 1970-01-01
    • 2021-01-27
    • 1970-01-01
    • 2019-02-23
    • 2020-06-27
    • 2019-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多