C / C ++中浮点常数的紧凑无损表示

时间:2013-03-01 11:08:22

标签: c++ c hex code-generation lossless

我有一个用C ++编写的程序,它为数学计算生成C源代码。我注意到常量在生成的代码中占用了很多空间,我正在寻找更紧凑的表示。

要生成常量,我现在正在使用:

double v = ...
cfile << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << v;

我很确定这是一种无损代表,但它也非常臃肿。例如,零和一个将表示为0.0000000000000000e + 00和1.0000000000000000e + 00。并且“0”或“1.”提供同样多的信息。

有没有办法以更紧凑但仍然无损的方式打印常量?对于人类读者来说,它不需要看起来很好,只需在纯C代码中进行编译(如果是C99,我更愿意,如果它也是有效的C ++)。十六进制可以是可移植的。

编辑:已移除代码段中的std::fixed

4 个答案:

答案 0 :(得分:9)

您可以使用十六进制浮点(The format specifier %a for printf() in C);它被定义为保留所有精度位(C11,7.21.6.1p8,a,A说明符)。

cfile << std::hexfloat << v;

如果您的编译器/标准库不支持hexfloat,则可以使用C99 %a printf说明符(这是等效的,如C ++ 11表88中第22.4.2.2节所述。 2):

printf("%a", v);

例如,以下程序有效C99:

#include <stdio.h>
int main() {
   double v = 0x1.8p+1;
   printf("%a\n", v);
}

您生成的源文件无效C ++ 11,因为相当荒谬的是C ++ 11不支持十六进制浮点文字。但是,许多C ++ 11编译器都支持C99十六进制浮点文字作为扩展。

答案 1 :(得分:3)

这不是表示,语言或标准库的问题,而是算法问题。如果您有一个代码生成器,那么...为什么不将生成的代码更改为最佳(=具有所需精度的最短)表示?这就是你手工编写代码时所做的事情。

在假设的put_constant(double value)例行程序中,您可以检查您必须编写的值

  • 是整数吗?不要使用std::fixedset_precision膨胀代码,只需强制转换为整数并添加一个点。
  • 尝试将其转换为默认设置的字符串,然后将其转换回double,如果没有任何更改,则默认(短)表示就足够了。
  • 将其转换为实际实现的字符串,并检查其长度。如果它超过N(见下文),请使用另一种表示,否则只需编写它。

当浮点数具有大量数字时,可能的(短)表示是使用内存表示。有了这个你有一个非常固定的开销,长度不会改变所以你应该只应用它很长的数字。一个简单的例子,说明它是如何工作的:

#define USE_L2D __int64 ___tmp = 0;
#define L2D(x) (double&)(___tmp=x)

int main(int argc, char* argv[])
{
    // 2.2 = in memory it is 0x400199999999999A

    USE_L2D
    double f1 = L2D(0x400199999999999A);
    double f2 = 123456.1234567891234567;

    return 0;
}

答案 2 :(得分:1)

首先,当你第一次说时,你自相矛盾 std::scientific,然后std::fixed。第二,你 可能也不想要。通常是默认格式 旨在做到最好。默认格式不是 有一个名字,也没有操纵者,但如果没有别的话,你会得到什么 格式已指定,可以设置(如果是其他代码 已设置不同的格式):

cfile.setf( std::ios_base::fmtflags(), std::ios_base::floatfield );

我建议使用它。 (你仍然需要精确度 当然。)

答案 3 :(得分:-4)

我不确定你可以像这样无损地传递浮点数。浮点必然是有损的。虽然它们可以精确地表示值的子集,但您不能包含所有有效数字 - 不同的硬件可能具有不同的表示,因此您无法保证不会丢失信息。即使您可以将其全部传递,因为接收硬件可能无法表示该值。

plain ofstream :: operator&lt;&lt;尽管如此,会打印出尽可能多的数字,因此实际上并不需要使问题复杂化。