On Sat Oct 31 05:41:51 2020, pjacklam wrote:
Show quoted text> The documentation says about operator overloading:
>
> = (The copy has the same precision as the copied object.)
>
> but apparently, "=" does not return a copy. Consider the following code:
>
> use strict;
> use warnings;
>
> use Math::MPFR qw< :mpfr >;
>
> my $RND = MPFR_RNDN; # rounding mode
> my $PREC = 120; # precision
>
> Rmpfr_set_default_prec($PREC);
> print "\nDefault precision is ", Rmpfr_get_default_prec(), "\n";
>
> # Compute pi to 120 bits.
>
> my $pi0 = Rmpfr_init(); # initialize
> Rmpfr_const_pi($pi0, $RND); # compute pi
> print "pi0 = ", $pi0, "\n"; # print it
>
> # Round to 20 (binary) digits.
>
> my $pi1 = $pi0; # make a copy
> Rmpfr_prec_round($pi1, 20, $RND); # round to 20 bits
> print "pi1 = ", $pi1, "\n"; # print it
>
> # Print the original value.
>
> print "pi0 = ", $pi0, "\n"; # why is this rounded?
>
> The code illustrates that rounding a copy does also round the original
> value. The output is
>
> Default precision is 120
> pi0 = 3.1415926535897932384626433832795028847
> pi1 = 3.1415939
> pi0 = 3.1415939
>
> However, if "=" returned a true copy, the original value should not
> have been rounded.
It looks to me that this demonstrates a trap in creating a copy via the overloading of the "=" operator.
It's also a trap of which I was unaware - thanks for bringing it to my attention.
The overload documentation says:
An object, however, is a reference to blessed data, so if $a and $b are
objects then the assignment "$a = $b" copies only the reference, leaving
$a and $b referring to the same object data. One might therefore expect
the operation "--$a" to decrement $b as well as $a. However, this would
not be consistent with how we expect the mathematical operators to work.
Perl resolves this dilemma by transparently calling a copy constructor
before calling a method defined to implement a mutator ("--", "+=", and
so on.). In the above example, when Perl reaches the decrement
statement, it makes a copy of the object data in $a and assigns to $a a
reference to the copied data. Only then does it call "decr()", which
alters the copied data, leaving $b unchanged. Thus the object metaphor
is preserved as far as possible, while mathemagical operations still
work according to the arithmetic metaphor.
There's more in the "Copy Constructor" section of the docs, including:
The subroutine for '=' does not overload the Perl assignment
operator: it is used only to allow mutators to work as described
here. (See "Assignments" above.)
AIUI, when you assign "$x = $y;" both $x and $y will refer to the same object until an operation that could change the value of one of those scalars is detected.
For example, for your script to DWIM, instead of doing:
$pi1 = $pi0;
do
$pi1 = $pi0;
$pi1 += 0; # now $pi1 and $pi0 refer to separate objects
And, apparently it has to be an operation that the overload pragma recognizes as being one that can change the value.
It won't work if you call "Rmpfr_add_ui($pi1, $pi1, 0, MPFR_RNDN);" instead of "$pi1 += 0;".
Obviously, Rmpfr_prec_round() doesn't trigger the split.
Neither would many other functions, including Rmpfr_trunc(), Rmpfr_ceil() and Rmpfr_floor().
Math::MPFR is behaving here in essentially the same way as Math::GMP and Math::BigInt.
The following script demonstrates that with Math::GMP, Math::BigInt and Math::MPFR, $x and $y refer to the same object until the "$y += 0;" call is reached.
use strict;
use warnings;
use Math::GMP;
use Math::BigInt;
use Math::MPFR;
use Devel::Peek;
my $x = Math::BigInt->new(42);
# my $x = Math::GMP->new(42);
# my $x = Math::MPFR->new(42);
my $y = $x;
# $x and $y still refer to the same object
Dump($x);
Dump($y);
$y += 0; # $y now refers to a separate object
Dump($y);
And, I believe, Math::BigFloat presents the very same trap as Math::MPFR
use strict;
use warnings;
use Math::BigFloat;
my $x = Math::BigFloat->new(1 / 3);
print "$x\n"; # prints 0.333333333333333
my $y = $x;
print "$y\n"; # prints 0.333333333333333
# $y += 0; # This will make script DWIM
$y->bround(4);
print "$y\n"; # prints 0.3333 - as expected
print "$x\n"; # prints 0.3333 - doesn't DWIM
Now ... it could be that there's some action I could take in the Math::MPFR source that would remove this trap.
Or maybe there's some documentation that should be added to raise awareness of it.
I don't know ... and I'm more than happy to consider thoughts on those aspects.
I think that, in Math::MPFR at least, there are safer ways to copy - eg Rmpfr_set(), Rmpfr_setsign(), Rmpfr_copysign() or even "$y = Math::MPFR->new($x);".
Admittedly, they all lack the convenience of doing "$y = $x;".
Cheers,
Rob