(e-mail address removed)-berlin.de (Anno Siegel) replied in message
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