Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

floating points and sprintf

by kennethwlangley (Novice)
on Nov 14, 2002 at 15:15 UTC ( #212861=perlquestion: print w/replies, xml ) Need Help??

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

I have a co-worker who just got bitten by sprintf converting floating point numbers to ints. Here is the relevant code snippet:
for ($j=0.0; $j< 0.5; $j += 0.1) { $x = 147.0 + $j; print $x, "\t"; print $x*100, "\t"; $t = $x*100.0; $y = sprintf("%d", $t); print $t, "\t"; print $y, "\n"; }
It seems that print converts the floating point correctly but sprintf doesn't. He is using sprintf because perlfunc says "Usuall the sprintf(), printf(), ... will serve you better than will int()". This code has been checked with perl5.6.1. Note: I placed the extra assignment statement ($t=$x*100) to ensure that sprintf only saw a the resulting floating point.

Replies are listed 'Best First'.
Re: floating points and sprintf
by pg (Canon) on Nov 14, 2002 at 16:37 UTC

      Do you use the basic blue Perl Monks theme? If so, I'm not surprised you made the error: it's really difficult to tell whether you're logged in or not. This is one of the benefits of using an alternate theme.

      This makes it really easy to spot when someone sneaks, for instance, a link past you: if you click on it, the site reverts back to the default blue, so you know you can't be logged in.

      And of course these days, you're not even limited to the basic flavours, you can either tweak them with CSS inclusions hosted on the site (see your user settings) or either write your own stylesheet or use someone else's (or use someone else's as a basis for bootstrapping your own). This has to be hosted on another machine, but that shouldn't be much of a problem in this day and age.

      print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
      That explains it completely. I suspected the problem lay inside how I was expecting the float to be evaled by the perl version of sprintf (given that the int operator will floor() the value). I'll pass the information on to my coworker. Thanks again.
Re: floating points and sprintf
by petral (Curate) on Nov 14, 2002 at 18:41 UTC
    This may not make your co-worker happy, but:
    $ perl -we'printf"%d, %.0f\n",$a=147.2*100,$a' 14719, 14720

Re: floating points and sprintf
by John M. Dlugosz (Monsignor) on Nov 14, 2002 at 15:34 UTC
    Basically, print $t produces 14720 when sprintf produces 14719.

    My guess: printf will round off to get what you expected, while sprintf %d does an int() which simply truncates. If the value were actually 14719.999999999999997, then that would explain the difference.

    Note that floating point binary can't hold 1/10 exactly any more than decimal can hold 1/3 exactly.

    Try using %.15f on such a value and see closer too what's really stored.

      Check my just posted note. The real value is 147.2. That is mulitplied by 100 (value should be close to 14720.0) and then converted. I understand rounding errors converting from floats to ints but this problem strikes me as a bug.
        No, the real value is 147.199999999999990, since .2 cannot be represented exactly in a floating-point variable. Multiplying by 100 will not make it magically go back to what you wanted it to be, since the roundoff already occured. 14719.999999... truncated to an int is 14719, exactly what it said it does. This is not a bug, this is a typical floating-point gotcha.

        You probably want to correctly round rather than truncating. E.g. $intval= int(0.5 + $floatval);.


Re: floating points and sprintf
by Anonymous Monk on Nov 14, 2002 at 16:34 UTC
    As Perl is based on c, let's look at how c handles this. In c, you can do:
    float x = 147.2; printf("%20.20f", x * 100);
    it gives you 14719.99969482421875000000, which is fine, unless you are working on some finance application (COBOL will not have this problem, as its decimal is fixed; In Java you will have same thing as c, as Java is also c based, just like Perl, but Java has a class called BigDecimal, which holds fixed decimal.)
    However, when you try
    float x = 147.2; printf("%d", x * 100);
    It produces -167772160, which is garbage, but it is not a c problem, it is the programmer's problem, as c is trying to interprete that piece of memory as integer, as the programmer required.
    To resolve this problem, when they develop Perl, they did this:
    float x = 147.2; printf("%d", (int)(x * 100));
    which prints out 14719.
    So a new problem is introduced. However this would be fine to most people, most of the time. If you really care the accuracy and precision, because of the nature of your application, then try Math::BigFloat.
      I digged a little bit. Try this, go module Math::BigFloat, there is a function called norm, add one line at the end of this sub "print $_;". You will see how it stores the float as a string, and also its precision.
Re: floating points and sprintf
by Zaxo (Archbishop) on Nov 14, 2002 at 15:31 UTC

    Wrong format, he wants '%e' or '%f'.

    Oops, replied to the title. Decimal representation of binary fractions problen, see 'perldoc -q round' from perlfaq4..

    After Compline,

      Not if he wants to convert a float to an int (check my quote from the perlfunc document). I forgot to include a table of results. Here it is:
      $ ./
      147    14700    14700    14700
      147.1  14710    14710    14710
      147.2  14720    14720    14719
      147.3  14730    14730    14730
      147.4  14740    14740    14740

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://212861]
Approved by broquaint
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (2)
As of 2022-08-19 07:41 GMT
Find Nodes?
    Voting Booth?

    No recent polls found