Pregunta

I have a program written in C++ which is generating C source code for mathematical calculations. I have noticed that the constants take up very much space in the generated code and am looking for a more compact representation.

To generate constants, I am now using:

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

I am pretty sure that this is a lossless representation, but it is also very bloated. For example a zero and a one would be represented as something like 0.0000000000000000e+00 and 1.0000000000000000e+00. And "0." or "1." carries just as much information.

Is there a way to print constants to file in a more compact, but still lossless manner? It does not need to look good for a human reader, just compile when present in plain C code (if C99, I would prefer if it's also valid C++). Hexadecimal could be ok if it is portable.

EDIT: Removed std::fixed in code snippet.

¿Fue útil?

Solución 2

This is not a problem of representation, language or standard library but of algorithm. If you have a code generator then...why don't you change the generated code to be the best (= shortest with required precision) representation? It's what you do when you write code by hand.

In the hypothetical put_constant(double value) routine you may check what's the value you have to write:

  • Is it an integer? Don't bloat the code with std::fixed and set_precision, just cast to integer and add a dot.
  • Try to convert it to string with default settings then convert it back to double, if nothing changed then default (short) representation is good enough.
  • Convert it to string with your actual implementation, and check its length. If it's more than N (see later) use another representation otherwise just write it.

A possible (short) representation for floating point numbers when they have a lot of digits is to use their memory representation. With this you have a pretty fixed overhead and length won't ever change so you should apply it only for very long numbers. A naive example to show how it may work:

#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;
}

Otros consejos

You can use hexadecimal floating point (The format specifier %a for printf() in C); it's defined to preserve all bits of precision (C11, 7.21.6.1p8, a,A specifiers).

cfile << std::hexfloat << v;

If your compiler/standard library doesn't support hexfloat, you can use C99 %a printf specifier (this is equivalent, as specified in C++11 table 88 under section 22.4.2.2.2):

printf("%a", v);

For example, the following program is valid C99:

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

Your generated source file will not be valid C++11 as rather absurdly C++11 does not support hexadecimal floating point literals. However many C++11 compilers support C99 hexadecimal floating point literals as an extension.

First, you're contradicting yourself when you first say std::scientific, and then std::fixed. And second, you probably don't want either. The default format is generally designed to do this best. The default format doesn't have a name, nor a manipulator, but is what you get if no other format has been specified, and can be set (in case other code has set a different format) using:

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

I'd recomment using this. (You still need the precision, of course.)

I'm not sure you can pass floating points losslessly like this. Floating points are necessarily lossy. While they can represent a subset of values precisely you cannot include ALL the significant figures - different hardware may have different representations so you cannot guarantee no loss of information. Even if you could pass it all across as the value may not be representable by the receiving hardware.

A plain ofstream::operator<< would print out as many digits as required, though, so there isn't really a need to complicate matters.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top