1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
# Performing shift operations on floating-point types is not
# permitted under IEEE standards.
# Here, we simply overload the '<<' and '>>' operators to
# increase/decrease the exponent by the given "shift" amount.
# Effectively, we are multiplying/dividing the value held in
# the Math::MPFR object by 2**$hift.
# This is precisely what happens when we left-shift/right-shift
# an integer value.
use strict;
use warnings;
use Math::MPFR qw(:mpfr);
use Math::BigFloat; # Aiming to emulate Math::BigFloat in Math::MPFR.
use Config;
use Test::More;
my $f = Math::MPFR->new() << 10;
cmp_ok(Rmpfr_nan_p($f), '!=', 0, "NaN << returns NaN");
$f = Math::MPFR->new() >> 10;
cmp_ok(Rmpfr_nan_p($f), '!=', 0, "NaN >> returns NaN");
$f <<= 10;
cmp_ok(Rmpfr_nan_p($f), '!=', 0, "NaN <<= returns NaN");
$f >>= 10;
cmp_ok(Rmpfr_nan_p($f), '!=', 0, "NaN <<= returns NaN");
Rmpfr_set_inf($f, 0);
my $n = $f << 10;
cmp_ok(Rmpfr_inf_p($n), '!=', 0, "+Inf << returns Inf");
cmp_ok($n, '>', 0, "+Inf << returns +Inf");
$n = $f >> 10;
cmp_ok(Rmpfr_inf_p($n), '!=', 0, "+Inf >> returns Inf");
cmp_ok($n, '>', 0, "+Inf >> returns +Inf");
$f <<= 10;
cmp_ok(Rmpfr_inf_p($f), '!=', 0, "+Inf <<= returns Inf");
cmp_ok($f, '>', 0, "+Inf <<= returns +Inf");
$f >>= 10;
cmp_ok(Rmpfr_inf_p($f), '!=', 0, "+Inf >>= returns Inf");
cmp_ok($f, '>', 0, "-Inf >>= returns +Inf");
my $fneg = Math::MPFR->new();
Rmpfr_set_inf($fneg, -1);
##########################
$n = $fneg << 10;
cmp_ok(Rmpfr_inf_p($n), '!=', 0, "-Inf << returns Inf");
cmp_ok($n, '<', 0, "-Inf << returns -Inf");
$n = $fneg >> 10;
cmp_ok(Rmpfr_inf_p($n), '!=', 0, "-Inf >> returns Inf");
cmp_ok($n, '<', 0, "-Inf >> returns -Inf");
$fneg <<= 10;
cmp_ok(Rmpfr_inf_p($f), '!=', 0, "-Inf <<= returns Inf");
cmp_ok($fneg, '<', 0, "-Inf <<= returns -Inf");
$fneg >>= 10;
cmp_ok(Rmpfr_inf_p($f), '!=', 0, "-Inf >>= returns Inf");
cmp_ok($fneg, '<', 0, "-Inf >>= returns -Inf");
Rmpfr_set_NV($f, 0.0, MPFR_RNDN);
$n = $f << 10;
cmp_ok(Rmpfr_zero_p($f), '!=', 0, "'<<' 0 results in 0");
$n = $f >> 10;
cmp_ok(Rmpfr_zero_p($f), '!=', 0, "'>>' 0 results in 0");
my $samples = 10;
my($mpfr_res, $mbf_res);
my @values;
for(1 .. $samples) {push @values, rand(10000)}
my @shifts;
for(1 .. $samples) {push @shifts, int(rand(40)) - 20}
$samples--;
for my $i(0 .. $samples) {
my $obj = Math::MPFR->new($values[$i]);
my $mbf = Math::BigFloat->new($values[$i]);
my $shift = $shifts[$i];
cmp_ok($obj << $shift, '==', $obj >> -$shift, "A: $obj: handled consistently by << and >>");
cmp_ok($obj << -$shift, '==', $obj >> $shift, "B: $obj: handled consistently by << and >>");
if($] >= 5.04) {
$mpfr_res = $obj << $shift;
$mbf_res = $mbf << $shift;
cmp_ok("$mpfr_res", '==', "$mbf_res", "<<: Math::BigFloat and Math::MPFR concur");
$mpfr_res = $obj >> $shift;
$mbf_res = $mbf >> $shift;
cmp_ok("$mpfr_res", '==', "$mbf_res", ">>: Math::BigFloat and Math::MPFR concur");
}
my($x, $y) = ($obj + 10, $obj + 10);
$x <<= $shift;
$y >>= -$shift;
cmp_ok($x, '==', $y, "A: $obj: handled consistently by <<= and >>=");
$x <<= -$shift;
$y >>= $shift;
cmp_ok($x, '==', $y, "B: $obj: handled consistently by <<= and >>=");
}
cmp_ok(Math::MPFR->new(-401.3) >> 1.8, '==', -201, "-401.3 >> 1.8 == -201");
cmp_ok(Math::MPFR->new(-401.3) >> 1.8, '==', -201, "-401.3 << -1.8 == -201");
eval { my $discard = 2 >> Math::MPFR->new(7);};
like($@, qr/argument that specifies the number of bits to be/, "switched overload throws expected error");
eval {my $discard = $f >> Math::BigFloat->new(7);};
like($@, qr/argument that specifies the number of bits to be/, "Math::BigFloat shift arg throws expected error with '>>'");
eval {$f <<= Math::BigInt->new(7);};
like($@, qr/argument that specifies the number of bits to be/, "Math::BigFloat shift arg throws expected error with '<<='");
if($Config{longsize} < $Config{ivsize}) {
eval { my $discard = $f >> ~0;};
like ( $@, qr/In Math::MPFR overloading of '>>' operator,/, "mp_bitcnt_t overflow is caught in '>>'");
eval { my $discard = $f << ~0;};
like ( $@, qr/In Math::MPFR overloading of '<<' operator,/, "mp_bitcnt_t overflow is caught in '<<'");
eval { $f >>= ~0;};
like ( $@, qr/In Math::MPFR overloading of '>>=' operator,/, "mp_bitcnt_t overflow is caught in '>>='");
eval { $f <<= ~0;};
like ( $@, qr/In Math::MPFR overloading of '<<=' operator,/, "mp_bitcnt_t overflow is caught in '<<='");
}
done_testing();
|