#1212 c99, printf and float zero precision

closed-fixed
2008-10-18
2008-09-29
Roumen Petrov
No

The test program:
#include <stdio.h>

int main () {
float f = 0.003;
printf("%.*f\n", -1, f);
printf("%.*f\n", 1, f);
printf("%.*f\n", 0, f);
printf("%.f\n" , f);
printf("%.0f\n" , f);
printf("%#.0f\n", f);

printf("%.*a\n", -1, f);
printf("%.*a\n", 1, f);
printf("%.*a\n", 0, f);
printf("%.a\n" , f);
printf("%.0a\n" , f);
printf("%#.0a\n", f);

printf("%a\n", 30.0) ;
printf("%A\n", 30.0) ;
printf("%.2a\n", 30.0);
printf("%.2A\n", 30.0);
return(0);
}

This program is compiled with following options:
1) -posix -lmingwex
2) without options run on os with msvcrt 7.0x
3) -specs=msvcr90 (as is described in wiki to link with msvcr90)

On the first group of test the output is (/ separate results):
0.003000 (same in all cases )

0.0 (same in all cases )

0. / 0 / 0

0. / 0 / 0

0. / 0 / 0

0. (same in all cases )

Mingw output extra dot and it seems to me that this is not compatible with c99.

The second group of the test
0x1.89374cp-9 / a / 0x1.89374cp-9

0xc.5p-12 / a /0x1.9p-9

0xcp-12 / a / 0x1p-9

0xcp-12 / a / 0x1p-9

0xcp-12 / a / 0x1p-9

0xc.p-12 / a / 0x1.p-9
Ms
vcrt 7.0 don't know %a format specifier. No idea what is correct output for c99 standard, but dot in output looks good.

The third group:
0x1.ep+4 / a /
0x1.e00000p+4
0X1.EP+4 / a /
0X1.E00000P+4
0xf.00p+1 / a /
0x1.e0p+4
0XF.00P+1 / a / 0X1.E0P+4
I think that mingw and msvcr90 fail on zero precision output(%a). The expected result is 0xfp+1.
On the "%.2a" mingw looks good, msvcr90 fail.

The question is whether mingwex output (rt version 3.15) is correct in first and third cases ?
What is expected output for the second case ?

Discussion

  • Keith Marshall
    Keith Marshall
    2008-10-18

    • labels: --> mingw runtime (deprecated use WSL)
    • milestone: --> IINR_-_Include_In_Next_Release
    • assigned_to: nobody --> keithmarshall
    • status: open --> closed-fixed
     
  • Keith Marshall
    Keith Marshall
    2008-10-18

    There are three issues here:--

    1) Unless you've seriously messed up your GCC specs file, you don't need `-lmingwex'; *all* MinGW compiled programs link it implicitly.

    2) There *is* a bug, in the "%f" formatting case, where the precision is explicitly zero, and the value to be formatted is less than 0.5; identified and resolved, but see detail below.

    3) There is no bug in the "%a" cases; with the exception of earlier MSVCRT version, (which lack support for this feature), all cases illustrate complete compliance with both ANSI-C99 and POSIX. Even the difference between MinGW and MSVCR90 in the "%.2a" case does *not* represent a failure of either. The standard does not require any specific alignment for the mantissa, beyond having exactly one non-zero hexadecimal digit preceding the radix point; thus `0xf.00p+1', `0x7.80p+2', `0x3.c0p+3' and `0x1.e0p+4' are equally acceptable representations of decimal 30.0. Similarly, for the zero precision case, differing alignment makes variation in the one retained hexadecimal digit a valid outcome; here, the MinGW implementation attempts to keep as many significant bits as possible, but it isn't required to do so -- note that, in your second test group, the value being truncated is `0x1.8p-9', and this can equally well be expressed as `0x3p-10', `0x6p-11' or `0xcp-12', (or even as `0x1p-9', with loss of one bit of precision, in the most significant four).

    To correct the bug in case (2), I've committed:--

    Remove extraneous radix point in printf( "%.0f", v ) for v < 0.5

    * mingwex/stdio/pformat.c (__pformat_emit_float): Always make output
    of radix point conditional on precision != 0 or `#' flag specified.

    Curiously, this bug didn't manifest in my test build, compiled natively to run on GNU/Linux. The difference is that, on Linux I used `fcvt()' to encode the ASCII output string, whereas in MinGW we use `gdtoa()'. I'd naively assumed they would give the same results, and in general they do, but in the particular case of an input value less than 0.5, with zero precision, `fcvt()' says to encode it as "0", with one digit preceding the radix point, while `gdtoa()' says it's an empty string, with zero digits preceding the radix point. This may be a bug in `gdtoa()', but it isn't difficult to handle it in `__pformat_emit_float()', and I've chosen to do so.