adding floating point

Discussion in 'Perl Misc' started by Amer Neely, Jan 27, 2007.

  1. Amer Neely

    Amer Neely Guest

    I've read the FAQ 'Why am I getting long decimals (eg, 19.9499999999999)
    instead of the numbers I should be getting (eg, 19.95)?' and other
    references, but am still confused by the results I'm getting.

    #! /usr/bin/perl

    BEGIN
    {
    open (STDERR,">>$0-err.txt");
    print STDERR "\n",scalar localtime,"\n$0\n";
    }

    use strict;
    use warnings;

    unless ($#ARGV == 1)
    {
    print "Usage: calc subtotal shipping\n";
    exit;
    }

    my $subtotal=$ARGV[0];
    my $shipping=$ARGV[1];
    #my $fuelfee = .1;
    my $fuelfee = 10;

    printf "%8.2f\n",$subtotal;
    printf "%8.2f\n",$shipping;
    printf "%8.2f\n",($shipping / $fuelfee); # when $fuelfee = 10
    printf "%8.2f\n",($shipping / $fuelfee) + $shipping + $subtotal; # when
    $fuelfee = 10
    #printf "%8.2f\n",($shipping * $fuelfee); # when $fuelfee = .1;
    #printf "%8.2f\n",(shipping * $fuelfee) + $shipping + $subtotal; # when
    $fuelfee = .1

    __END__
    Subtotal: $ 185.63
    Shipping: $ 24.15
    Fuel Fee: $ 2.42
    Total: $ 212.19

    If you add the numbers above in your head or a calculator, you get 212.2.

    So I'm confused why perl is out by a penny.

    Is it related to the FAQ?

    $fuelfee is being rounded up by perl from 2.415 to 2.42. But it appears
    to be using 2.415 in the addition, to get 212.195 but then not rounding
    that to 212.2.
    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 27, 2007
    #1
    1. Advertising

  2. Amer Neely

    Amer Neely Guest

    Amer Neely wrote:
    > I've read the FAQ 'Why am I getting long decimals (eg, 19.9499999999999)
    > instead of the numbers I should be getting (eg, 19.95)?' and other
    > references, but am still confused by the results I'm getting.
    >
    > #! /usr/bin/perl
    >
    > BEGIN
    > {
    > open (STDERR,">>$0-err.txt");
    > print STDERR "\n",scalar localtime,"\n$0\n";
    > }
    >
    > use strict;
    > use warnings;
    >
    > unless ($#ARGV == 1)
    > {
    > print "Usage: calc subtotal shipping\n";
    > exit;
    > }
    >
    > my $subtotal=$ARGV[0];
    > my $shipping=$ARGV[1];
    > #my $fuelfee = .1;
    > my $fuelfee = 10;
    >
    > printf "%8.2f\n",$subtotal;
    > printf "%8.2f\n",$shipping;
    > printf "%8.2f\n",($shipping / $fuelfee); # when $fuelfee = 10
    > printf "%8.2f\n",($shipping / $fuelfee) + $shipping + $subtotal; # when
    > $fuelfee = 10
    > #printf "%8.2f\n",($shipping * $fuelfee); # when $fuelfee = .1;
    > #printf "%8.2f\n",(shipping * $fuelfee) + $shipping + $subtotal; # when


    There is a typo in my pasted code above. 'shipping' should obviously be
    '$shipping'.

    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 27, 2007
    #2
    1. Advertising

  3. Amer Neely wrote:
    > I've read the FAQ 'Why am I getting long decimals (eg,
    > 19.9499999999999) instead of the numbers I should be getting (eg,
    > 19.95)?' and other references, but am still confused by the results

    [...]
    > Total: $ 212.19
    >
    > If you add the numbers above in your head or a calculator, you get
    > 212.2.
    > So I'm confused why perl is out by a penny.
    >
    > Is it related to the FAQ?


    Yes. You must have missed "Introduction to Computer Numerics".
    This has nothing to do with Perl but everything with how decimal numbers are
    represented internally in a computer. You would get the same result in any
    other programming language unless it is a language that is specifically
    designed to handle numerical problems.

    jue
     
    Jürgen Exner, Jan 27, 2007
    #3
  4. Amer Neely

    Amer Neely Guest

    Bob Walton wrote:
    > Amer Neely wrote:
    >> Amer Neely wrote:
    >>> I've read the FAQ 'Why am I getting long decimals (eg,
    >>> 19.9499999999999) instead of the numbers I should be getting (eg,
    >>> 19.95)?' and other references, but am still confused by the results
    >>> I'm getting.
    >>>
    >>> #! /usr/bin/perl
    >>>
    >>> BEGIN
    >>> {
    >>> open (STDERR,">>$0-err.txt");
    >>> print STDERR "\n",scalar localtime,"\n$0\n";
    >>> }
    >>>
    >>> use strict;
    >>> use warnings;
    >>>
    >>> unless ($#ARGV == 1)
    >>> {
    >>> print "Usage: calc subtotal shipping\n";
    >>> exit;
    >>> }
    >>>
    >>> my $subtotal=$ARGV[0];
    >>> my $shipping=$ARGV[1];
    >>> #my $fuelfee = .1;
    >>> my $fuelfee = 10;
    >>>
    >>> printf "%8.2f\n",$subtotal;
    >>> printf "%8.2f\n",$shipping;
    >>> printf "%8.2f\n",($shipping / $fuelfee); # when $fuelfee = 10
    >>> printf "%8.2f\n",($shipping / $fuelfee) + $shipping + $subtotal; #
    >>> when $fuelfee = 10
    >>> #printf "%8.2f\n",($shipping * $fuelfee); # when $fuelfee = .1;
    >>> #printf "%8.2f\n",(shipping * $fuelfee) + $shipping + $subtotal; # when

    >>
    >> There is a typo in my pasted code above. 'shipping' should obviously
    >> be '$shipping'.
    >>

    > That's why you should always copy/paste your real working tested code
    > into newsgroup notes, as suggested by the posting guidelines, rather
    > than retyping it.


    You missed the part where I said '...pasted code'. I *did* paste it in,
    but the original was wrong as well.

    >
    > I'm not sure how much more clear a statement could be made than the FAQ
    > on floating point rounding. Note that this is not an issue with Perl or
    > any other computing language, but an issue with the computer hardware
    > which performs floating point arithmetic: IT IS APPROXIMATE. IT IS NOT
    > EXACT. ROUNDING MAY NOT HAPPEN THE WAY YOU EXPECT OR WISH. RESULTS MAY
    > DIFFER ON DIFFERENT HARDWARE PLATFORMS AND FLOATING POINT INSTRUCTION SETS.
    >
    > Numbers such as the decimal number 0.1 are not represented exactly.
    > Print it out with:
    >
    > D:\JUNK>perl -e "print '%50.30f',0.1";
    > 0.100000000000000010000000000000
    >
    > if you don't believe me.
    >
    > If you perform "exact" decimal arithmetic on your formula above with
    > your example numbers, you get 212.195 -- and which way to round it to
    > two places is up to the user (yeah, some folks may have preferences, but
    > then someone else will prefer the other way). With floating point
    > arithmetic rather than exact decimal arithmetic, you may get something
    > like 212.194999999999 (which will round to 212.19) or 212.195000000001
    > (which will round to 212.20) -- with which one depending on your
    > hardware platform -- my PC generates 212.194999999999999000000... if I
    > print it with %50.30f for a format string, and 212.19 with %.2f as a
    > format string. If you want exact arithmetic, then you have to use the
    > facilities of your computer which accomplish exact arithmetic:
    > integers. In Perl, that is:
    >
    > use integer;
    >
    > Note that it is tricky business to correctly perform exact arithmetic,
    > particularly on financial computations where someone is auditing every
    > penny. Best to leave such to the pros.
    >
    > HTH


    Thanks for the clarification. I'll try running a round routine on it.
    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 27, 2007
    #4
  5. Amer Neely <> wrote:
    > Amer Neely wrote:


    >> #printf "%8.2f\n",(shipping * $fuelfee) + $shipping + $subtotal; # when

    >
    > There is a typo in my pasted code above. 'shipping' should obviously be
    > '$shipping'.



    A typo in a comment will not have any effect on program execution. :)


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, Jan 27, 2007
    #5
  6. Amer Neely <> wrote:

    > I've read the FAQ 'Why am I getting long decimals (eg, 19.9499999999999)
    > instead of the numbers I should be getting (eg, 19.95)?' and other
    > references, but am still confused by the results I'm getting.



    If you can arrange to use integer data rather than floating point data,
    then you wouldn't really need to understand the number theory issues
    of accuracy and precision. See below.


    > unless ($#ARGV == 1)



    That means "if there are not 2 command line arguments".

    A more natural what of writing that (to me anyway) would be:

    if ( @ARGV != 2 )


    > Total: $ 212.19


    > So I'm confused why perl is out by a penny.



    Since your data appears to be in units of (floating point) dollars,
    you should consider instead using units of (integer) cents in
    all of your calculations, then convert to dollars only for
    final output.

    If you need it to be dead-accurate (as is often the case when the
    data is money :), then take care to perform only integer arithmetic
    (eg. watch for division operations).


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, Jan 27, 2007
    #6
  7. Amer Neely

    Amer Neely Guest

    Abigail wrote:
    > Amer Neely () wrote on MMMMDCCCXCVII September
    > MCMXCIII in <URL:news:wJyuh.3189$>:
    > ## I've read the FAQ 'Why am I getting long decimals (eg, 19.9499999999999)
    > ## instead of the numbers I should be getting (eg, 19.95)?' and other
    > ## references, but am still confused by the results I'm getting.
    > ##
    > ## #! /usr/bin/perl
    > ##
    > ## BEGIN
    > ## {
    > ## open (STDERR,">>$0-err.txt");
    > ## print STDERR "\n",scalar localtime,"\n$0\n";
    > ## }
    > ##
    > ## use strict;
    > ## use warnings;
    > ##
    > ## unless ($#ARGV == 1)
    > ## {
    > ## print "Usage: calc subtotal shipping\n";
    > ## exit;
    > ## }
    > ##
    > ## my $subtotal=$ARGV[0];
    > ## my $shipping=$ARGV[1];
    > ## #my $fuelfee = .1;
    > ## my $fuelfee = 10;
    > ##
    > ## printf "%8.2f\n",$subtotal;
    > ## printf "%8.2f\n",$shipping;
    > ## printf "%8.2f\n",($shipping / $fuelfee); # when $fuelfee = 10
    > ## printf "%8.2f\n",($shipping / $fuelfee) + $shipping + $subtotal; # when
    > ## $fuelfee = 10
    > ## #printf "%8.2f\n",($shipping * $fuelfee); # when $fuelfee = .1;
    > ## #printf "%8.2f\n",(shipping * $fuelfee) + $shipping + $subtotal; # when
    > ## $fuelfee = .1
    > ##
    > ## __END__
    > ## Subtotal: $ 185.63
    > ## Shipping: $ 24.15
    > ## Fuel Fee: $ 2.42
    > ## Total: $ 212.19
    > ##
    > ## If you add the numbers above in your head or a calculator, you get 212.2.
    >
    > Yes.
    >
    > ## So I'm confused why perl is out by a penny.
    > ##
    > ## Is it related to the FAQ?
    >
    > Not really.
    >
    > ## $fuelfee is being rounded up by perl from 2.415 to 2.42.
    >
    > No, it's not. $fuelfee = 10 and doesn't change. But Perl also doesn't
    > round $shipping / $fuelfee to 2.42. What it does is round what it
    > *displays*.
    >
    > ## But it appears
    > ## to be using 2.415 in the addition, to get 212.195 but then not rounding
    > ## that to 212.2.
    >
    > That's the difference between rounding intermediate results and rounding
    > the final results. If you want to get 212.20 as an answer, you must round
    > the intermediate values as well:
    >
    > printf "%8.2f" => sprintf ("%8.2f" => $shipping / $fuelfee)
    > + $shipping + $subtotal;
    >
    >
    > In general, the following is not true:
    >
    > f(a) op f(b) = f(a op b)
    >
    > and that's what you were doing, with 'op' addition, and 'f' printf's
    > rounding functionality.
    >
    >
    > Abigail


    OK, this is making more sense now. I was confusing the 'display' value
    with the underlying real one (no pun intended). I've managed to get it
    working by applying a rounding operation to each value along the way.
    But if I understand, printf (or sprintf) to each value would achieve the
    same? I'll try that next. Thanks :)

    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 27, 2007
    #7
  8. Amer Neely

    Amer Neely Guest

    Tad McClellan wrote:
    > Amer Neely <> wrote:
    >
    >> I've read the FAQ 'Why am I getting long decimals (eg, 19.9499999999999)
    >> instead of the numbers I should be getting (eg, 19.95)?' and other
    >> references, but am still confused by the results I'm getting.

    >
    >
    > If you can arrange to use integer data rather than floating point data,
    > then you wouldn't really need to understand the number theory issues
    > of accuracy and precision. See below.
    >


    Hmm. Yes, I can see that would work. I may give that a try.

    >
    >> unless ($#ARGV == 1)

    >
    >
    > That means "if there are not 2 command line arguments".
    >
    > A more natural what of writing that (to me anyway) would be:
    >
    > if ( @ARGV != 2 )
    >
    >
    >> Total: $ 212.19

    >
    >> So I'm confused why perl is out by a penny.

    >
    >
    > Since your data appears to be in units of (floating point) dollars,
    > you should consider instead using units of (integer) cents in
    > all of your calculations, then convert to dollars only for
    > final output.
    >
    > If you need it to be dead-accurate (as is often the case when the
    > data is money :), then take care to perform only integer arithmetic
    > (eg. watch for division operations).
    >
    >


    I've since applied a rounding function to each operation along the way,
    instead of the final result as pointed out by Abigail, and got it to
    work (at least for this example). I'll test a few more examples and see
    what happens. Thanks for clearing up the mud :)

    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 27, 2007
    #8
  9. Amer Neely wrote:
    >> FAQ 'Why am I getting long decimals (eg, 19.9499999999999) ## instead of
    >> the numbers I should be getting (eg, 19.95)?' and other

    >
    > OK, this is making more sense now. I was confusing the 'display' value
    > with the underlying real one (no pun intended). I've managed to get it
    > working by applying a rounding operation to each value along the way.


    That is not a good idea. You are replacing one place of introducing
    inaccuracy with numerous places of introducing inaccuracy.

    There are two options:
    - Live with 'real' numbers being inaccurate
    - use a different representation for your values, e.g. compute your prices
    in pennies instead of in dollars. And convert them to dollar for display
    only. Natural numbers can be represented accurately without approximation.

    jue
     
    Jürgen Exner, Jan 27, 2007
    #9
  10. Amer Neely

    Amer Neely Guest

    Tad McClellan wrote:
    > Amer Neely <> wrote:
    >> Amer Neely wrote:

    >
    >>> #printf "%8.2f\n",(shipping * $fuelfee) + $shipping + $subtotal; # when

    >> There is a typo in my pasted code above. 'shipping' should obviously be
    >> '$shipping'.

    >
    >
    > A typo in a comment will not have any effect on program execution. :)
    >
    >

    Yeah, I know, but I've seen so many posters get jumped on for exactly
    that kind of thing. :)

    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 27, 2007
    #10
  11. Amer Neely

    Amer Neely Guest

    Jürgen Exner wrote:
    > Amer Neely wrote:
    >>> FAQ 'Why am I getting long decimals (eg, 19.9499999999999) ## instead of
    >>> the numbers I should be getting (eg, 19.95)?' and other

    >> OK, this is making more sense now. I was confusing the 'display' value
    >> with the underlying real one (no pun intended). I've managed to get it
    >> working by applying a rounding operation to each value along the way.

    >
    > That is not a good idea. You are replacing one place of introducing
    > inaccuracy with numerous places of introducing inaccuracy.
    >
    > There are two options:
    > - Live with 'real' numbers being inaccurate
    > - use a different representation for your values, e.g. compute your prices
    > in pennies instead of in dollars. And convert them to dollar for display
    > only. Natural numbers can be represented accurately without approximation.
    >
    > jue
    >
    >


    I understand what you are saying, but I don't think this application
    requires the degree of accuracy you are considering. I only need to
    reflect what someone with a calculator, or using their head, would
    arrive at, applying standard rounding.

    Now, if this were a financial investment application, I wouldn't even
    consider doing it myself.

    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 27, 2007
    #11
  12. Amer Neely

    Amer Neely Guest

    Abigail wrote:
    > Amer Neely () wrote on MMMMDCCCXCVII September
    > MCMXCIII in <URL:news:5HKuh.18184$>:
    > ~~
    > ~~ OK, this is making more sense now. I was confusing the 'display' value
    > ~~ with the underlying real one (no pun intended). I've managed to get it
    > ~~ working by applying a rounding operation to each value along the way.
    > ~~ But if I understand, printf (or sprintf) to each value would achieve the
    > ~~ same? I'll try that next. Thanks :)
    >
    >
    > No, I think you're making a mistake. Using a variable as argument to
    > printf (or sprintf) doesn't change the value of that variable. printf
    > prints a *different* value (2.42 instead of 2.415 in this case).
    >
    >
    >
    > Abigail


    Yes, which is what I would expect from a rounding function.

    I've used some of the suggestions and am now getting a 'more' correct
    answer. 2.42 is what I would expect to see on an invoice, not 2.415,
    even though that is the mathematically correct value. But in most
    day-to-day financial transactions, we round up or down, and this is the
    'right' answer as far as I can see - the addition is now correct.

    #! /usr/bin/perl

    BEGIN
    {
    open (STDERR,">>$0-err.txt");
    print STDERR "\n",scalar localtime,"\n$0\n";
    }

    use strict;
    use warnings;
    unless ($#ARGV == 1)
    {
    print "Usage: calc subtotal shipping\n";
    exit;
    }
    my $subtotal=$ARGV[0];
    my $shipping=$ARGV[1];
    my $fuelfee=($shipping / 10);
    printf "%8.2f\n",$subtotal;
    printf "%8.2f\n",$shipping;
    printf "%8.2f\n",$fuelfee;
    my $newtotal=round( $subtotal + $shipping + ($shipping / 10) ,2);
    print "=" x 8,"\n";
    ## from Abigail
    printf qq{%8.2f} => sprintf (qq{%8.2f} => 24.25 / 10) + 24.15 + 185.63;

    sub round
    {
    my ($n, $p) = @_;
    $p ||= 0;
    int($n * 10**$p + .5 * ($n < 0 ? -1 : 1)) / 10**$p;
    }


    --
    Amer Neely
    w: www.softouch.on.ca/
    b: www.softouch.on.ca/blog/
    Perl | MySQL programming for all data entry forms.
    "We make web sites work!"
     
    Amer Neely, Jan 28, 2007
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. H aka N
    Replies:
    15
    Views:
    15,821
    Ben Jones
    Mar 2, 2006
  2. Motaz Saad
    Replies:
    7
    Views:
    6,552
  3. Replies:
    4
    Views:
    1,333
    Default User
    Feb 22, 2006
  4. Saraswati lakki
    Replies:
    0
    Views:
    1,408
    Saraswati lakki
    Jan 6, 2012
  5. teeshift
    Replies:
    2
    Views:
    289
    Chris Pearl
    Dec 1, 2006
Loading...

Share This Page