【问题标题】:How to automatically select between %f and %a w/o losing precision?如何在不丢失精度的情况下自动选择 %f 和 %a?
【发布时间】:2021-10-21 09:29:58
【问题描述】:

示例代码:

void print_f( float f )
{
   ...
}

int main( void )
{
    print_f( 1.0f );      // prints 1.0
    print_f( 29.0f );     // prints 29.0
    print_f( 29.13f );    // prints 0x1.d2147ap+4
    print_f( 256.0f );    // prints 256.0
    print_f( 256.1f );    // prints 0x1.00199ap+8
    // and so on
    return 0;
}

即如果使用%f无法精确打印浮动常量,则使用%a

问题:“使用%f无法精确打印浮点常量”的条件是什么?

【问题讨论】:

  • 我想说你永远不想在%f%a 之间动态切换。如果您想要人类可读,请使用%f。如果您希望机器可读且具有完美的可重复性,请使用%a。哪个消费者不会介意这两者之间的这种不相容的来回?
  • 无法完成。代码中的常量在编译时转换为可表示的值。
  • 但是,对于任何给定的浮点类型(例如 floatdouble),我相信您可以设计一个 printf 精度,这样即使不精确,打印的值也会导致如果接收方使用atofstrtodscanf("%f") 将其读回,则在另一端读取相同的值。%a 的优点是它更容易实现结束端到端无损传输,无需打印可能不必要的额外数字——但我相信您也可以使用%e%g 实现端到端无损传输。 (不过,可能不是%f,或者至少不会浪费大量空间。)
  • 更新问题以明确说明选择的标准。例如: (a) 如果 x 是 float f 中的值,并且 %f 产生的数字正好是 x,则使用 %f,否则使用 %a。 (b) 如果 x 是 float f 中的值,并且 %f 产生一个数字,当使用舍入到最近模式正确舍入转换为 IEEE-754 binary32(“单个”)时,使用 %f,否则使用%a。 (c) 如果 x 是float f 中的值,并且%f 生成一个数字,当使用当前实现的舍入转换为float 时,使用%f,否则使用%a
  • @pmor 我突然想到,您的问题与经典且经常被问到的“如何在最小空间内以最大精度打印浮点数?”有很多共同点,并且有些因此,Printf width specifier to maintain precision of floating-point value 的答案可能适用。

标签: c floating-point printf precision c11


【解决方案1】:

“使用 %f 不能精确打印浮点常量”的条件是什么?

这个答案是指由 FP 常量定义的浮点对象(例如double),而不是原始源代码文本。

当输出的文本/字符串没有往返返回原始double时,字符串不够精确。

如果 OP 不想尝试各种精确度,请尝试"%f" 一次。

void print_f(double x) {
  char buf[1000];
  for (int prec = 0; ; prec++) {
    int len = snprintf(buf, sizeof buf, "%.*f", prec, x);
    if (len < 0 || (unsigned) len >= sizeof buf) break; 
    double y = atof(buf);
    if (x == y) {
      puts(buf);
      return;
    }
  }
  printf("%a\n", x);
}

以上使用线性尝试。重写可以尝试1,2,4,8,16,....然后平分到最小。


替代方案,printf("%.*g\n", DBL_DECIMAL_DIG, x); 接近 OP 的目标,但代码更简单。

【讨论】:

  • 您认为可以确定没有 is_round_trip_back_to_the_original 检查吗?例如,通过某种方式使用frexp
  • @pmor 是的,可以不进行 is_round_trip_back_to_the_original 检查。然而,我认为它的工作量更大且容易出错。为什么不使用printf("%.*g\n", DBL_DECIMAL_DIG, x); 而不是"%f"。它是不同的,但它始终保持足够的精度?
  • @pmor 所有有限的double 都可以完全以十进制的somehow 打印。然而printf() 可能无法正确打印超过许多有效数字 - 这是实施质量问题。高度可移植的代码不应依赖于正确打印超过DBL_DECIMAL_DIG (17) 有效数字。 (我什至不会指望超过DBL_DIG (15) 有效数字。)
  • @pmor 更多关于以高有效精度打印:printf() 尝试打印 DBL_DECIMAL_DIG 有效或更多数字,出租质量实施可能仅打印 about 正确,只是零,甚至是垃圾数字。我仍然希望,即使输出错误,该字符串的 atof() 仍然会产生匹配的原始 double,因为在这些最低有效数字中实现往返有相当大的容差。
  • @pmor 是什么让你的问题如此尴尬的是更喜欢"%f",而不是"%e""%a""%g""%f"floating 排除在显示 double 之外,因为它需要 fixed 位小数点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
  • 2011-09-26
  • 1970-01-01
  • 2018-12-08
  • 1970-01-01
相关资源
最近更新 更多