http://qs1969.pair.com?node_id=11122363

syphilis has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

This has been (partially, sort of) cross-posted at https://github.com/Perl/perl5/issues/18170.

According to the perl source's uconfig.h:
/* Gconvert: * This preprocessor macro is defined to convert a floating point * number to a string without a . This * emulates the behavior of sprintf("%g"), but is sometimes much mo +re * efficient. If gconvert() is not available, but gcvt() drops the * trailing decimal point, then gcvt() is used. If all else fails, * a macro using sprintf("%g") is used. Arguments for the Gconvert * macro are: value, number of digits, whether trailing zeros shoul +d * be retained, and the output buffer. * The usual values are: * d_Gconvert='gconvert((x),(n),(t),(b))' * d_Gconvert='gcvt((x),(n),(b))' * d_Gconvert='sprintf((b),"%.*g",(n),(x))' * The last two assume trailing zeros should not be kept. */
Unfortunately, on Ubuntu-18.04, the Gconvert macro uses gcvt() - ie the second option.
It's unfortunate because, on Ubuntu-18.04, it produces (for perls whose nvtype is 'double):
$ perl -le 'printf "%.54g\n", 0.3;' 0.29999999999999999
Contrast that with(eg) Windows and freebsd-12.0, both of which correctly output:
0.299999999999999988897769753748434595763683319091796875
The issue is that I want the Gconvert macro on my Ubuntu-18.04 perls (when nvtype is double) to use sprintf() instead of gcvt().
That way, I'll get the correct output, as opposed to the truncated garbage that is currently being displayed.

The question is: "How do I realize that objective ?"
Can I do it via a configure argument when building perl?
Or do I need to do it by hacking the perl source ?

It's a fairly simple hack, if that's what's required.
But I'd prefer to do it via a configure argument, if that's possible.

Cheers,
Rob

PS
I also wonder if people think that this current behaviour on Ubuntu-18.04 should be fixed.
I think it should ... but I shy away from pushing the case because I have this notion that the vast (and I mean VAST) majority of perl practitioners really do not care about it, and would be quite happy for the current behaviour to continue.

Replies are listed 'Best First'.
Re: Influencing the Gconvert macro
by Corion (Patriarch) on Sep 30, 2020 at 12:31 UTC

    From this comment:

    * d_Gconvert='gconvert((x),(n),(t),(b))' * d_Gconvert='gcvt((x),(n),(b))' * d_Gconvert='sprintf((b),"%.*g",(n),(x))'

    ... I assume that you should be able to influence the value of the Gconvert macro by supplying a value on the ./Configure command line:

    sh ./Configure -des -DGconvert='sprintf(...)'
    # or maybe
    d_Gconvert='sprintf(...)' sh ./Configure -des

    (but I have not tested this, nor do I really know how to determine if what I see is correct ;) )

    The "real fix", I guess, would be to extend the Configure unit for Gconvert (likely d_gconvert.U) to check the output of the respective C programs and then choose an appropriate value...

    Update: See below for Tux approach of setting up a Policy.sh file, to evade quoting problems etc

      you should be able to influence the value of the Gconvert macro by supplying a value on the ./Configure command line

      Excellent!! Following your suggested possible candidates, I found that the configure arg -Dd_Gconvert='sprintf((b),"%.*g",(n),(x))' seems to be doing the trick with perl-5.33.2.
      sh Configure -des -Duse64bitall -Dprefix=/home/sisyphus/perl-5.33.2-d +-Uversiononly -Dusedevel -Dd_Gconvert='sprintf((b),"%.*g",(n),(x))' & +& make ..... Everything is up to date. Type 'make test' to run test suite. $ ./perl -I./lib -le 'printf "%.54g\n", 0.3;' 0.299999999999999988897769753748434595763683319091796875 sisyphus@sisyphus5-desktop:~/comp/perl-5.33.2$ ./perl -I./lib -le 'pri +ntf "%.254g\n", 1.7e+251;' 1700000000000000056638039423376108468948472250210191793353461791741809 +721595401553113821670191901996837100770654163405637701161634938299172 +691569638797131996693245937962742921605027934132599151410492365393458 +53734303029546817799537579060533492478640128
      I haven't run make test yet. It's getting late, so I'll leave that until the morning.

      Cheers,
      Rob

      If you can't or won't spend time in fixing compline/d_gconvert.U, the best approach will be to create your own Policy.sh which defined d_Gconvert exactly as you want it.

      The -D argument might work, but I am always scared of interpolation when requiring quotes. The tricky parts are the callback units.

      Setting this using env does not work.


      Enjoy, Have FUN! H.Merijn
        If you can't or won't spend time in fixing compline/d_gconvert.U ...

        If I can fix it, then I will undertake to do so.
        At the moment, however, I have no idea how d_gconvert.U fits into "the scheme of things".
        AFAICS, it's not part of the perl source.

        ... but I am always scared of interpolation when requiring quotes

        I bothers me a bit, too. Although the configure option that I used seems to have done the trick, it wasn't exactly noiseless:
        In file included from sv.c:32:0: sv.c: In function ‘Perl_sv_vcatpvfn_flags’: config.h:909:39: warning: ‘%.*g’ directive writing between 1 and 133 b +ytes into a region of size 127 [-Wformat-overflow=] #define Gconvert(x,n,t,b) sprintf((b),"%.*g",(n),(x)) ^ perl.h:6791:13: note: in definition of macro ‘WITH_LC_NUMERIC_SET_TO_N +EEDED_IN’ block; + \ ^~~~~ sv.c:48:5: note: in expansion of macro ‘PERL_UNUSED_RESULT’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~~~~~~~~~~~ sv.c:48:24: note: in expansion of macro ‘Gconvert’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~ sv.c:13118:21: note: in expansion of macro ‘SNPRINTF_G’ SNPRINTF_G(fv, ebuf, sizeof(ebuf), precis) ^ config.h:909:39: note: assuming directive output of 132 bytes #define Gconvert(x,n,t,b) sprintf((b),"%.*g",(n),(x)) ^ perl.h:6791:13: note: in definition of macro ‘WITH_LC_NUMERIC_SET_TO_N +EEDED_IN’ block; + \ ^~~~~ sv.c:48:5: note: in expansion of macro ‘PERL_UNUSED_RESULT’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~~~~~~~~~~~ sv.c:48:24: note: in expansion of macro ‘Gconvert’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~ sv.c:13118:21: note: in expansion of macro ‘SNPRINTF_G’ SNPRINTF_G(fv, ebuf, sizeof(ebuf), precis) ^ In file included from /usr/include/stdio.h:862:0, from perlio.h:41, from iperlsys.h:50, from perl.h:3934, from sv.c:32: /usr/include/x86_64-linux-gnu/bits/stdio2.h:33:10: note: ‘__builtin___ +sprintf_chk’ output between 2 and 134 bytes into a destination of siz +e 127 return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __bos (__s), __fmt, __va_arg_pack ()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from sv.c:32:0: config.h:909:39: warning: ‘%.*g’ directive writing between 1 and 133 b +ytes into a region of size 127 [-Wformat-overflow=] #define Gconvert(x,n,t,b) sprintf((b),"%.*g",(n),(x)) ^ perl.h:6791:13: note: in definition of macro ‘WITH_LC_NUMERIC_SET_TO_N +EEDED_IN’ block; + \ ^~~~~ sv.c:48:5: note: in expansion of macro ‘PERL_UNUSED_RESULT’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~~~~~~~~~~~ sv.c:48:24: note: in expansion of macro ‘Gconvert’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~ sv.c:13118:21: note: in expansion of macro ‘SNPRINTF_G’ SNPRINTF_G(fv, ebuf, sizeof(ebuf), precis) ^ config.h:909:39: note: assuming directive output of 132 bytes #define Gconvert(x,n,t,b) sprintf((b),"%.*g",(n),(x)) ^ perl.h:6791:13: note: in definition of macro ‘WITH_LC_NUMERIC_SET_TO_N +EEDED_IN’ block; + \ ^~~~~ sv.c:48:5: note: in expansion of macro ‘PERL_UNUSED_RESULT’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~~~~~~~~~~~ sv.c:48:24: note: in expansion of macro ‘Gconvert’ PERL_UNUSED_RESULT(Gconvert((NV)(nv), (int)ndig, 0, buffer)) ^~~~~~~~ sv.c:13118:21: note: in expansion of macro ‘SNPRINTF_G’ SNPRINTF_G(fv, ebuf, sizeof(ebuf), precis) ^ In file included from /usr/include/stdio.h:862:0, from perlio.h:41, from iperlsys.h:50, from perl.h:3934, from sv.c:32: /usr/include/x86_64-linux-gnu/bits/stdio2.h:33:10: note: ‘__builtin___ +sprintf_chk’ output between 2 and 134 bytes into a destination of siz +e 127 return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __bos (__s), __fmt, __va_arg_pack ()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Of course, I probably should at least run make test before I start getting too excited about how successful it has been ;-)

        Cheers,
        Rob