#!perl use strict; use warnings; use Test::More tests => 230; use Math::BigFloat; my @k = (16, 32, 64, 128); sub stringify { my $x = shift; return "$x" unless $x -> is_finite(); my $nstr = $x -> bnstr(); my $sstr = $x -> bsstr(); return length($nstr) < length($sstr) ? $nstr : $sstr; } for my $k (@k) { # Parameters specific to this format: my $b = 2; my $p = $k == 16 ? 11 : $k == 32 ? 24 : $k == 64 ? 53 : $k - sprintf("%.0f", 4 * log($k)/log(2)) + 13; $b = Math::BigFloat -> new($b); $k = Math::BigFloat -> new($k); $p = Math::BigFloat -> new($p); my $w = $k - $p; my $emax = 2 ** ($w - 1) - 1; my $emin = 1 - $emax; my $format = sprintf 'binary%u', $k; my $binv = Math::BigFloat -> new("0.5"); my $data = [ { dsc => "smallest positive subnormal number", bin => "0" . ("0" x $w) . ("0" x ($p - 2)) . "1", asc => "$b ** ($emin) * $b ** (" . (1 - $p) . ") " . "= $b ** (" . ($emin + 1 - $p) . ")", mbf => $binv ** ($p - 1 - $emin), }, { dsc => "largest subnormal number", bin => "0" . ("0" x $w) . ("1" x ($p - 1)), asc => "$b ** ($emin) * (1 - $b ** (" . (1 - $p) . "))", mbf => $binv ** (-$emin) * (1 - $binv ** ($p - 1)), }, { dsc => "smallest positive normal number", bin => "0" . ("0" x ($w - 1)) . "1" . ("0" x ($p - 1)), asc => "$b ** ($emin)", mbf => $binv ** (-$emin), }, { dsc => "largest normal number", bin => "0" . ("1" x ($w - 1)) . "0" . "1" x ($p - 1), asc => "$b ** $emax * ($b - $b ** (" . (1 - $p) . "))", mbf => $b ** $emax * ($b - $binv ** ($p - 1)), }, { dsc => "largest number less than one", bin => "0" . "0" . ("1" x ($w - 2)) . "0" . "1" x ($p - 1), asc => "1 - $b ** (-$p)", mbf => 1 - $binv ** $p, }, { dsc => "smallest number larger than one", bin => "0" . "0" . ("1" x ($w - 1)) . ("0" x ($p - 2)) . "1", asc => "1 + $b ** (" . (1 - $p) . ")", mbf => 1 + $binv ** ($p - 1), }, { dsc => "second smallest number larger than one", bin => "0" . "0" . ("1" x ($w - 1)) . ("0" x ($p - 3)) . "10", asc => "1 + $b ** (" . (2 - $p) . ")", mbf => 1 + $binv ** ($p - 2), }, { dsc => "one", bin => "0" . "0" . ("1" x ($w - 1)) . "0" x ($p - 1), asc => "1", mbf => Math::BigFloat -> new("1"), }, { dsc => "minus one", bin => "1" . "0" . ("1" x ($w - 1)) . "0" x ($p - 1), asc => "-1", mbf => Math::BigFloat -> new("-1"), }, { dsc => "two", bin => "0" . "1" . ("0" x ($w - 1)) . ("0" x ($p - 1)), asc => "2", mbf => Math::BigFloat -> new("2"), }, { dsc => "minus two", bin => "1" . "1" . ("0" x ($w - 1)) . ("0" x ($p - 1)), asc => "-2", mbf => Math::BigFloat -> new("-2"), }, { dsc => "positive zero", bin => "0" . ("0" x $w) . ("0" x ($p - 1)), asc => "+0", mbf => Math::BigFloat -> new("0"), }, { dsc => "negative zero", bin => "1" . ("0" x $w) . ("0" x ($p - 1)), asc => "-0", mbf => Math::BigFloat -> new("0"), }, { dsc => "positive infinity", bin => "0" . ("1" x $w) . ("0" x ($p - 1)), asc => "+inf", mbf => Math::BigFloat -> new("inf"), }, { dsc => "negative infinity", bin => "1" . ("1" x $w) . ("0" x ($p - 1)), asc => "-inf", mbf => Math::BigFloat -> new("-inf"), }, { dsc => "NaN (sNaN on most processors, such as x86 and ARM)", bin => "0" . ("1" x $w) . ("0" x ($p - 2)) . "1", asc => "sNaN", mbf => Math::BigFloat -> new("NaN"), }, { dsc => "NaN (qNaN on most processors, such as x86 and ARM)", bin => "0" . ("1" x $w) . "1" . ("0" x ($p - 3)) . "1", asc => "qNaN", mbf => Math::BigFloat -> new("NaN"), }, { dsc => "NaN (an alternative encoding)", bin => "0" . ("1" x $w) . ("1" x ($p - 1)), asc => "NaN", mbf => Math::BigFloat -> new("NaN"), }, { dsc => "NaN (encoding used by Perl on Cygwin)", bin => "1" . ("1" x $w) . ("1" . ("0" x ($p - 2))), asc => "NaN", mbf => Math::BigFloat -> new("NaN"), }, ]; for my $entry (@$data) { my $bin = $entry -> {bin}; my $bytes = pack "B*", $bin; my $hex = unpack "H*", $bytes; note("\n", $entry -> {dsc }, " (k = $k)\n\n"); my $expected = stringify($entry -> {mbf}); my ($got, $test); $got = Math::BigFloat -> from_ieee754($bin, $format); $got = stringify($got); $test = qq|Math::BigFloat->from_ieee754("$bin")|; is($got, $expected, $test); $got = Math::BigFloat -> from_ieee754($hex, $format); $got = stringify($got); $test = qq|Math::BigFloat->from_ieee754("$hex")|; is($got, $expected, $test); $got = Math::BigFloat -> from_ieee754($bytes, $format); $got = stringify($got); (my $str = $hex) =~ s/(..)/\\x$1/g; $test = qq|Math::BigFloat->from_ieee754("$str")|; is($got, $expected, $test); } } note("\nTest as class method vs. instance method.\n\n"); # As class method. my $x = Math::BigFloat -> from_ieee754("4000000000000000", "binary64"); is($x, 2, "class method"); # As instance method, the invocand should be modified. $x -> from_ieee754("4008000000000000", "binary64"); is($x, 3, "instance method modifies invocand");