-1

Sample code:

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

I.e. if the floating constant cannot be printed precisely using %f, then %a is used.

Question: what is the condition for "the floating constant cannot be printed precisely using %f"?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
pmor
  • 5,392
  • 4
  • 17
  • 36
  • 1
    I would say you never want to switch dynamically between `%f` and `%a`. If you want human-readable, use `%f`. If you want machine readable with perfect repeatability, use `%a`. What consumer won't mind this incompatible back-and-forth between the two? – Steve Summit Aug 19 '21 at 16:54
  • 2
    Can't be done. The constant in code is converted to a representable value at compile time. – dbush Aug 19 '21 at 16:55
  • This seems to reduce to [your previous question](https://stackoverflow.com/questions/68835588/how-to-determine-w-o-conversions-that-a-given-floating-constant-can-be-represent). If the compiler can't make the determination, then runtime is definitely much too late as the actual token from the source code is no longer available. – Nate Eldredge Aug 19 '21 at 17:02
  • @dbush To my knowledge, some representable values can be printed precisely unsing `%f`, some cannot. The idea is to use `%a` for "cannot" case. – pmor Aug 19 '21 at 17:02
  • @pmor What I'm saying is that distinction disappears once the code is compiled. – dbush Aug 19 '21 at 17:03
  • @pmor You have to be careful what you mean by "representable values" and "printed precisely". Many values cannot be represented precisely. Once a value has been stored in a `float` or `double`, it is also problematic to print it precisely. – Steve Summit Aug 19 '21 at 17:05
  • @NateEldredge As as understand: the floating constant cannot represented, however, the variable holding the nearest representable FP value can be precisely printed using `%f`. This question is not about "can / cannot represented", but about "keeping the absolute precision while automatically switching between %f and %a". – pmor Aug 19 '21 at 17:07
  • 1
    However, for any given floating-point type (e.g. `float` or `double`), I believe you can devise a `printf` precision such that the printed value, even if imprecise, will lead to the same value on the other end if a receiver uses `atof` or `strtod` or `scanf("%f")` to read it back in. The advantage of `%a` is it makes it *easier* to achieve end-to-end lossless transmission, without printfing possibly-unnecessary extra digits — but I believe you can also achieve end-to-end lossless transmission with `%e` or `%g`. (Probably not `%f`, though, or at least not without wasting tons of space.) – Steve Summit Aug 19 '21 at 17:07
  • Auto selecting between `%f` and `%a` is unlikely to be useful. A number like 1.25f is precise when printed with `%f`, yet 1.225f is not. 16777218.0f is, 16777219.0f is not. So the correct choice is to always use `%a`, or use some ridiculous precision with `%f`, like `%.70f`. – user3386109 Aug 19 '21 at 17:11
  • *To my knowledge, some representable values can be printed precisely using %f, some cannot.* False. Every `float` and `double` value can be printed perfectly precisely, if you're willing to use enough digits. (Although it might take 105 digits, even for a `float`.) But there are plenty of real numbers, and plenty of decimal fractions, that cannot be represented precisely as a `float` or `double`. – Steve Summit Aug 19 '21 at 17:12
  • So what would you want for `1.9999999999999999999999999997f`? The actual number cannot be represented exactly as `float`, but the nearest representable value is `2.0` whose exact value will be printed by the `%f` format specifier. – Nate Eldredge Aug 19 '21 at 17:13
  • @user3386109 Even `%70f` won't always be enough. But I believe `%10e` or `%10g` probably would be for a float, and 15-20 digits for a `double`. – Steve Summit Aug 19 '21 at 17:15
  • @SteveSummit You are correct that `%e` or `%g` is a better choice (to deal with numbers less than 1). But 10 digits is nowhere near enough. For example, the number 1.225000000000000088817841970012523233890533447265625f, is precisely representable as a `float`. – user3386109 Aug 19 '21 at 17:18
  • @SteveSummit _if you're willing to use enough digits_: I was talking about plain `%f`. C11: if the precision is missing, it is taken as 6. – pmor Aug 19 '21 at 17:18
  • @NateEldredge _what would you want for 1.9999999999999999999999999997f_: yes, the nearest representable value is 2.0 whose exact value will be printed by the %f format specifier. Hence, `2.0`. – pmor Aug 19 '21 at 17:20
  • @user3386109 I understand about numbers like 1.225000000000000088817841970012523233890533447265625. But if you print that guy out using, say, `%.6f`, and then convert the resulting string back to a `float`, under a good-quality implementation you will get precisely the same `float` value. – Steve Summit Aug 19 '21 at 17:22
  • @SteveSummit And therein lies the reason not to do what OP is attempting to do. Different implementations may have subtly different behaviors, and I believe they are within their rights under the specification to do so. – user3386109 Aug 19 '21 at 17:28
  • So you're asking whether a given `float` value would have its represented value printed exactly with a given number `n` of decimal places to `%f`, or the default `n=6`? (Not merely that the string printed out would be converted back to the same `float` value.) The numbers represented by binary floating-point are [dyadic rationals](https://en.wikipedia.org/wiki/Dyadic_rational), and a dyadic rational can be printed exactly with `n` decimal places if and only if it is an integer multiple of `2^(-n)`. Which you can check by simple computations with the significand and exponent. – Nate Eldredge Aug 19 '21 at 18:08
  • 1
    Update the question to explicit state the criterion for the selection. For example: (a) If x is the value in `float f`, and `%f` produces a numeral that is exactly x, use `%f`, otherwise use `%a`. (b) If x is the value in `float f`, and `%f` produces a numeral that, when converted to IEEE-754 binary32 (“single”) with correct rounding using round-to-nearest mode, use `%f`, otherwise use `%a`. (c) If x is the value in `float f`, and `%f` produces a numeral that, when converted to `float` using the current implementation’s rounding, use `%f`, otherwise use `%a`. – Eric Postpischil Aug 19 '21 at 18:17
  • (d) If x is a numeral, and `f` is a `float` that results from the source text `f = x;`, and `%f` produces a numeral that is exactly x, use `%f`, otherwise use `%a`. Hint: Don’t pick (d); it is essentially impossible. Edit the question to state the criterion clearly and explicitly. – Eric Postpischil Aug 19 '21 at 18:19
  • @user3386109: The fact that different implementations may have different behaviors is not a reason not to do what OP is attempting to do unless portability is their goal (not yours). OP has entered a campaign of questions showing they are engaging in some project which may well customize itself to various implementations, in which case lack of portability would not be a barrier. – Eric Postpischil Aug 19 '21 at 18:20
  • @user3386109 I said "I understand about numbers like 1.225000000000000088817841970012523233890533447265625", but we both goofed. Not that it really matters at this point, but I believe that's a `double`. I believe the `float` value you meant was 1.22500002384185791015625. – Steve Summit Aug 19 '21 at 18:32
  • 1
    @pmor It occurs to me that your question shares a lot in common with the classic and frequently-asked "How can I print floating-point numbers with maximum precision in minimum space?", and that some of the answers at [Printf width specifier to maintain precision of floating-point value](https://stackoverflow.com/questions/16839658/printf-width-specifier-to-maintain-precision-of-floating-point-value) may therefore be applicable. – Steve Summit Aug 19 '21 at 22:10

1 Answers1

1

what is the condition for "the floating constant cannot be printed precisely using %f"?

This answer refers to the floating point object (e.g. double) defined by the FP constant, not the original source code text.

When the output text/string does not round trip back to the original double, string is not precise enough.

If OP does not want to try various precsisons, then try "%f" once.

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

Above uses linear attempts. Re-write could try 1,2,4,8,16,.... and then bisect to the minimum.


Alternative, printf("%.*g\n", DBL_DECIMAL_DIG, x); to get close to OP's goal, but with simpler code.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Do you think that it is possible to determine that w/o is_round_trip_back_to_the_original check? For example, by using somehow `frexp`. – pmor Aug 19 '21 at 17:33
  • @pmor Yes, possible to do w/o is_round_trip_back_to_the_original check. Yet I see it as more work and error prone. Why not use `printf("%.*g\n", DBL_DECIMAL_DIG, x);` instead of `"%f"`. It is different, yet it always retains sufficient precision? – chux - Reinstate Monica Aug 19 '21 at 18:59
  • @pmor All finite `double` can be _exactly_ printed in decimal [somehow](https://codereview.stackexchange.com/q/212490/29485). Yet `printf()` may fail to print correctly past many significant digits - that is a quality of implementation issue. Highly portable code should not rely on printing more than `DBL_DECIMAL_DIG (17)` significant digits correctly. (I would not even count on more than `DBL_DIG (15)` significant digits correctly.) – chux - Reinstate Monica Aug 19 '21 at 19:11
  • @pmor More on printing with high significant precision: With `printf()` attempting to print `DBL_DECIMAL_DIG` significant or more digits, lessor quality implementations may print only _about_ correct, just zeros, or even junk digits. I’d still expect, even with errant output, the `atof()` of that string still result in a matching original `double` as there is a fair amount of tolerance to achieve round-trip in these least significant digits. – chux - Reinstate Monica Aug 19 '21 at 21:25
  • 1
    @pmor What makes your question so awkward is preferring `"%f"`, rather than `"%e"`, `"%a"`, or `"%g"`. `"%f"` takes the _floating_ out of presenting a `double` as it obliges a _fixed_ place decimal point. – chux - Reinstate Monica Aug 19 '21 at 21:26
  • Didn't get your last sentence (_"%f" takes the floating out of presenting a double as it obliges a fixed place decimal point._). Can you rephrase it? – pmor Sep 28 '21 at 17:02
  • @pmor (another good question) Floating point numbers, for the most part, have _relative_ precision. With `"%f"`, 1.0e300 prints hundreds of uninformative digits past the first 15-20. 1e-10 prints as 0.000000, quite useless as nearly half of all `double` print the same value. `"%f"` would make sense for a _fixed point_ type. – chux - Reinstate Monica Sep 28 '21 at 17:14