Math::BigFloat oddities

Discussion in 'Perl Misc' started by Peter J. Acklam, Jul 6, 2004.

  1. I am totally out of clues as to why the following happens.
    Please, let me know...

    First, subtracting an element from itself should give 0...

    $ perl -MMath::BigFloat -wle \
    '$x = Math::BigFloat->new(3.14) ; $x = $x - $x ; print $x'
    0

    $ perl -MMath::BigFloat -wle \
    '$x = Math::BigFloat->new(3.14) ; $x -= $x ; print $x'
    6.28
    ^^^^

    What was that?

    Secondly, dividing an element by itself should give 1...

    $ perl -MMath::BigFloat -wle \
    '$x = Math::BigFloat->new(3.14) ; $x = $x / $x ; print $x'
    1 # OK; x / x = 1

    $ perl -MMath::BigFloat -wle \
    '$x = Math::BigFloat->new(3.14) ; $x /= $x ; print $x'
    0.00000000000000000000000000000000000000000001
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    What was that?

    Is there a bug in the operator overloading of Math::BigFloat
    objects?

    Peter

    --
    #!/local/bin/perl5 -wp -*- mode: cperl; coding: iso-8859-1; -*-
    # matlab comment stripper (strips comments from Matlab m-files)
    s/^((?:(?:[])}\w.]'+|[^'%])+|'[^'\n]*(?:''[^'\n]*)*')*).*/$1/x;
     
    Peter J. Acklam, Jul 6, 2004
    #1
    1. Advertising

  2. Peter J. Acklam

    Anno Siegel Guest

    Peter J. Acklam <> wrote in comp.lang.perl.misc:
    > I am totally out of clues as to why the following happens.
    > Please, let me know...
    >
    > First, subtracting an element from itself should give 0...
    >
    > $ perl -MMath::BigFloat -wle \
    > '$x = Math::BigFloat->new(3.14) ; $x = $x - $x ; print $x'
    > 0
    >
    > $ perl -MMath::BigFloat -wle \
    > '$x = Math::BigFloat->new(3.14) ; $x -= $x ; print $x'
    > 6.28
    > ^^^^
    >
    > What was that?
    >
    > Secondly, dividing an element by itself should give 1...
    >
    > $ perl -MMath::BigFloat -wle \
    > '$x = Math::BigFloat->new(3.14) ; $x = $x / $x ; print $x'
    > 1 # OK; x / x = 1
    >
    > $ perl -MMath::BigFloat -wle \
    > '$x = Math::BigFloat->new(3.14) ; $x /= $x ; print $x'
    > 0.00000000000000000000000000000000000000000001
    > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    >
    > What was that?
    >
    > Is there a bug in the operator overloading of Math::BigFloat
    > objects?


    The "-=" bug is already in Math::BigInt from which Math::BigFloat inherits
    most of its overloading. I'm not sure what exactly is going on, but the
    code jumps through quite a few hoops when it comes to protecting the
    operands of overloading from unwanted changes. I suppose something is
    going wrong there. I guess a bug report is in order.

    Anno
     
    Anno Siegel, Jul 6, 2004
    #2
    1. Advertising

  3. Peter J. Acklam

    Joe Smith Guest

    Purl Gurl wrote:

    > Purl Gurl (Godzilla) wrote:
    >
    >>Anno Siegel wrote:

    >
    >>>> $ perl -MMath::BigFloat -wle \
    >>>> '$x = Math::BigFloat->new(3.14) ; $x = $x - $x ; print $x'

    >
    > syntax error at -e line 1, at EOF
    > Execution of -e aborted due to compilation errors.


    You will note in the example given, the prompt is $ not C:\>.
    Those examples work just fine with bash or tcsh on Linux.
    Perhaps your shell is broken?
    -Joe
     
    Joe Smith, Jul 6, 2004
    #3
  4. Joe Smith <> wrote:

    > Purl Gurl wrote:
    >
    > > Purl Gurl (Godzilla) wrote:
    > >
    > >>Anno Siegel wrote:

    > >
    > >>>> $ perl -MMath::BigFloat -wle \
    > >>>> '$x = Math::BigFloat->new(3.14) ; $x = $x - $x ; print $x'

    > > syntax error at -e line 1, at EOF
    > > Execution of -e aborted due to compilation errors.

    >
    > You will note in the example given, the prompt is $ not C:\>.
    > Those examples work just fine with bash or tcsh on Linux.
    > Perhaps your shell is broken?


    Neither command.com nor cmd.exe understands that backslash means
    a broken line, nor do they undestand quoted strings using single
    quotation marks.

    Peter

    --
    #!/local/bin/perl5 -wp -*- mode: cperl; coding: iso-8859-1; -*-
    # matlab comment stripper (strips comments from Matlab m-files)
    s/^((?:(?:[])}\w.]'+|[^'%])+|'[^'\n]*(?:''[^'\n]*)*')*).*/$1/x;
     
    Peter J. Acklam, Jul 6, 2004
    #4
  5. Peter J. Acklam

    J. Romano Guest

    > Peter J. Acklam <> wrote in comp.lang.perl.misc:
    > >
    > > First, subtracting an element from itself should give 0...
    > >
    > > $ perl -MMath::BigFloat -wle \
    > > '$x = Math::BigFloat->new(3.14) ; $x = $x - $x ; print $x'
    > > 0
    > >
    > > $ perl -MMath::BigFloat -wle \
    > > '$x = Math::BigFloat->new(3.14) ; $x -= $x ; print $x'
    > > 6.28
    > > ^^^^
    > >
    > > What was that?


    -berlin.de (Anno Siegel) replied in message
    news:<ccejqo$flj$-Berlin.DE>...
    >
    > The "-=" bug is already in Math::BigInt from which Math::BigFloat inherits
    > most of its overloading. I'm not sure what exactly is going on, but the
    > code jumps through quite a few hoops when it comes to protecting the
    > operands of overloading from unwanted changes. I suppose something is
    > going wrong there. I guess a bug report is in order.



    Okay, I looked inside the code for Math::BigInt (which has the same
    problem as Math::BigFloat) and here is what is happening:

    Using the '-=' operator on a Math::BigInt object calls the bsub()
    method, which subtracts two Math::BigInt objects by doing the
    following:

    1. It flips the sign of the second object (to negate it)
    2. It adds the two objects together (with the badd() method)
    3. It flips the sign of the second object again (to restore it)

    Apparently the bsub() method assumes that the first and second object
    are NOT the same. But since they are, when steps 1 and 3 get carried
    out, both the first object AND the second object get their sign
    flipped. To clarify, consider the following code:

    use Math::BigInt; # line 1
    my $x = new Math::BigInt(3); # line 2
    $x -= $x; # line 3 (which SHOULD equal "$x = $x - $x")
    print $x; # line 4 (this prints a 6 (instead of zero))

    Pay attention to line 3. Logically, you think it should be equivalent
    to

    $x = 3 - 3;

    but instead the following happens:

    Step 1 negates $x (so that it becomes -3 on BOTH sides of '-=').
    Step 2 adds -3 and -3 (which makes -6) and puts that answer in $x.
    Step 3 negates $x (making it become 6).

    Clearly, the Math::BigInt and Math::BigInt modules are not expecting
    their own objects to use the '-=' on themselves. If that should ever
    happen, the object would double (instead of zero-ing out). To fix
    this, either the '-=' operator should never be called with an object
    and itself, or the following lines should be added to the
    Math::BigInt::bsub() method:

    if (overload::StrVal($x) eq overload::StrVal($y))
    {
    # The first and second objects are the same, so return zero:
    return Math::BigInt->new(0);
    }

    Unfortunately, this fix may not work for NaN (Not a Number) values,
    since NaN minus itself should return NaN, not zero (at least, I would
    think that it should return NaN).

    It looks like this bug only occurs when an object uses the '-='
    operator (or the '/=' operator) on itself. This bug wouldn't happen
    with the line:

    $x -= $x - 3;

    because ($x-3) gets evaluated first, returning a separate Math::BigInt
    value, which won't interfere with the '-=' bug.

    And if you think about it, there's really no reason to use lines
    like:

    $x -= $x; # logically should make $x zero
    $x /= $x; # logically sets $x to 1 (assuming $x is non-zero)

    because you already know what the answer should be, unless there is a
    possibility that $x is NaN. (If you want to check to see if $x is
    NaN, you should probably check BEFORE doing any math operations with
    the $x->is_nan() method.)

    To sum up, Peter, it looks like you found the only instance where
    the '-=' fails. And since lines like "$x -= $x" were never expected
    to be used, that special situation was never handled (causing a
    classic bug).

    Also note that some versions of Math::BigInt are implemented
    differently than others (that is, the code is quite a bit different).
    With one version of Perl, I can reproduce the exact same bug you have,
    but with another version of Perl, "$x -= $x" correctly sets $x to
    zero.

    I hope this helps.

    -- Jean-Luc Romano
     
    J. Romano, Jul 7, 2004
    #5
  6. (J. Romano) wrote:

    > Okay, I looked inside the code for Math::BigInt (...)
    >
    > To sum up, Peter, it looks like you found the only instance where
    > the '-=' fails. And since lines like "$x -= $x" were never expected
    > to be used, that special situation was never handled (causing a
    > classic bug).


    I am overwhelmed by you having put so much effort into this.
    Thank you! You were correct assuming that NaN - NaN = NaN, not
    zero, but there are two more cases which do not return zero:
    Inf - Inf = NaN and -Inf - -Inf = NaN.

    > Also note that some versions of Math::BigInt are implemented
    > differently than others (that is, the code is quite a bit
    > different). With one version of Perl, I can reproduce the exact
    > same bug you have, but with another version of Perl, "$x -= $x"
    > correctly sets $x to zero.


    I didn't have the latest version from CPAN

    T/TE/TELS/math/Math-BigInt-1.70.tar.gz

    I installed it and the bug is still there.

    > I hope this helps.


    It helped a lot.

    BTW, I sent a bug report yesterday.

    Peter

    --
    #!/local/bin/perl5 -wp -*- mode: cperl; coding: iso-8859-1; -*-
    # matlab comment stripper (strips comments from Matlab m-files)
    s/^((?:(?:[])}\w.]'+|[^'%])+|'[^'\n]*(?:''[^'\n]*)*')*).*/$1/x;
     
    Peter J. Acklam, Jul 7, 2004
    #6
  7. Peter J. Acklam

    J. Romano Guest

    (J. Romano) wrote in message news:<>...
    >
    > Using the '-=' operator on a Math::BigInt object calls the bsub()
    > method, which subtracts two Math::BigInt objects by doing the
    > following:
    >
    > 1. It flips the sign of the second object (to negate it)
    > 2. It adds the two objects together (with the badd() method)
    > 3. It flips the sign of the second object again (to restore it)


    Let me clarify a bit here. Suppose we had two Math::BigInt
    variables:

    my $a = Math::BigInt->new(5); # $a equals 5
    my $b = Math::BigInt->new(1); # $b equals 1

    and we used them together with the '-=' operator, like this:

    $a -= $b;

    The '-=' operator basically does the following three steps:

    1. It temporarily negates $b:
    $b = -$b; # $b is now -1
    2. It adds $a and $b and puts the result in $a:
    $a = $a + $b; # same as: $a = 5 + -1 (which equals 4)
    3. It negates $b again to restore its original sign:
    $b = -$b; # $b is now back to 1

    If $x was declared with a line like:

    my $x = Math::BigInt->new(3); # $x equals 3

    You can see why the operator '-=' messes up with a line like:

    $x -= $x;

    by applying the same three steps to $x:

    1. It temporarily negates $x:
    $x = -$x; # $x is now -3
    2. It adds $x and $x and puts the result in $x:
    $x = $x + $x; # same as: $x = -3 + -3 (which equals -6)
    3. It negates $x again to restore its original sign:
    $x = -$x; # $x is now 6 (not zero)!

    If you understood that, this should make more sense now:

    > Apparently the bsub() method assumes that the first and second object
    > are NOT the same. But since they are, when steps 1 and 3 get carried
    > out, both the first object AND the second object get their sign
    > flipped.


    A better fix than the one I suggested last time would be to give
    the second object a copy of the first if it's found that they are both
    references to the same object:

    if (overload::StrVal($x) eq overload::StrVal($y))
    {
    # The first and second objects are the same,
    # so give the second a copy of the first:
    $y = Math::BigInt->new($x);
    }

    This fix should go in the Math::BigInt::bsub() method right before the
    sign of $y is flipped. Unlike the previous fix I suggested, this fix
    should be able to handle NaN values. It might use a little more
    overhead (by creating a new copy of a Math::BigInt), but that's fairly
    negligible due to the fact that a line like "$x -= $x" rarely ever
    gets called.

    So that's the reason for the bug with the '-=' operator in
    Math::BigInt. I'm not sure if Math::BigFloat inherits the same bug,
    or if it's a different one altogether. Either way, I hope this clears
    up some confusion (and some bugs!).

    -- Jean-Luc Romano
     
    J. Romano, Jul 7, 2004
    #7
  8. Peter J. Acklam

    J. Romano Guest

    (Peter J. Acklam) wrote in message news:<>...
    > (J. Romano) wrote:
    >
    > > To sum up, Peter, it looks like you found the only instance where
    > > the '-=' fails. And since lines like "$x -= $x" were never expected
    > > to be used, that special situation was never handled (causing a
    > > classic bug).

    >
    > I am overwhelmed by you having put so much effort into this.
    > Thank you!


    You're very welcome! Comments in the code make finding bugs easier
    (that's why I advise every coder to add in-code comments).
    Fortunately, the Math::BigInt module is fairly commented (but, as
    always, I wish there were more comments).

    > I didn't have the latest version from CPAN
    >
    > T/TE/TELS/math/Math-BigInt-1.70.tar.gz
    >
    > I installed it and the bug is still there.


    Oh, sorry... I forgot to mention that the version I used that
    didn't have the bug was a very old version. Let me check real
    quick... the old version (without the bug) is $VERSION='0.01' (that's
    quite an old version) whereas the more recent version (the one with
    the bug) is $VERSION='1.60'.

    The old version's Math::BigInt objects were simply references to
    scalar strings, but the newer versions of Math::BigInt have objects
    that are references to hashes, with "value" and "sign" as attributes.

    I've been tinkering around a bit more with the Math::BigInt and
    Math::BigFloat modules, and I discovered more things related to the
    bug:

    Here's another quick fix: Use a text editor to comment out the:

    '-=' => sub { $_[0]->bsub($_[1]); },
    '/=' => sub { scalar $_[0]->bdiv($_[1]); },

    lines in the "use overload" pragma in the Math/BigInt.pm module. This
    will prevent the '-=' and '/=' operators from being overloaded.
    Instead they will be autogenerated from the '-' and the '/' operators.
    Perl is smart enough to do this correctly, avoiding the bug found in
    Math::BigInt::bsub().

    In fact, the old version of Math::BigInt didn't overload any
    assignment mutators (like '+=', '-=', '*=', '/=', etc.) because Perl
    automagically autogenerated them.

    (I originally found this confusing, even after reading "perldoc
    overload". Therefore, two weeks ago I decided to study this a bit. I
    posted a write-up of my findings on comp.lang.perl.misc on June 26,
    2004 with the subject "Regarding copy constructors and mutators". I
    expected to get some replies correcting me, suggesting advice, or
    maybe even an occasional flame, but nobody replied. Maybe nobody
    replied because there weren't any errors, or maybe because nobody
    bothered to read it... Either way, studying this helped me find the
    source of your bug.)

    Anyway, removing (or commenting out) the '-=' and '/=' lines in the
    "use overload" pragma of Math::BigInt will fix the problem in both
    Math::BigInt and in Math::BigFloat (due to the fact that
    Math::BigFloat inherits from Math::BigInt, as Anno said). This is
    just a temporary fix, of course, since a comment near the top of the
    BigInt.pm module explains that the assignment mutators are overloaded
    as shortcuts for speed. In other words, they don't have to be
    overloaded, but they are for efficiency. Unfortunately, this allowed
    for the bug you found, Peter. The optimal fix would be to fix the bug
    inside Math::BigInt::bsub() (and the one probably in
    Math::BigInt::bdiv()).

    I don't know if this reply explained anything more clearly or just
    made things more confusing, but I'm posting it nevertheless.

    Happy Perling,

    -- Jean-Luc Romano
     
    J. Romano, Jul 8, 2004
    #8
    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. Philip Townsend

    XPathNavigator oddities

    Philip Townsend, Dec 5, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    328
    Philip Townsend
    Dec 5, 2003
  2. =?Utf-8?B?Q2hhcmxlc0E=?=

    Datagrid 'select' button oddities

    =?Utf-8?B?Q2hhcmxlc0E=?=, Mar 20, 2006, in forum: ASP .Net
    Replies:
    3
    Views:
    1,862
    =?Utf-8?B?Q2hhcmxlc0E=?=
    Mar 20, 2006
  3. steve
    Replies:
    3
    Views:
    347
    steve
    Apr 14, 2004
  4. Francis Avila

    Some csv oddities

    Francis Avila, Nov 27, 2003, in forum: Python
    Replies:
    0
    Views:
    532
    Francis Avila
    Nov 27, 2003
  5. VK
    Replies:
    15
    Views:
    1,277
    Dr J R Stockton
    May 2, 2010
Loading...

Share This Page