Data-Float-0.015/0000755000175000017500000000000014773242404011272 5ustar rrrrData-Float-0.015/META.json0000664000175000017500000000267314773242404012725 0ustar rrrr{ "abstract" : "details of the floating point data type", "author" : [ "Robert Rothenberg " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.72, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Data-Float", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Carp" : "0", "Exporter" : "0", "constant" : "0", "integer" : "0", "parent" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" } }, "test" : { "requires" : { "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/robrwo/Data-Float/issues" }, "repository" : { "type" : "git", "url" : "git://github.com/robrwo/Data-Float.git" } }, "version" : "0.015", "x_serialization_backend" : "JSON::PP version 4.06" } Data-Float-0.015/SIGNATURE0000644000175000017500000000532614773242404012564 0ustar rrrrThis file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.89. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: RIPEMD160 SHA256 180fc2cd310b3de6c3a6db0fc83ffb3153f527345bffd513edbb9b0f490a78a6 Changes SHA256 f5930add3dc5bcde575ead01d2a1979900e2fdd94379068cfb19d40e85234118 MANIFEST SHA256 1a008c14e0d11e0f42223f670f80de1bc5c8649d5bf94536b54a75a4ecafb8d2 MANIFEST.SKIP SHA256 da1bdca6b2f6b32289477f5d00a4dbc994d01725b1c0326d18948b0778250d57 META.json SHA256 f2e8da219788eea4925c4f9ea16195ca5219f63b9965e49d7a3b749ba534219e META.yml SHA256 e761975468ec0cd24cf4378873da17763de3bba1e5d09582c3a7bc465a1cc4ad Makefile.PL SHA256 f73ba8e3d00bc7960973792e4c1e1d6b2175bcc3dbfb6a04fcec94bdcee95f26 README SHA256 1615c1ea54e12647281b0648afbe845b437b5dcb7b453df1cd22199c11fcfab4 SECURITY.md SHA256 eb2a2acef14c51beeffcd2768ab4a1285a901814c8a5f2b7a255875925723a9c lib/Data/Float.pm SHA256 49fc08e7f68f5ec367e51b6ab36c10f6bd8e30557b52b51c576b80013ecec7c5 t/class.t SHA256 78d49bfa54d55407977a2192d585c5adb1f16fa04dfb301216847c4e6c220989 t/const.t SHA256 4b6512931cef329ca7a50c179acea0f80b817a2ad2dc5f1cfd3a17a7ae91e69d t/copysign.t SHA256 bf6c16ce8de9cc4e9a1a517c3e660143a0fe00e0af04f8c594673b591f049fe6 t/hex.t SHA256 84a6b96f536b8616bb3a7f58ee3e219c18daee675c1d16052e8f4e74a1ab1fd1 t/id_cmp.t SHA256 6f1ecc37d1eba70fc7d98f8ee46f99f2648ecd682b221636b75a6ae6bebea380 t/nextafter.t SHA256 8529715e3a4895014c25192e31b35ed684df5f427c177e50da509675843895bb t/parts.t SHA256 3679257bdfb4a07658e98a41325f82c1744f7dae6d1d0151f1b216af0c1df5c9 t/pod_cvg.t SHA256 e16860066c4ca9b2ee9e7d4604297def8a58b53bf0ca03eed863b5d9c5a2ac91 t/pod_syn.t SHA256 071111c3214ff842bc787e80e45124c5d5120fcb01a8d9edabaa2738b4c4b3d6 t/pow2.t -----BEGIN PGP SIGNATURE----- iQGzBAEBAwAdFiEEeIwq7Pfyfxtssr5nHmWrcYGDC6wFAmftRQQACgkQHmWrcYGD C6y1Cwv9HqhoeTSeoUxUEQ6+HSGZwscwkIhsrvePa8gSu+IMZuQZ/maHc29cvG66 v4ot4p+GJmRsczNhUoEyii6s1wgVD854RNn6QK53wkxzbApol//yhEy5IcubwkjY 4Hnl7flOnAHebz6Ls0Yte2vR94jsgfERnDD4MIzqLahDmSP/xS1ruk7vea4iMdFC gbmDAiohy7hhJfnaIgoz/3BSLtFpipZEZFEW83Lv++48skTRbBQsoQkYJkxN/RSW 2ffFcBJuVcNtKV1vlaBFeb04HFQpDs8Mj4EKP/ne/RlE0ylep/DM4/OkGGIbCBQJ ds0tLjSfs49gJW1n1YYFXQmFfI0rKMmMX/q+gW+Wkv48cPINJ8NtqLfrt/G2Jd3M cdwM818iaDtbMLvbCX10Q694a72bJkQPaMyllqa7h9b70T46aPBtub4XATE7Brce +uRm6I1ozf3YwaQ1eQOzRWRwn2DZVS7noSAc+PQzra4MWhfgfZqThR70QvD/R0M+ k535DU8j =Gj9V -----END PGP SIGNATURE----- Data-Float-0.015/MANIFEST0000644000175000017500000000037014773007146012425 0ustar rrrrChanges lib/Data/Float.pm Makefile.PL MANIFEST MANIFEST.SKIP META.json META.yml README SECURITY.md SIGNATURE Added here by Module::Build t/class.t t/const.t t/copysign.t t/hex.t t/id_cmp.t t/nextafter.t t/parts.t t/pod_cvg.t t/pod_syn.t t/pow2.t Data-Float-0.015/t/0000755000175000017500000000000014773242404011535 5ustar rrrrData-Float-0.015/t/copysign.t0000644000175000017500000000413613137162164013556 0ustar rrrruse warnings; use strict; use Test::More tests => 46; BEGIN { use_ok "Data::Float", qw( copysign have_signed_zero have_infinite have_nan significand_bits float_is_nan ); } ok copysign(+1.2, +5) == +1.2; ok copysign(-1.2, +5) == +1.2; ok copysign(+1.2, -5) == -1.2; ok copysign(-1.2, -5) == -1.2; sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } my($z, $r); $z = 0; $r = copysign($z, +5); is zpat($z), "+0+0+0"; is zpat($r), "+0+0+0"; ok $r == 0; $z = 0; $r = copysign($z, -5); is zpat($z), "+0+0+0"; is zpat($r), "+0+0+0"; ok $r == 0; SKIP: { skip "no signed zero", 12 unless have_signed_zero; $z = +0.0; $r = copysign($z, +5); is zpat($z), "+0-0+0"; is zpat($r), "+0-0+0"; ok $r == 0; $z = -0.0; $r = copysign($z, +5); is zpat($z), "-0+0-0"; is zpat($r), "+0-0+0"; ok $r == 0; $z = +0.0; $r = copysign($z, -5); is zpat($z), "+0-0+0"; is zpat($r), "-0+0-0"; ok $r == 0; $z = -0.0; $r = copysign($z, -5); is zpat($z), "-0+0-0"; is zpat($r), "-0+0-0"; ok $r == 0; } $z = 0; ok copysign(+1.2, $z) == +1.2; is zpat($z), "+0+0+0"; $z = 0; ok copysign(-1.2, $z) == +1.2; is zpat($z), "+0+0+0"; SKIP: { skip "no signed zero", 8 unless have_signed_zero; $z = +0.0; ok copysign(+1.2, $z) == +1.2; is zpat($z), "+0-0+0"; $z = +0.0; ok copysign(-1.2, $z) == +1.2; is zpat($z), "+0-0+0"; $z = -0.0; ok copysign(+1.2, $z) == -1.2; is zpat($z), "-0+0-0"; $z = -0.0; ok copysign(-1.2, $z) == -1.2; is zpat($z), "-0+0-0"; } SKIP: { skip "infinities not available", 8 unless have_infinite; no strict "refs"; my $pinf = &{"Data::Float::pos_infinity"}; my $ninf = &{"Data::Float::neg_infinity"}; ok copysign($pinf, +5) == $pinf; ok copysign($ninf, +5) == $pinf; ok copysign($pinf, -5) == $ninf; ok copysign($ninf, -5) == $ninf; ok copysign(+1.2, $pinf) == +1.2; ok copysign(-1.2, $pinf) == +1.2; ok copysign(+1.2, $ninf) == -1.2; ok copysign(-1.2, $ninf) == -1.2; } SKIP: { skip "NaN not available", 3 unless have_nan; no strict "refs"; my $nan = &{"Data::Float::nan"}; ok float_is_nan(copysign($nan, +5)); ok float_is_nan(copysign($nan, $nan)); ok abs(copysign(+1.2, $nan)) == 1.2; } 1; Data-Float-0.015/t/parts.t0000644000175000017500000000345413137162164013056 0ustar rrrruse warnings; use strict; use Test::More tests => 1 + 4*3 + 2*2 + 5*17; BEGIN { use_ok "Data::Float", qw( have_signed_zero have_subnormal have_infinite min_normal_exp significand_bits mult_pow2 float_sign signbit float_parts ); } sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } sub test_sign($$$) { my($val, $sign, $is_zero) = @_; my $tval = $val; is float_sign($tval), $sign; is zpat($tval), zpat($val) if $is_zero; $tval = $val; is signbit($val), $sign eq "-" ? 1 : 0; is zpat($tval), zpat($val) if $is_zero; } test_sign(0, "+", 1); SKIP: { skip "no signed zero", 8 unless have_signed_zero; test_sign(+0.0, "+", 1); test_sign(-0.0, "-", 1); } SKIP: { skip "infinities not available", 4 unless have_infinite; no strict "refs"; test_sign(&{"Data::Float::pos_infinity"}, "+", 0); test_sign(&{"Data::Float::neg_infinity"}, "-", 0); } sub test_parts($$$$) { my($val, $sign, $exp, $sgnf) = @_; is float_sign($val), $sign; is signbit($val), $sign eq "-" ? 1 : 0; my($tsign, $texp, $tsgnf) = float_parts($val); ok $tsign eq $sign; ok $texp == $exp; ok $tsgnf == $sgnf; } test_parts(+1, "+", 0, 1.0); test_parts(+2, "+", 1, 1.0); test_parts(+3, "+", 1, 1.5); test_parts(-1, "-", 0, 1.0); test_parts(-2, "-", 1, 1.0); test_parts(-3, "-", 1, 1.5); test_parts(+1.0, "+", 0, 1.0); test_parts(+2.0, "+", 1, 1.0); test_parts(+3.0, "+", 1, 1.5); test_parts(+0.375, "+", -2, 1.5); test_parts(-1.0, "-", 0, 1.0); test_parts(-2.0, "-", 1, 1.0); test_parts(-3.0, "-", 1, 1.5); test_parts(-0.375, "-", -2, 1.5); test_parts(+512.5, "+", 9, 1.0009765625); test_parts(-0.078125, "-", -4, 1.25); SKIP: { skip "subnormals not available", 5 unless have_subnormal; no strict "refs"; test_parts(+3.0*&{"Data::Float::min_finite"}, "+", min_normal_exp, mult_pow2(3.0, -significand_bits())); } 1; Data-Float-0.015/t/pod_syn.t0000644000175000017500000000023613137162164013373 0ustar rrrruse warnings; use strict; use Test::More; plan skip_all => "Test::Pod not available" unless eval "use Test::Pod 1.00; 1"; Test::Pod::all_pod_files_ok(); 1; Data-Float-0.015/t/pod_cvg.t0000644000175000017500000000027313137162164013342 0ustar rrrruse warnings; use strict; use Test::More; plan skip_all => "Test::Pod::Coverage not available" unless eval "use Test::Pod::Coverage; 1"; Test::Pod::Coverage::all_pod_coverage_ok(); 1; Data-Float-0.015/t/nextafter.t0000644000175000017500000000674513137162164013733 0ustar rrrruse warnings; use strict; use Test::More tests => 65; BEGIN { use_ok "Data::Float", qw( nextup nextdown nextafter have_signed_zero have_infinite have_nan significand_bits min_finite max_finite max_number float_is_nan pow2 ); } ok nextup( 1) == 1 + pow2(-significand_bits()); ok nextafter(1, +9) == 1 + pow2(-significand_bits()); ok nextdown( 1) == (1-pow2(-significand_bits())) + pow2(-significand_bits()-1); ok nextafter(1, -9) == (1-pow2(-significand_bits())) + pow2(-significand_bits()-1); ok nextup( 3.5) == 3.5 + pow2(1-significand_bits()); ok nextafter(3.5, +9) == 3.5 + pow2(1-significand_bits()); ok nextdown( 3.5) == 3.5 - pow2(1-significand_bits()); ok nextafter(3.5, -9) == 3.5 - pow2(1-significand_bits()); ok nextafter(1.2, 1.2) == 1.2; ok nextup(max_number) == max_number; ok nextdown(-max_number()) == -max_number(); sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } my($za, $zb, $r); if(have_signed_zero) { $za = +0.0; $zb = +0.0; $r = nextafter($za, $zb); is zpat($za), "+0-0+0"; is zpat($zb), "+0-0+0"; is zpat($r), "+0-0+0"; ok $r == 0; $za = -0.0; $zb = +0.0; $r = nextafter($za, $zb); is zpat($za), "-0+0-0"; is zpat($zb), "+0-0+0"; is zpat($r), "+0-0+0"; ok $r == 0; $za = +0.0; $zb = -0.0; $r = nextafter($za, $zb); is zpat($za), "+0-0+0"; is zpat($zb), "-0+0-0"; is zpat($r), "-0+0-0"; ok $r == 0; $za = -0.0; $zb = -0.0; $r = nextafter($za, $zb); is zpat($za), "-0+0-0"; is zpat($zb), "-0+0-0"; is zpat($r), "-0+0-0"; ok $r == 0; } else { $za = 0; $zb = 0; $r = nextafter($za, $zb); is zpat($za), "+0+0+0"; is zpat($zb), "+0+0+0"; is zpat($r), "+0+0+0"; ok $r == 0; SKIP: { skip "no signed zero", 12; } } $za = +0.0; ok nextup( $za) == +min_finite(); is zpat($za), zpat(+0.0); $za = +0.0; ok nextafter($za, +9) == +min_finite(); is zpat($za), zpat(+0.0); $za = +0.0; ok nextdown( $za) == -min_finite(); is zpat($za), zpat(+0.0); $za = +0.0; ok nextafter($za, -9) == -min_finite(); is zpat($za), zpat(+0.0); SKIP: { skip "negative zero not available", 8 unless have_signed_zero; $za = -0.0; ok nextup( $za) == +min_finite(); is zpat($za), zpat(-0.0); $za = -0.0; ok nextafter($za, +9) == +min_finite(); is zpat($za), zpat(-0.0); $za = -0.0; ok nextdown( $za) == -min_finite(); is zpat($za), zpat(-0.0); $za = -0.0; ok nextafter($za, -9) == -min_finite(); is zpat($za), zpat(-0.0); } $r = nextup( -min_finite()); is zpat($r), zpat(-0.0); ok $r == 0.0; $r = nextafter(-min_finite(), +9); is zpat($r), zpat(-0.0); ok $r == 0.0; $r = nextdown( +min_finite()); is zpat($r), zpat(+0.0); ok $r == 0.0; $r = nextafter(+min_finite(), -9); is zpat($r), zpat(+0.0); ok $r == 0.0; SKIP: { skip "infinities not available", 8 unless have_infinite; no strict "refs"; my $pinf = &{"Data::Float::pos_infinity"}; my $ninf = &{"Data::Float::neg_infinity"}; ok nextup( +max_finite()) == $pinf; ok nextafter(+max_finite(), $pinf) == $pinf; ok nextdown( -max_finite()) == $ninf; ok nextafter(-max_finite(), $ninf) == $ninf; ok nextup( $ninf) == -max_finite(); ok nextafter($ninf, $pinf) == -max_finite(); ok nextdown( $pinf) == +max_finite(); ok nextafter($pinf, $ninf) == +max_finite(); } SKIP: { skip "NaN not available", 5 unless have_nan; no strict "refs"; my $nan = &{"Data::Float::nan"}; ok float_is_nan(nextup($nan)); ok float_is_nan(nextdown($nan)); ok float_is_nan(nextafter($nan, +9)); ok float_is_nan(nextafter(+1.2, $nan)); ok float_is_nan(nextafter($nan, $nan)); } 1; Data-Float-0.015/t/pow2.t0000644000175000017500000000166713137162164012620 0ustar rrrruse warnings; use strict; use Test::More tests => 25; BEGIN { use_ok "Data::Float", qw( pow2 mult_pow2 min_finite_exp max_finite_exp ); } ok pow2(0) == 1.0; ok pow2(1) == 2.0; ok pow2(5) == 32.0; ok pow2(-1) == 0.5; ok pow2(-5) == 0.03125; ok pow2(max_finite_exp) != 0; eval { pow2(max_finite_exp+1); }; like $@, qr/\Aexponent [^ \n]+ out of range/; ok pow2(min_finite_exp) != 0; eval { pow2(min_finite_exp-1); }; like $@, qr/\Aexponent [^ \n]+ out of range/; sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } foreach(0, +0.0, -0.0) { my $z = $_; my $m = mult_pow2($z, 5); is zpat($z), zpat($_); is zpat($m), zpat($_); ok $m == 0; } ok mult_pow2(1.5, -2) == 0.375; ok mult_pow2(-1.5, -2) == -0.375; ok mult_pow2(1025.0, -10) == 1.0009765625; ok mult_pow2(1.0009765625, 10) == 1025.0; ok mult_pow2(0.5, max_finite_exp+1) == pow2(max_finite_exp); ok mult_pow2(2.0, min_finite_exp-1) == pow2(min_finite_exp); 1; Data-Float-0.015/t/class.t0000644000175000017500000000327313137162164013031 0ustar rrrruse warnings; use strict; use Test::More tests => 1 + 8*11 + 3*8; BEGIN { use_ok "Data::Float", qw( have_subnormal have_infinite have_nan float_class float_is_normal float_is_subnormal float_is_nzfinite float_is_zero float_is_finite float_is_infinite float_is_nan ); } no strict "refs"; my %values = ( NORMAL => [ -1000.0, -0.125, +0.125, +1000.0, ], SUBNORMAL => [ sub { have_subnormal ? 3.0*&{"Data::Float::min_finite"} : undef } ], ZERO => [ 0, +0.0, -0.0 ], INFINITE => [ sub { have_infinite ? &{"Data::Float::neg_infinity"} : undef }, sub { have_infinite ? &{"Data::Float::pos_infinity"} : undef }, ], NAN => [ sub { have_nan ? &{"Data::Float::nan"} : undef }, ], ); foreach(values %values) { foreach(@$_) { $_ = $_->() if ref($_) eq "CODE"; } } foreach my $class (keys %values) { foreach(@{$values{$class}}) { SKIP: { skip "special value not available", 8 unless defined $_; my $t; is float_class($t = $_), $class; is !!float_is_normal($t = $_), $class eq "NORMAL"; is !!float_is_subnormal($t = $_), $class eq "SUBNORMAL"; is !!float_is_nzfinite($t = $_), !!($class =~ /NORMAL\z/); is !!float_is_zero($t = $_), $class eq "ZERO"; is !!float_is_finite($t = $_), !!($class =~ /ZERO\z|NORMAL\z/); is !!float_is_infinite($t = $_), $class eq "INFINITE"; is !!float_is_nan($t = $_), $class eq "NAN"; } } } sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } foreach(\&float_class, \&float_is_normal, \&float_is_subnormal, \&float_is_nzfinite, \&float_is_zero, \&float_is_finite, \&float_is_infinite, \&float_is_nan ) { foreach my $z (0, +0.0, -0.0) { my $tz = $z; my $pat = zpat($tz); $_->($tz); is zpat($tz), $pat; } } 1; Data-Float-0.015/t/const.t0000644000175000017500000000427513137162164013055 0ustar rrrruse warnings; use strict; use Test::More tests => 30; BEGIN { use_ok "Data::Float", qw( have_signed_zero have_subnormal have_infinite have_nan significand_bits significand_step max_finite_exp max_finite_pow2 max_finite max_number max_integer min_normal_exp min_normal min_finite_exp min_finite ); } foreach (significand_bits, max_finite_exp, max_finite_pow2, max_finite, max_number, max_integer, min_normal_exp, min_finite_exp) { ok int($_) == $_; } my $a = 1; for(my $i = max_finite_exp; $i-- > 0; ) { $a += $a; } is $a, max_finite_pow2; ok max_finite > max_finite_pow2; ok max_finite - max_finite_pow2 < max_finite_pow2; $a = 1; for(my $i = min_normal_exp; $i++ < 0; ) { $a *= 0.5; } is $a, min_normal; $a = 1; for(my $i = min_finite_exp; $i++ < 0; ) { $a *= 0.5; } is $a, min_finite; $a = 1; for(my $i = significand_bits; $i-- > 0; ) { $a *= 0.5; } is $a, significand_step; $a = 1; for(my $i = significand_bits+1; $i--; ) { $a += $a; } is $a, max_integer; if(have_subnormal) { ok min_finite_exp < min_normal_exp; } else { ok min_finite_exp <= min_normal_exp; } ok max_integer - (max_integer-1) == 1; ok +(min_finite * 0.5) * 2.0 != min_finite; sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } if(have_signed_zero) { no strict "refs"; is zpat(0.0), "+0-0+0"; is zpat(-0.0), "-0+0-0"; my $pos_zero = &{"Data::Float::pos_zero"}; my $neg_zero = &{"Data::Float::neg_zero"}; is zpat($pos_zero), "+0-0+0"; is zpat($neg_zero), "-0+0-0"; { no warnings "void"; $pos_zero == $pos_zero; $neg_zero == $neg_zero; } $pos_zero = &{"Data::Float::pos_zero"}; $neg_zero = &{"Data::Float::neg_zero"}; is zpat($pos_zero), "+0-0+0"; is zpat($neg_zero), "-0+0-0"; } else { is zpat(0.0), "+0+0+0"; is zpat(-0.0), "+0+0+0"; SKIP: { skip "no signed zeroes", 4; } } if(have_infinite) { no strict "refs"; ok &{"Data::Float::pos_infinity"} > max_finite; ok &{"Data::Float::neg_infinity"} < -max_finite(); ok max_number == &{"Data::Float::pos_infinity"}; ok max_number > max_finite; } else { SKIP: { skip "no infinities", 3; } ok max_number == max_finite; } SKIP: { skip "no NaNs", 1 unless have_nan; no strict "refs"; my $nan = &{"Data::Float::nan"}; ok $nan != $nan; } 1; Data-Float-0.015/t/id_cmp.t0000644000175000017500000000230213137162164013147 0ustar rrrruse warnings; use strict; use Test::More tests => 1 + 2*9*9 + 8*9; BEGIN { use_ok "Data::Float", qw( have_infinite have_signed_zero have_nan float_id_cmp totalorder ); } no strict "refs"; my @values = ( sub { have_nan ? &{"Data::Float::nan"} : undef }, sub { have_infinite ? &{"Data::Float::neg_infinity"} : undef }, -1000.0, -0.125, sub { have_signed_zero ? &{"Data::Float::neg_zero"} : undef }, +0.0, +0.125, +1000.0, sub { have_infinite ? &{"Data::Float::pos_infinity"} : undef }, ); foreach(@values) { $_ = $_->() if ref($_) eq "CODE"; } sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } for(my $ia = @values; $ia--; ) { for(my $ib = @values; $ib--; ) { SKIP: { my($a, $b) = @values[$ia, $ib]; my $az = $ia == 4 || $ia == 5 ? 1 : 0; my $bz = $ib == 4 || $ib == 5 ? 1 : 0; skip "special value not available", 2*(1+$az+$bz) unless defined($a) && defined($b); my($ta, $tb) = ($a, $b); is float_id_cmp($ta, $tb), ($ia <=> $ib); is zpat($ta), zpat($a) if $az; is zpat($tb), zpat($b) if $bz; ($ta, $tb) = ($a, $b); is !!totalorder($ta, $tb), ($ia <= $ib); is zpat($ta), zpat($a) if $az; is zpat($tb), zpat($b) if $bz; } } } 1; Data-Float-0.015/t/hex.t0000644000175000017500000001223513137162164012506 0ustar rrrruse warnings; use strict; use Test::More tests => 102; BEGIN { use_ok "Data::Float", qw( float_hex hex_float have_signed_zero have_infinite have_nan float_is_nan ); } my %str_opt = ( exp_neg_sign => "(ENS)", exp_pos_sign => "(EPS)", hex_prefix_string => "(HEX)", infinite_string => "(INF)", nan_string => "(NAN)", neg_sign => "(VNS)", pos_sign => "(VPS)", zero_strategy => "STRING=(ZERO)", ); SKIP: { skip "no infinities", 22 unless have_infinite; no strict "refs"; my $pinf = &{"Data::Float::pos_infinity"}; my $ninf = &{"Data::Float::neg_infinity"}; is float_hex($pinf), "+inf"; is float_hex($ninf), "-inf"; is float_hex($pinf, \%str_opt), "(VPS)(INF)"; is float_hex($ninf, \%str_opt), "(VNS)(INF)"; ok hex_float("inf") == $pinf; ok hex_float("Inf") == $pinf; ok hex_float("iNf") == $pinf; ok hex_float("+inf") == $pinf; ok hex_float("+Inf") == $pinf; ok hex_float("+iNf") == $pinf; ok hex_float("-inf") == $ninf; ok hex_float("-Inf") == $ninf; ok hex_float("-iNf") == $ninf; ok hex_float("infinity") == $pinf; ok hex_float("Infinity") == $pinf; ok hex_float("iNfiniTy") == $pinf; ok hex_float("+infinity") == $pinf; ok hex_float("+Infinity") == $pinf; ok hex_float("+iNfiniTy") == $pinf; ok hex_float("-infinity") == $ninf; ok hex_float("-Infinity") == $ninf; ok hex_float("-iNfiniTy") == $ninf; } SKIP: { skip "no NaN", 20 unless have_nan; no strict "refs"; is float_hex(&{"Data::Float::nan"}), "nan"; is float_hex(&{"Data::Float::nan"}, \%str_opt), "(NAN)"; ok float_is_nan(hex_float("nan")); ok float_is_nan(hex_float("Nan")); ok float_is_nan(hex_float("nAn")); ok float_is_nan(hex_float("+nan")); ok float_is_nan(hex_float("+Nan")); ok float_is_nan(hex_float("+nAn")); ok float_is_nan(hex_float("-nan")); ok float_is_nan(hex_float("-Nan")); ok float_is_nan(hex_float("-nAn")); ok float_is_nan(hex_float("snan")); ok float_is_nan(hex_float("sNan")); ok float_is_nan(hex_float("SnAn")); ok float_is_nan(hex_float("+snan")); ok float_is_nan(hex_float("+sNan")); ok float_is_nan(hex_float("+SnAn")); ok float_is_nan(hex_float("-snan")); ok float_is_nan(hex_float("-sNan")); ok float_is_nan(hex_float("-SnAn")); } my %opt = ( frac_digits_bits_mod => "IGNORE" ); foreach([ +1, "+0x1p+0" ], [ +3.75, "+0x1.ep+1" ], [ -3.75, "-0x1.ep+1" ], [ +0.375, "+0x1.8p-2" ], [ +1.09375, "+0x1.18p+0" ], ) { my($val, $hex) = @$_; is float_hex($val, \%opt), $hex; ok hex_float($hex) == $val; } ok hex_float("1.ep1") == +3.75; ok hex_float("3.c") == +3.75; ok hex_float("1ep-3") == +3.75; ok hex_float("0.01ep9") == +3.75; foreach(1023013230.1, 1.23e30, 3.564e-30) { ok hex_float(float_hex($_)) == $_; } sub zpat($) { my($z) = @_; my $nz = -$z; sprintf("%+.f%+.f%+.f",$z,$nz,-$nz) } my $z; $z = 0; is float_hex($z), "+0.0"; is zpat($z), "+0+0+0"; SKIP: { skip "no signed zero", 4 unless have_signed_zero; $z = +0.0; is float_hex($z), "+0.0"; is zpat($z), "+0-0+0"; $z = -0.0; is float_hex($z), "-0.0"; is zpat($z), "-0+0-0"; } is float_hex(0, \%str_opt), "(VPS)(ZERO)"; like float_hex(0, { %str_opt, zero_strategy => "SUBNORMAL" }), qr/\A\(VPS\)\(HEX\)0\.0+p\(ENS\)[1-9][0-9]*\z/; like float_hex(0, { %str_opt, zero_strategy => "EXPONENT=-33" }), qr/\A\(VPS\)\(HEX\)0\.0+p\(ENS\)33\z/; $z = hex_float("0"); is zpat($z), zpat(+0.0); ok $z == 0.0; $z = hex_float("+0"); is zpat($z), zpat(+0.0); ok $z == 0.0; $z = hex_float("-0"); is zpat($z), zpat(-0.0); ok $z == 0.0; $z = hex_float("0.0"); is zpat($z), zpat(+0.0); ok $z == 0.0; $z = hex_float("+0.0"); is zpat($z), zpat(+0.0); ok $z == 0.0; $z = hex_float("-0.0"); is zpat($z), zpat(-0.0); ok $z == 0.0; like float_hex(2, { exp_digits => 5 }), qr/\A\+0x1\.0+p\+00001\z/; like float_hex(2, { exp_digits_range_mod => "ATLEAST" }), qr/\A\+0x1\.0+p\+0+1\z/; %opt = ( %str_opt, frac_digits_bits_mod => "IGNORE" ); is float_hex(+3.75, \%opt), "(VPS)(HEX)1.ep(EPS)1"; is float_hex(-3.75, \%opt), "(VNS)(HEX)1.ep(EPS)1"; is float_hex(+0.375, \%opt), "(VPS)(HEX)1.8p(ENS)2"; is float_hex(-0.375, \%opt), "(VNS)(HEX)1.8p(ENS)2"; is float_hex(+3.75, { frac_digits => 5, frac_digits_bits_mod => "IGNORE" }), "+0x1.e0000p+1"; is float_hex(+3.75, { frac_digits => 1, frac_digits_bits_mod => "IGNORE" }), "+0x1.ep+1"; is float_hex(+1.09375, { frac_digits => 5, frac_digits_bits_mod => "IGNORE" }), "+0x1.18000p+0"; is float_hex(+1.09375, { frac_digits => 2, frac_digits_bits_mod => "IGNORE" }), "+0x1.18p+0"; is float_hex(+1.09375, { frac_digits => 1, frac_digits_bits_mod => "IGNORE" }), "+0x1.18p+0"; %opt = ( frac_digits_bits_mod => "IGNORE", frac_digits_value_mod => "IGNORE" ); is float_hex(+1.09375, { %opt, frac_digits => 5 }), "+0x1.18000p+0"; is float_hex(+1.09375, { %opt, frac_digits => 2 }), "+0x1.18p+0"; is float_hex(+1.09375, { %opt, frac_digits => 1 }), "+0x1.2p+0"; is float_hex(+1.09375, { %opt, frac_digits => 0 }), "+0x1p+0"; is float_hex(+1.90625, { %opt, frac_digits => 5 }), "+0x1.e8000p+0"; is float_hex(+1.90625, { %opt, frac_digits => 2 }), "+0x1.e8p+0"; is float_hex(+1.90625, { %opt, frac_digits => 1 }), "+0x1.ep+0"; is float_hex(+1.90625, { %opt, frac_digits => 0 }), "+0x1p+1"; like float_hex(1, { exp_digits_range_mod => "ATLEAST" }), qr/\A\+0x1\.0+p\+00+\z/; like float_hex(1, { exp_digits => 5 }), qr/\A\+0x1\.0+p\+00000\z/; 1; Data-Float-0.015/lib/0000755000175000017500000000000014773242404012040 5ustar rrrrData-Float-0.015/lib/Data/0000755000175000017500000000000014773242404012711 5ustar rrrrData-Float-0.015/lib/Data/Float.pm0000644000175000017500000012344014773001700014310 0ustar rrrr=head1 NAME Data::Float - details of the floating point data type =head1 SYNOPSIS use Data::Float qw(have_signed_zero); if(have_signed_zero) { ... # and many other constants; see text use Data::Float qw( float_class float_is_normal float_is_subnormal float_is_nzfinite float_is_zero float_is_finite float_is_infinite float_is_nan); $class = float_class($value); if(float_is_normal($value)) { ... if(float_is_subnormal($value)) { ... if(float_is_nzfinite($value)) { ... if(float_is_zero($value)) { ... if(float_is_finite($value)) { ... if(float_is_infinite($value)) { ... if(float_is_nan($value)) { ... use Data::Float qw(float_sign signbit float_parts); $sign = float_sign($value); $sign_bit = signbit($value); ($sign, $exponent, $significand) = float_parts($value); use Data::Float qw(float_hex hex_float); print float_hex($value); $value = hex_float($string); use Data::Float qw(float_id_cmp totalorder); @sorted_floats = sort { float_id_cmp($a, $b) } @floats; if(totalorder($a, $b)) { ... use Data::Float qw( pow2 mult_pow2 copysign nextup nextdown nextafter); $x = pow2($exp); $x = mult_pow2($value, $exp); $x = copysign($magnitude, $sign_from); $x = nextup($x); $x = nextdown($x); $x = nextafter($x, $direction); =head1 DESCRIPTION This module is about the native floating point numerical data type. A floating point number is one of the types of datum that can appear in the numeric part of a Perl scalar. This module supplies constants describing the native floating point type, classification functions, and functions to manipulate floating point values at a low level. =head1 FLOATING POINT =head2 Classification Floating point values are divided into five subtypes: =over =item normalised The value is made up of a sign bit (making the value positive or negative), a significand, and exponent. The significand is a number in the range [1, 2), expressed as a binary fraction of a certain fixed length. (Significands requiring a longer binary fraction, or lacking a terminating binary representation, cannot be obtained.) The exponent is an integer in a certain fixed range. The magnitude of the value represented is the product of the significand and two to the power of the exponent. =item subnormal The value is made up of a sign bit, significand, and exponent, as for normalised values. However, the exponent is fixed at the minimum possible for a normalised value, and the significand is in the range (0, 1). The length of the significand is the same as for normalised values. This is essentially a fixed-point format, used to provide gradual underflow. Not all floating point formats support this subtype. Where it is not supported, underflow is sudden, and the difference between two minimum-exponent normalised values cannot be exactly represented. =item zero Depending on the floating point type, there may be either one or two zero values: zeroes may carry a sign bit. Where zeroes are signed, it is primarily in order to indicate the direction from which a value underflowed (was rounded) to zero. Positive and negative zero compare as numerically equal, and they give identical results in most arithmetic operations. They are on opposite sides of some branch cuts in complex arithmetic. =item infinite Some floating point formats include special infinite values. These are generated by overflow, and by some arithmetic cases that mathematically generate infinities. There are two infinite values: positive infinity and negative infinity. Perl does not always generate infinite values when normal floating point behaviour calls for it. For example, the division C<1.0/0.0> causes an exception rather than returning an infinity. =item not-a-number (NaN) This type of value exists in some floating point formats to indicate error conditions. Mathematically undefined operations may generate NaNs, and NaNs propagate through all arithmetic operations. A NaN has the distinctive property of comparing numerically unequal to all floating point values, including itself. Perl does not always generate NaNs when normal floating point behaviour calls for it. For example, the division C<0.0/0.0> causes an exception rather than returning a NaN. Perl has only (at most) one NaN value, even if the underlying system supports different NaNs. (IEEE 754 arithmetic has NaNs which carry a quiet/signal bit, a sign bit (yes, a sign on a not-number), and many bits of implementation-defined data.) =back =head2 Mixing floating point and integer values Perl does not draw a strong type distinction between native integer (see L) and native floating point values. Both types of value can be stored in the numeric part of a plain (string) scalar. No distinction is made between the integer representation and the floating point representation where they encode identical values. Thus, for floating point arithmetic, native integer values that can be represented exactly in floating point may be freely used as floating point values. Native integer arithmetic has exactly one zero value, which has no sign. If the floating point type does not have signed zeroes then the floating point and integer zeroes are exactly equivalent. If the floating point type does have signed zeroes then the integer zero can still be used in floating point arithmetic, and it behaves as an unsigned floating point zero. On such systems there are therefore three types of zero available. There is a bug in Perl which sometimes causes floating point zeroes to change into integer zeroes; see L for details. Where a native integer value is used that is too large to exactly represent in floating point, it will be rounded as necessary to a floating point value. This rounding will occur whenever an operation has to be performed in floating point because the result could not be exactly represented as an integer. This may be confusing to functions that expect a floating point argument. Similarly, some operations on floating point numbers will actually be performed in integer arithmetic, and may result in values that cannot be exactly represented in floating point. This happens whenever the arguments have integer values that fit into the native integer type and the mathematical result can be exactly represented as a native integer. This may be confusing in cases where floating point semantics are expected. See L for discussion of Perl's numeric semantics. =cut package Data::Float; { use 5.006; } use warnings; use strict; use Carp qw(croak); our $VERSION = "0.015"; use parent "Exporter"; our @EXPORT_OK = qw( float_class float_is_normal float_is_subnormal float_is_nzfinite float_is_zero float_is_finite float_is_infinite float_is_nan float_sign signbit float_parts float_hex hex_float float_id_cmp totalorder pow2 mult_pow2 copysign nextup nextdown nextafter ); # constant functions get added to @EXPORT_OK later =head1 CONSTANTS =head2 Features =over =item have_signed_zero Truth value indicating whether floating point zeroes carry a sign. If yes, then there are two floating point zero values: +0.0 and -0.0. (Perl scalars can nevertheless also hold an integer zero, which is unsigned.) If no, then there is only one zero value, which is unsigned. =item have_subnormal Truth value indicating whether there are subnormal floating point values. =item have_infinite Truth value indicating whether there are infinite floating point values. =item have_nan Truth value indicating whether there are NaN floating point values. It is difficult to reliably generate a NaN in Perl, so in some unlikely circumstances it is possible that there might be NaNs that this module failed to detect. In that case this constant would be false but a NaN might still turn up somewhere. What this constant reliably indicates is the availability of the C constant below. =back =head2 Extrema =over =item significand_bits The number of fractional bits in the significand of finite floating point values. The significand also has an implicit integer bit, not counted in this constant; the integer bit is always 1 for normalised values and always 0 for subnormal values. =item significand_step The difference between adjacent representable values in the range [1, 2] (where the exponent is zero). This is equal to 2^-significand_bits. =item max_finite_exp The maximum exponent permitted for finite floating point values. =item max_finite_pow2 The maximum representable power of two. This is 2^max_finite_exp. =item max_finite The maximum representable finite value. This is 2^(max_finite_exp+1) - 2^(max_finite_exp-significand_bits). =item max_number The maximum representable number. This is positive infinity if there are infinite values, or max_finite if there are not. =item max_integer The maximum integral value for which all integers from zero to that value inclusive are representable. Equivalently: the minimum positive integral value N for which the value N+1 is not representable. This is 2^(significand_bits+1). The name is somewhat misleading. =item min_normal_exp The minimum exponent permitted for normalised floating point values. =item min_normal The minimum positive value representable as a normalised floating point value. This is 2^min_normal_exp. =item min_finite_exp The base two logarithm of the minimum representable positive finite value. If there are subnormals then this is min_normal_exp - significand_bits. If there are no subnormals then this is min_normal_exp. =item min_finite The minimum representable positive finite value. This is 2^min_finite_exp. =back =head2 Special Values =over =item pos_zero The positive zero value. (Exists only if zeroes are signed, as indicated by the C constant.) If Perl is at risk of transforming floating point zeroes into integer zeroes (see L), then this is actually a non-constant function that always returns a fresh floating point zero. Thus the return value is always a true floating point zero, regardless of what happened to zeroes previously returned. =item neg_zero The negative zero value. (Exists only if zeroes are signed, as indicated by the C constant.) If Perl is at risk of transforming floating point zeroes into integer zeroes (see L), then this is actually a non-constant function that always returns a fresh floating point zero. Thus the return value is always a true floating point zero, regardless of what happened to zeroes previously returned. =item pos_infinity The positive infinite value. (Exists only if there are infinite values, as indicated by the C constant.) =item neg_infinity The negative infinite value. (Exists only if there are infinite values, as indicated by the C constant.) =item nan Not-a-number. (Exists only if NaN values were detected, as indicated by the C constant.) =back =cut sub _mk_constant($$) { my($name, $value) = @_; no strict "refs"; *{__PACKAGE__."::".$name} = sub () { $value }; push @EXPORT_OK, $name; } # # mult_pow2() multiplies a specified value by a specified power of two. # This is done using repeated multiplication, and can cope with cases # where the power of two cannot be directly represented as a floating # point value. (E.g., 0x1.b2p-900 can be multiplied by 2^1500 to get # to 0x1.b2p+600; the input and output values can be represented in # IEEE double, but 2^1500 cannot.) Overflow and underflow can occur. # # @powtwo is an array such that powtwo[i] = 2^2^i. Its elements are # used in the repeated multiplication in mult_pow2. Similarly, # @powhalf is such that powhalf[i] = 2^-2^i. Reading the exponent # in binary indicates which elements of @powtwo/@powhalf to multiply # by, except that it may indicate elements that don't exist, either # because they're not representable or because the arrays haven't # been filled yet. mult_pow2() will use the last element of the array # repeatedly in this case. Thus array elements after the first are # only an optimisation, and do not change behaviour. # my @powtwo = (2.0); my @powhalf = (0.5); sub mult_pow2($$) { my($value, $exp) = @_; return $_[0] if $value == 0.0; my $powa = \@powtwo; if($exp < 0) { $powa = \@powhalf; $exp = -$exp; } for(my $i = 0; $i != $#$powa && $exp != 0; $i++) { $value *= $powa->[$i] if $exp & 1; $exp >>= 1; } $value *= $powa->[-1] while $exp--; return $value; } # # Range of finite exponent values. # my $min_finite_exp; my $max_finite_exp; my $max_finite_pow2; my $min_finite; my @directions = ( { expsign => -1, powa => \@powhalf, xexp => \$min_finite_exp, xpower => \$min_finite, }, { expsign => +1, powa => \@powtwo, xexp => \$max_finite_exp, xpower => \$max_finite_pow2, }, ); while(!$directions[0]->{done} || !$directions[1]->{done}) { foreach my $direction (@directions) { next if $direction->{done}; my $lastpow = $direction->{powa}->[-1]; my $nextpow = $lastpow * $lastpow; unless(mult_pow2($nextpow, -$direction->{expsign} * (1 << (@{$direction->{powa}} - 1))) == $lastpow) { $direction->{done} = 1; next; } push @{$direction->{powa}}, $nextpow; } } foreach my $direction (@directions) { my $expsign = $direction->{expsign}; my $xexp = 1 << (@{$direction->{powa}} - 1); my $extremum = $direction->{powa}->[-1]; for(my $addexp = $xexp; $addexp >>= 1; ) { my $nx = mult_pow2($extremum, $expsign*$addexp); if(mult_pow2($nx, -$expsign*$addexp) == $extremum) { $xexp += $addexp; $extremum = $nx; } } ${$direction->{xexp}} = $expsign * $xexp; ${$direction->{xpower}} = $extremum; } _mk_constant("min_finite_exp", $min_finite_exp); _mk_constant("min_finite", $min_finite); _mk_constant("max_finite_exp", $max_finite_exp); _mk_constant("max_finite_pow2", $max_finite_pow2); # # pow2() generates a power of two from scratch. It complains if given # an exponent that would make an unrepresentable value. # sub pow2($) { my($exp) = @_; croak "exponent $exp out of range [$min_finite_exp, $max_finite_exp]" unless $exp >= $min_finite_exp && $exp <= $max_finite_exp; return mult_pow2(1.0, $exp); } # # Significand size. # my($significand_bits, $significand_step); { my $i; for($i = 1; ; $i++) { my $tryeps = $powhalf[$i]; last unless (1.0 + $tryeps) - 1.0 == $tryeps; } $i--; $significand_bits = 1 << $i; $significand_step = $powhalf[$i]; while($i--) { my $tryeps = $significand_step * $powhalf[$i]; if((1.0 + $tryeps) - 1.0 == $tryeps) { $significand_bits += 1 << $i; $significand_step = $tryeps; } } } _mk_constant("significand_bits", $significand_bits); _mk_constant("significand_step", $significand_step); my $max_finite = $max_finite_pow2 - pow2($max_finite_exp - $significand_bits - 1); $max_finite += $max_finite; my $max_integer = pow2($significand_bits + 1); _mk_constant("max_finite", $max_finite); _mk_constant("max_integer", $max_integer); # # Subnormals. # my $have_subnormal; { my $testval = $min_finite * 1.5; $have_subnormal = $testval == $min_finite || $testval == ($min_finite + $min_finite); } _mk_constant("have_subnormal", $have_subnormal); my $min_normal_exp = $have_subnormal ? $min_finite_exp + $significand_bits : $min_finite_exp; my $min_normal = $have_subnormal ? mult_pow2($min_finite, $significand_bits) : $min_finite; _mk_constant("min_normal_exp", $min_normal_exp); _mk_constant("min_normal", $min_normal); # # Feature tests. # my $have_signed_zero = sprintf("%e", -0.0) =~ /\A-/; _mk_constant("have_signed_zero", $have_signed_zero); my($pos_zero, $neg_zero); if($have_signed_zero) { $pos_zero = +0.0; $neg_zero = -0.0; my $tzero = -0.0; { no warnings "void"; $tzero == $tzero; } my $ntzero = -$tzero; if(sprintf("%e", -$ntzero) =~ /\A-/) { _mk_constant("pos_zero", $pos_zero); _mk_constant("neg_zero", $neg_zero); } else { # Zeroes lose their signedness upon arithmetic operations. # Therefore make the pos_zero and neg_zero functions # return fresh zeroes to avoid trouble. *pos_zero = sub () { my $ret = $pos_zero }; *neg_zero = sub () { my $ret = $neg_zero }; push @EXPORT_OK, "pos_zero", "neg_zero"; } } my($have_infinite, $pos_infinity, $neg_infinity); { my $testval = $max_finite * $max_finite; $have_infinite = $testval == $testval && $testval != $max_finite; _mk_constant("have_infinite", $have_infinite); if($have_infinite) { _mk_constant("pos_infinity", $pos_infinity = $testval); _mk_constant("neg_infinity", $neg_infinity = -$testval); } } my $max_number = $have_infinite ? $pos_infinity : $max_finite; _mk_constant("max_number", $max_number); my($have_nan, $nan); foreach my $nan_formula ( '$have_infinite && $pos_infinity/$pos_infinity', 'log(-1.0)', '0.0/0.0', '"nan"') { my $maybe_nan = eval 'local $SIG{__DIE__}; local $SIG{__WARN__} = sub { }; '. $nan_formula; if(do { local $SIG{__WARN__} = sub { }; $maybe_nan != $maybe_nan }) { $have_nan = 1; $nan = $maybe_nan; _mk_constant("nan", $nan); last; } } _mk_constant("have_nan", $have_nan); # The rest of the code is parsed after the constants have been calculated # and installed, so that it can benefit from their constancy. { local $/ = undef; my $code = ; close(DATA); { local $SIG{__DIE__}; eval $code; } die $@ if $@ ne ""; } 1; __DATA__ =head1 FUNCTIONS Each "float_" function takes a floating point argument to operate on. The argument must be a native floating point value, or a native integer with a value that can be represented in floating point. Giving a non-numeric argument will cause mayhem. See L for a way to check for numericness. Only the numeric value of the scalar is used; the string value is completely ignored, so dualvars are not a problem. =head2 Classification Each "float_is_" function returns a simple truth value result. =over =item float_class(VALUE) Determines which of the five classes described above VALUE falls into. Returns "NORMAL", "SUBNORMAL", "ZERO", "INFINITE", or "NAN" accordingly. =cut sub float_class($) { my($val) = @_; return "ZERO" if $val == 0.0; return "NAN" if $val != $val; $val = -$val if $val < 0; return "INFINITE" if have_infinite && $val == $pos_infinity; return have_subnormal && $val < min_normal ? "SUBNORMAL" : "NORMAL"; } =item float_is_normal(VALUE) Returns true iff VALUE is a normalised floating point value. =cut sub float_is_normal($) { float_class($_[0]) eq "NORMAL" } =item float_is_subnormal(VALUE) Returns true iff VALUE is a subnormal floating point value. =cut sub float_is_subnormal($) { float_class($_[0]) eq "SUBNORMAL" } =item float_is_nzfinite(VALUE) Returns true iff VALUE is a non-zero finite value (either normal or subnormal; not zero, infinite, or NaN). =cut sub float_is_infinite($); sub float_is_nzfinite($) { my($val) = @_; return $val != 0.0 && $val == $val && !float_is_infinite($val); } =item float_is_zero(VALUE) Returns true iff VALUE is a zero. If zeroes are signed then the sign is irrelevant. =cut sub float_is_zero($) { my($val) = @_; return $val == 0.0; } =item float_is_finite(VALUE) Returns true iff VALUE is a finite value (either normal, subnormal, or zero; not infinite or NaN). =cut sub float_is_finite($) { my($val) = @_; return $val == $val && !float_is_infinite($val); } =item float_is_infinite(VALUE) Returns true iff VALUE is an infinity (either positive infinity or negative infinity). =cut sub float_is_infinite($) { return undef unless have_infinite; my($val) = @_; return $val == $pos_infinity || $val == $neg_infinity; } =item float_is_nan(VALUE) Returns true iff VALUE is a NaN. =cut sub float_is_nan($) { my($val) = @_; return $val != $val; } =back =head2 Examination =over =item float_sign(VALUE) Returns "B<+>" or "B<->" to indicate the sign of VALUE. An unsigned zero returns the sign "B<+>". Cs if VALUE is a NaN. =cut sub signbit($); sub float_sign($) { my($val) = @_; croak "can't get sign of a NaN" if $val != $val; return signbit($val) ? "-" : "+"; } =item signbit(VALUE) VALUE must be a floating point value. Returns the sign bit of VALUE: 0 if VALUE is positive or a positive or unsigned zero, or 1 if VALUE is negative or a negative zero. Returns an unpredictable value if VALUE is a NaN. This is an IEEE 754 standard function. According to the standard NaNs have a well-behaved sign bit, but Perl can't see that bit. =cut sub signbit($) { my($val) = @_; return (have_signed_zero && $val == 0.0 ? sprintf("%+.f", $val) eq "-0" : $val < 0.0) ? 1 : 0; } =item float_parts(VALUE) Divides up a non-zero finite floating point value into sign, exponent, and significand, returning these as a three-element list in that order. The significand is returned as a floating point value, in the range [1, 2) for normalised values, and in the range (0, 1) for subnormals. Cs if VALUE is not finite and non-zero. =cut sub float_parts($) { my($val) = @_; croak "$val is not non-zero finite" unless float_is_nzfinite($val); my $sign = "+"; if($val < 0.0) { $sign = "-"; $val = -$val; } if(have_subnormal && $val < min_normal) { return ($sign, min_normal_exp, mult_pow2($val, -(min_normal_exp))); } my $exp = 0; if($val < 1.0) { for(my $i = @powhalf; $i--; ) { $exp <<= 1; if($val < $powhalf[$i]) { $exp |= 1; $val = mult_pow2($val, 1 << $i); } } $val *= 2.0; $exp = -1-$exp; } elsif($val >= 2.0) { for(my $i = @powtwo; $i--; ) { $exp <<= 1; if($val >= $powtwo[$i]) { $exp |= 1; $val = mult_pow2($val, -(1 << $i)); } } } return ($sign, $exp, $val); } =back =head2 String conversion =over =item float_hex(VALUE[, OPTIONS]) Encodes the exact value of VALUE as a hexadecimal fraction, returning the fraction as a string. Specifically, for finite values the output is of the form "IB<0x>IB<.>IB

I", where "I" is the sign, "IB<.>I" is the significand in hexadecimal, and "I" is the exponent in decimal with a sign. The details of the output format are very configurable. If OPTIONS is supplied, it must be a reference to a hash, in which these keys may be present: =over =item B The number of digits of exponent to show, unless this is modified by B or more are required to show the exponent exactly. (The exponent is always shown in full.) Default 0, so the minimum possible number of digits is used. =item B Modifies the number of exponent digits to show, based on the number of digits required to show the full range of exponents for normalised and subnormal values. If "B" then nothing is done. If "B" then at least this many digits are shown. Default "B". =item B The string that is prepended to a negative exponent. Default "B<->". =item B The string that is prepended to a non-negative exponent. Default "B<+>". Make it the empty string to suppress the positive sign. =item B The number of fractional digits to show, unless this is modified by B or B. Default 0, but by default this gets modified. =item B Modifies the number of fractional digits to show, based on the length of the significand. There is a certain number of digits that is the minimum required to explicitly state every bit that is stored, and the number of digits to show might get set to that number depending on this option. If "B" then nothing is done. If "B" then at least this many digits are shown. If "B" then at most this many digits are shown. If "B" then exactly this many digits are shown. Default "B". =item B Modifies the number of fractional digits to show, based on the number of digits required to show the actual value exactly. Works the same way as B. Default "B". =item B The string that is prefixed to hexadecimal digits. Default "B<0x>". Make it the empty string to suppress the prefix. =item B The string that is returned for an infinite magnitude. Default "B". =item B The string that is returned for a NaN value. Default "B". =item B The string that is prepended to a negative value (including negative zero). Default "B<->". =item B The string that is prepended to a positive value (including positive or unsigned zero). Default "B<+>". Make it the empty string to suppress the positive sign. =item B The manner in which subnormal values are displayed. If "B", they are shown with the minimum exponent for normalised values and a significand in the range (0, 1). This matches how they are stored internally. If "B", they are shown with a significand in the range [1, 2) and a lower exponent, as if they were normalised. This gives a consistent appearance for magnitudes regardless of normalisation. Default "B". =item B The manner in which zero values are displayed. If "BI", the string I is used, preceded by a sign. If "B", it is shown with significand zero and the minimum normalised exponent. If "BI", it is shown with significand zero and exponent I. Default "B". An unsigned zero is treated as having a positive sign. =back =cut my %float_hex_defaults = ( infinite_string => "inf", nan_string => "nan", exp_neg_sign => "-", exp_pos_sign => "+", pos_sign => "+", neg_sign => "-", hex_prefix_string => "0x", subnormal_strategy => "SUBNORMAL", zero_strategy => "STRING=0.0", frac_digits => 0, frac_digits_bits_mod => "ATLEAST", frac_digits_value_mod => "ATLEAST", exp_digits => 0, exp_digits_range_mod => "IGNORE", ); sub _float_hex_option($$) { my($options, $name) = @_; my $val = defined($options) ? $options->{$name} : undef; return defined($val) ? $val : $float_hex_defaults{$name}; } use constant exp_digits_range => do { my $minexp = min_normal_exp - significand_bits; my $maxexp = max_finite_exp + 1; my $len_minexp = length(-$minexp); my $len_maxexp = length($maxexp); $len_minexp > $len_maxexp ? $len_minexp : $len_maxexp; }; use constant frac_digits_bits => (significand_bits + 3) >> 2; use constant frac_sections => do { use integer; (frac_digits_bits + 6) / 7; }; sub float_hex($;$) { my($val, $options) = @_; return _float_hex_option($options, "nan_string") if $val != $val; if(have_infinite) { my $inf_sign; if($val == $pos_infinity) { $inf_sign = _float_hex_option($options, "pos_sign"); EMIT_INFINITY: return $inf_sign. _float_hex_option($options, "infinite_string"); } elsif($val == $neg_infinity) { $inf_sign = _float_hex_option($options, "neg_sign"); goto EMIT_INFINITY; } } my($sign, $exp, $sgnf); if($val == 0.0) { $sign = float_sign($val); my $strat = _float_hex_option($options, "zero_strategy"); if($strat =~ /\ASTRING=(.*)\z/s) { my $string = $1; return _float_hex_option($options, $sign eq "-" ? "neg_sign" : "pos_sign"). $string; } elsif($strat eq "SUBNORMAL") { $exp = min_normal_exp; } elsif($strat =~ /\AEXPONENT=([-+]?[0-9]+)\z/) { $exp = $1; } else { croak "unrecognised zero strategy `$strat'"; } $sgnf = 0.0; } else { ($sign, $exp, $sgnf) = float_parts($val); } my $digits = int($sgnf); if($digits eq "0" && $sgnf != 0.0) { my $strat = _float_hex_option($options, "subnormal_strategy"); if($strat eq "NORMAL") { my $add_exp; (undef, $add_exp, $sgnf) = float_parts($sgnf); $exp += $add_exp; $digits = "1"; } elsif($strat eq "SUBNORMAL") { # do nothing extra } else { croak "unrecognised subnormal strategy `$strat'"; } } $sgnf -= $digits; for(my $i = frac_sections; $i--; ) { $sgnf *= 268435456.0; my $section = int($sgnf); $digits .= sprintf("%07x", $section); $sgnf -= $section; } $digits =~ s/(.)0+\z/$1/; my $ndigits = 1 + _float_hex_option($options, "frac_digits"); croak "negative number of digits requested" if $ndigits <= 0; my $mindigits = 1; my $maxdigits = $ndigits + frac_digits_bits; foreach my $constraint (["frac_digits_bits_mod", 1+frac_digits_bits], ["frac_digits_value_mod", length($digits)]) { my($optname, $number) = @$constraint; my $mod = _float_hex_option($options, $optname); if($mod =~ /\A(?:ATLEAST|EXACTLY)\z/) { $mindigits = $number if $mindigits < $number; } if($mod =~ /\A(?:ATMOST|EXACTLY)\z/) { $maxdigits = $number if $maxdigits > $number; } croak "unrecognised length modification setting `$mod'" unless $mod =~ /\A(?:AT(?:MO|LEA)ST|EXACTLY|IGNORE)\z/; } croak "incompatible length constraints" if $maxdigits < $mindigits; $ndigits = $ndigits < $mindigits ? $mindigits : $ndigits > $maxdigits ? $maxdigits : $ndigits; if($ndigits > length($digits)) { $digits .= "0" x ($ndigits - length($digits)); } elsif($ndigits < length($digits)) { my $chopped = substr($digits, $ndigits, length($digits), ""); if($chopped =~ /\A[89abcdef]/ && !($chopped =~ /\A80*\z/ && $digits =~ /[02468ace]\z/)) { for(my $i = length($digits) - 1; ; ) { my $d = substr($digits, $i, 1); $d =~ tr/0-9a-f/1-9a-f0/; substr($digits, $i, 1, $d); last unless $d eq "0"; } if($digits =~ /\A2/) { $exp++; substr($digits, 0, 1, "1"); } } } my $nexpdigits = _float_hex_option($options, "exp_digits"); my $mod = _float_hex_option($options, "exp_digits_range_mod"); if($mod eq "ATLEAST") { $nexpdigits = exp_digits_range if $nexpdigits < exp_digits_range; } elsif($mod ne "IGNORE") { croak "unrecognised exponent length ". "modification setting `$mod'"; } $digits =~ s/\A(.)(.)/$1.$2/; return sprintf("%s%s%sp%s%0*d", _float_hex_option($options, $sign eq "-" ? "neg_sign" : "pos_sign"), _float_hex_option($options, "hex_prefix_string"), $digits, _float_hex_option($options, $exp < 0 ? "exp_neg_sign" : "exp_pos_sign"), $nexpdigits, abs($exp)); } =item hex_float(STRING) Generates and returns a floating point value from a string encoding it in hexadecimal. The standard input form is "[I][B<0x>]I[B<.>I][B

I]", where "I" is the sign, "I[B<.>I]" is a (fractional) hexadecimal number, and "I" an optionally-signed exponent in decimal. If present, the exponent identifies a power of two (not sixteen) by which the given fraction will be multiplied. If the value given in the string cannot be exactly represented in the floating point type because it has too many fraction bits, the nearest representable value is returned, with ties broken in favour of the value with a zero low-order bit. If the value given is too large to exactly represent then an infinity is returned, or the largest finite value if there are no infinities. Additional input formats are accepted for special values. "[I]B[B]" returns an infinity, or Cs if there are no infinities. "[I][B]B" returns a NaN, or Cs if there are no NaNs available. All input formats are understood case insensitively. The function correctly interprets all possible outputs from C with default settings. =cut sub hex_float($) { my($str) = @_; if($str =~ /\A([-+]?)(?:0x)?([0-9a-f]+)(?:\.([0-9a-f]+)+)? (?:p([-+]?[0-9]+))?\z/xi) { my($sign, $digits, $frac_digits, $in_exp) = ($1, $2, $3, $4); my $value; $frac_digits = "" unless defined $frac_digits; $in_exp = "0" unless defined $in_exp; $digits .= $frac_digits; $digits =~ s/\A0+//; if($digits eq "") { $value = 0.0; goto GOT_MAG; } my $digit_exp = (length($digits) - length($frac_digits)) * 4; my @limbs; push @limbs, hex($1) while $digits =~ /(.{7})/sgc; push @limbs, hex(substr($1."000000", 0, 7)) if $digits =~ /(.+)/sg; my $skip_bits = $limbs[0] < 0x4000000 ? $limbs[0] < 0x2000000 ? 3 : 2 : $limbs[0] < 0x8000000 ? 1 : 0; my $val_exp = $digit_exp - $skip_bits - 1 + $in_exp; my $sig_bits; if($val_exp > max_finite_exp) { $value = have_infinite ? Data::Float::pos_infinity() : max_finite; goto GOT_MAG; } elsif($val_exp < min_finite_exp-1) { $value = 0.0; goto GOT_MAG; } elsif($val_exp < min_normal_exp) { $sig_bits = $val_exp - (min_finite_exp-1); } else { $sig_bits = significand_bits+1; } my $gbit_lpos = do { use integer; ($skip_bits+$sig_bits)/28 }; if(@limbs > $gbit_lpos) { my $gbit_bpos = 27 - ($skip_bits + $sig_bits) % 28; my $sbit = 0; while(@limbs > $gbit_lpos+1) { $sbit = 1 if pop(@limbs) != 0; } my $gbit_mask = 1 << $gbit_bpos; my $sbit_mask = $gbit_mask - 1; if($limbs[$gbit_lpos] & $sbit_mask) { $sbit = 1; $limbs[$gbit_lpos] &= ~$sbit_mask; } if($limbs[$gbit_lpos] & $gbit_mask) { unless($sbit) { if($gbit_bpos == 27 && $gbit_lpos != 0) { $sbit = $limbs[$gbit_lpos - 1] & 1; } else { $sbit = $limbs[$gbit_lpos] & ($gbit_mask << 1); } } if($sbit) { $limbs[$gbit_lpos] += $gbit_mask; } else { $limbs[$gbit_lpos] -= $gbit_mask; } } } $value = 0.0; for(my $i = @limbs; $i--; ) { $value += mult_pow2($limbs[$i], -28*($i+1)); } $value = mult_pow2($value, $in_exp + $digit_exp); GOT_MAG: return $sign eq "-" ? -$value : $value; } elsif($str =~ /\A([-+]?)inf(?:inity)?\z/i) { croak "infinite values not available" unless have_infinite; return $1 eq "-" ? Data::Float::neg_infinity() : Data::Float::pos_infinity(); } elsif($str =~ /\A([-+]?)s?nan\z/si) { croak "Nan value not available" unless have_nan; return Data::Float::nan(); } else { croak "bad syntax for hexadecimal floating point value"; } } =back =head2 Comparison =over =item float_id_cmp(A, B) This is a comparison function supplying a total ordering of floating point values. A and B must both be floating point values. Returns -1, 0, or +1, indicating whether A is to be sorted before, the same as, or after B. The ordering is of the identities of floating point values, not their numerical values. If zeroes are signed, then the two types are considered to be distinct. NaNs compare equal to each other, but different from all numeric values. The exact ordering provided is mostly numerical order: NaNs come first, followed by negative infinity, then negative finite values, then negative zero, then positive (or unsigned) zero, then positive finite values, then positive infinity. In addition to sorting, this function can be useful to check for a zero of a particular sign. =cut sub float_id_cmp($$) { my($a, $b) = @_; if(float_is_nan($a)) { return float_is_nan($b) ? 0 : -1; } elsif(float_is_nan($b)) { return +1; } elsif(float_is_zero($a) && float_is_zero($b)) { return signbit($b) - signbit($a); } else { return $a <=> $b; } } =item totalorder(A, B) This is a comparison function supplying a total ordering of floating point values. A and B must both be floating point values. Returns a truth value indicating whether A is to be sorted before-or-the-same-as B. That is, it is a <= predicate on the total ordering. The ordering is the same as that provided by C: NaNs come first, followed by negative infinity, then negative finite values, then negative zero, then positive (or unsigned) zero, then positive finite values, then positive infinity. This is an IEEE 754r standard function. According to the standard it is meant to distinguish different kinds of NaNs, based on their sign bit, quietness, and payload, but this function (like the rest of Perl) perceives only one NaN. =cut sub totalorder($$) { float_id_cmp($_[0], $_[1]) <= 0 } =back =head2 Manipulation =over =item pow2(EXP) EXP must be an integer. Returns the value two the the power EXP. Cs if that value cannot be represented exactly as a floating point value. The return value may be either normalised or subnormal. =item mult_pow2(VALUE, EXP) EXP must be an integer, and VALUE a floating point value. Multiplies VALUE by two to the power EXP. This gives exact results, except in cases of underflow and overflow. The range of EXP is not constrained. All normal floating point multiplication behaviour applies. =item copysign(VALUE, SIGN_FROM) VALUE and SIGN_FROM must both be floating point values. Returns a floating point value with the magnitude of VALUE and the sign of SIGN_FROM. If SIGN_FROM is an unsigned zero then it is treated as positive. If VALUE is an unsigned zero then it is returned unchanged. If VALUE is a NaN then it is returned unchanged. If SIGN_FROM is a NaN then the sign copied to VALUE is unpredictable. This is an IEEE 754 standard function. According to the standard NaNs have a well-behaved sign bit, which can be read and modified by this function, but Perl only perceives one NaN and can't see its sign bit, so behaviour on NaNs is not standard-conforming. =cut sub copysign($$) { my($val, $signfrom) = @_; return $val if float_is_nan($val); $val = -$val if signbit($val) != signbit($signfrom); return $val; } =item nextup(VALUE) VALUE must be a floating point value. Returns the next representable floating point value adjacent to VALUE with a numerical value that is strictly greater than VALUE, or returns VALUE unchanged if there is no such value. Infinite values are regarded as being adjacent to the largest representable finite values. Zero counts as one value, even if it is signed, and it is adjacent to the smallest representable positive and negative finite values. If a zero is returned, because VALUE is the smallest representable negative value, and zeroes are signed, it is a negative zero that is returned. Returns NaN if VALUE is a NaN. This is an IEEE 754r standard function. =cut sub nextup($) { my($val) = @_; return $val if $val != $val || $val == max_number; return -(max_finite) if have_infinite && $val == -(max_number); return min_finite if $val == 0.0; my($sign, $exp, $significand) = float_parts($val); if($sign eq "+") { $significand += significand_step; if($significand == 2.0) { return max_number if have_infinite && $exp == max_finite_exp; $significand = 1.0; $exp++; } } else { if($significand == 1.0 && $exp != min_normal_exp) { $significand = 2.0; $exp--; } $significand -= significand_step; } return copysign(mult_pow2($significand, $exp), $val); } =item nextdown(VALUE) VALUE must be a floating point value. Returns the next representable floating point value adjacent to VALUE with a numerical value that is strictly less than VALUE, or returns VALUE unchanged if there is no such value. Infinite values are regarded as being adjacent to the largest representable finite values. Zero counts as one value, even if it is signed, and it is adjacent to the smallest representable positive and negative finite values. If a zero is returned, because VALUE is the smallest representable positive value, and zeroes are signed, it is a positive zero that is returned. Returns NaN if VALUE is a NaN. This is an IEEE 754r standard function. =cut sub nextdown($) { -nextup(-(my $n = $_[0])) } =item nextafter(VALUE, DIRECTION) VALUE and DIRECTION must both be floating point values. Returns the next representable floating point value adjacent to VALUE in the direction of DIRECTION, or returns DIRECTION if it is numerically equal to VALUE. Infinite values are regarded as being adjacent to the largest representable finite values. Zero counts as one value, even if it is signed, and it is adjacent to the positive and negative smallest representable finite values. If a zero is returned and zeroes are signed then it has the same sign as VALUE. Returns NaN if either argument is a NaN. This is an IEEE 754 standard function. =cut sub nextafter($$) { my($val, $dir) = @_; return $_[1] if $dir != $dir || $val == $dir; return $dir > $val ? nextup($_[0]) : nextdown($_[0]); } =back =head1 BUGS As of Perl 5.8.7 floating point zeroes will be partially transformed into integer zeroes if used in almost any arithmetic, including numerical comparisons. Such a transformed zero appears as a floating point zero (with its original sign) for some purposes, but behaves as an integer zero for other purposes. Where this happens to a positive zero the result is indistinguishable from a true integer zero. Where it happens to a negative zero the result is a fourth type of zero, the existence of which is a bug in Perl. This fourth type of zero will give confusing results, and in particular will elicit inconsistent behaviour from the functions in this module. Because of this transforming behaviour, it is best to avoid relying on the sign of zeroes. If you require signed-zero semantics then take special care to maintain signedness. Avoid using a zero directly in arithmetic and handle it as a special case. Any flavour of zero can be accurately copied from one scalar to another without affecting the original. The functions in this module all avoid modifying their arguments, and where they are meant to return signed zeroes they always return a pristine one. As of Perl 5.8.7 stringification of a floating point zero does not preserve its signedness. The number-to-string-to-number round trip turns a positive floating point zero into an integer zero, but accurately maintains negative and integer zeroes. If a negative zero gets partially transformed into an integer zero, as described above, the stringification that it gets is based on its state at the first occasion on which the scalar was stringified. NaN handling is generally not well defined in Perl. Arithmetic with a mathematically undefined result may either C or generate a NaN. Avoid relying on any particular behaviour for such operations, even if your hardware's behaviour is known. As of Perl 5.8.7 the B<%> operator truncates its arguments to integers, if the divisor is within the range of the native integer type. It therefore operates correctly on non-integer values only when the divisor is very large. =head1 SEE ALSO L, L, L =head1 AUTHOR Andrew Main (Zefram) Currently maintained by Robert Rothenberg =head1 COPYRIGHT Copyright (C) 2006, 2007, 2008, 2010, 2012, 2017, 2025 Andrew Main (Zefram) =head1 LICENSE This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; Data-Float-0.015/MANIFEST.SKIP0000644000175000017500000000012714773007146013172 0ustar rrrr.gitignore MANIFEST.bak .git/ _build/ MYMETA.* Makefile$ Data-Float-* blib/ pm_to_blib Data-Float-0.015/Changes0000644000175000017500000001516614773242340012575 0ustar rrrrRevision history for Data-Float: v0.015 2025-04-02 15:06+01:00 Europe/London [Documentation] - Update security policy and fix inconsistent contact instructions. This clarifies the license of the security policy. GH#3. - Update Changes to conform to the CPAN::Changes spec. GH#2. [Other] - Issues have been moved to GitHub. GH#1. v0.014 2025-03-28 17:08+00:00 Europe/London [Documentation] - Added a security policy. - Updated maintainer information. [Toolchain] - Removed use of Module::Build, and re-add Makefile.PL to the distribution. v0.013 2017-07-29 - no longer include a Makefile.PL in the distribution - in documentation, use four-column indentation for all verbatim material - in META.{yml,json}, point to public bug tracker v0.012 2012-02-04 - include META.json in distribution - convert .cvsignore to .gitignore - add MYMETA.json to .gitignore v0.011 2010-10-21 - port to Perl 5.13.6, where changed behaviour of signed zeroes in Perl arithmetic broke nextdown() and nextafter() and caused false test failures for several other functions v0.010 2010-07-26 - use simpler "parent" pragma in place of "base" - in documentation, use the term "truth value" instead of the less precise "boolean" - check for required Perl version at runtime - use full stricture in test suite - use full stricture in Build.PL - in Build.PL, explicitly declare configure-time requirements - remove bogus "exit 0" from Build.PL - add MYMETA.yml to .cvsignore v0.009 2008-04-06 - refer to optional constants more carefully in the code, to avoid syntax problems on systems that lack infinities and NaNs - fix a skip count that caused false test failures on systems lacking signed zeroes v0.008 2008-04-02 - bugfix: in initialisation, correctly override any ambient $SIG{__DIE__} v0.007 2007-10-02 - close DATA filehandle when finished reading from it - fix some tests that were producing false failures on perl 5.6 v0.006 2007-10-01 - bugfix: change behaviour of nextafter() when both arguments are numerically zero to that specified by IEEE 754: the second argument is returned rather than the first - bugfix: in hex_float(), cleanly reject non-ASCII digits in exponent - bugfix: in float_hex(), cleanly reject non-ASCII digits in an "EXPONENT="-type "zero_strategy" setting - add IEEE 754r functions nextup() and nextdown() - add IEEE 754r function totalorder() - add constant max_number - hex_float(): accept IEEE 754r special input strings "infinity" and "snan", in addition to the existing "inf" and "nan" - hex_float(): make the "0x" prefix in hexadecimal input optional - float_hex(): new option "hex_prefix_string" to control the "0x" prefix - test classification functions, examination functions, string conversion functions, and manipulation functions (all the functions that were not being tested) - test all functions for lack of side effects on zero arguments and purity of zero results - in documentation, note new standard-conforming behaviour of copysign() with a NaN second argument - in documentation, note that hex_float() accepts the special "0.0" form of input without a sign, as well as with one - in documentation, where the IEEE standard nature of functions is noted, add discussion of non-conforming behaviour on NaNs - in documentation, change some variable names in the synopsis for clarity - test POD syntax and coverage, and rename some internal functions to satisfy the coverage test - tweak tests on constants to avoid infinite loops if importing constant functions fails - build with Module::Build instead of ExtUtils::MakeMaker - complete dependency list - include signature in distribution - in documentation, separate "license" section from "copyright" section v0.005 2007-01-25 - bugfix: change behaviour of copysign() with a NaN as the second argument to that specified by IEEE 754: it is not an error but (in the context of Perl's opaque NaNs) results in copying an unpredictable sign - add hex_float() function to input floating point values in hexadecimal - add IEEE 754 function signbit() - float_id_cmp(): tighten specification of return values to match Perl's <=> operator (actual behaviour always matched the tighter spec, it just wasn't documented and tested) - in documentation, note that the string values of float arguments are ignored - in documentation, clarify note about implicit conversion of integer to float - in documentation, note that both arguments to nextafter() must be floating point values - in documentation, note standard nature of signbit(), copysign(), and nextafter() - in documentation, note float_id_cmp()'s relation to the IEEE 754r function totalorder() - in documentation, note that Perl does not distinguish between different NaNs - in documentation, give a second definition of max_integer (equivalent to the first) - add test for consistency of constants - in documentation, reference Scalar::Number - remove now-useless test t/use.t v0.004 2007-01-12 - bugfix: correct value for max_integer to 2^(significand_bits+1), and correct its description in the documentation - make pos_zero and neg_zero constants into non-constant functions that return fresh floating point zeroes, if running on a Perl where floating point zeroes can transmogrify into integer zeroes - add float_id_cmp() function for total ordering of floats - in documentation, more details of behaviour of zeroes - in documentation, discussion of interaction with native integer values - in documentation, note truncating behaviour of the % operator - in documentation, note the slightly misleading nature of the names "significand_bits" and "max_integer" - reference Data::Integer and perlnumber(1) in documentation v0.003 2006-08-08 - float_hex(): add OPTIONS parameter to control details of output formatting - when looking for NaNs, see whether the string "nan" qualifies - slight clarification to documentation of significand_step v0.002 2006-08-03 - bugfix: in mult_pow2(), copysign(), and nextafter(), take care to return a pristine signed zero when returning zero: they were returning zeroes that got broken (due to the Perl bug noted in the documentation) by internal arithmetic v0.001 2006-08-01 - add IEEE 754 functions copysign() and nextafter() - test sign of zero using sprintf() to avoid being confused by dualvars - slight expansion of initial paragraph of documentation v0.000 2006-07-30 - initial released version Data-Float-0.015/README0000644000175000017500000000146714773007146012164 0ustar rrrrNAME Data::Float - details of the floating point data type DESCRIPTION This module is about the native floating point numerical data type. A floating point number is one of the types of datum that can appear in the numeric part of a Perl scalar. This module supplies constants describing the native floating point type, classification functions, and functions to manipulate floating point values at a low level. INSTALLATION perl Makefile.PL make make test make install AUTHOR Andrew Main (Zefram) Currently maintained by Robert Rothenberg COPYRIGHT Copyright (C) 2006, 2007, 2008, 2010, 2012, 2017, 2025 Andrew Main (Zefram) LICENSE This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Data-Float-0.015/SECURITY.md0000644000175000017500000000732514773000317013065 0ustar rrrr# Security Policy for the Data-Float distribution. Security vulnerabilities can be reported via the project GitHub repository [Security Advisories](https://github.com/robrwo/Data-Float/security/advisories). This is the Security Policy for Data-Float. This text is based on the CPAN Security Group's Guidelines for Adding a Security Policy to Perl Distributions (version 1.2.0) https://security.metacpan.org/docs/guides/security-policy-for-authors.html # How to Report a Security Vulnerability Security vulnerabilities can be reported via the project GitHub repository [Security Advisories](https://github.com/robrwo/Data-Float/security/advisories). Please include as many details as possible, including code samples or test cases, so that we can reproduce the issue. Check that your report does not expose any sensitive data, such as passwords, tokens, or personal information. If you would like any help with triaging the issue, or if the issue is being actively exploited, please copy the report to the CPAN Security Group (CPANSec) at . Please *do not* use the public issue reporting system on RT or GitHub issues for reporting security vulnerabilities. Please do not disclose the security vulnerability in public forums until past any proposed date for public disclosure, or it has been made public by the maintainers or CPANSec. That includes patches or pull requests. For more information, see [Report a Security Issue](https://security.metacpan.org/docs/report.html) on the CPANSec website. ## Response to Reports The maintainer(s) aim to acknowledge your security report as soon as possible. However, this project is maintained by a single person in their spare time, and they cannot guarantee a rapid response. If you have not received a response from them within one week, then please send a reminder to them and copy the report to CPANSec at . Please note that the initial response to your report will be an acknowledgement, with a possible query for more information. It will not necessarily include any fixes for the issue. The project maintainer(s) may forward this issue to the security contacts for other projects where we believe it is relevant. This may include embedded libraries, system libraries, prerequisite modules or downstream software that uses this software. They may also forward this issue to CPANSec. # Which Software This Policy Applies To Any security vulnerabilities in Data-Float are covered by this policy. Security vulnerabilities are considered anything that allows users to execute unauthorised code, access unauthorised resources, or to have an adverse impact on accessibility or performance of a system. Security vulnerabilities in upstream software (embedded libraries, prerequisite modules or system libraries, or in Perl), are not covered by this policy unless they affect Data-Float, or Data-Float can be used to exploit vulnerabilities in them. Security vulnerabilities in downstream software (any software that uses Data-Float, or plugins to it that are not included with the Data-Float distribution) are not covered by this policy. ## Supported Versions of Data-Float The maintainer(s) will only commit to releasing security fixes for the latest version of Data-Float. # Installation and Usage Issues The distribution metadata specifies minimum versions of prerequisites that are required for Data-Float to work. However, some of these prerequisites may have security vulnerabilities, and you should ensure that you are using up-to-date versions of these prerequisites. Where security vulnerabilities are known, the metadata may indicate newer versions as recommended. ## Usage Please see the software documentation for further information. Data-Float-0.015/META.yml0000664000175000017500000000145314773242404012550 0ustar rrrr--- abstract: 'details of the floating point data type' author: - 'Robert Rothenberg ' build_requires: ExtUtils::MakeMaker: '0' Test::More: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.72, CPAN::Meta::Converter version 2.150010' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Data-Float no_index: directory: - t - inc requires: Carp: '0' Exporter: '0' constant: '0' integer: '0' parent: '0' perl: '5.006' strict: '0' warnings: '0' resources: bugtracker: https://github.com/robrwo/Data-Float/issues repository: git://github.com/robrwo/Data-Float.git version: '0.015' x_serialization_backend: 'CPAN::Meta::YAML version 0.020' Data-Float-0.015/Makefile.PL0000644000175000017500000000152414773007146013250 0ustar rrrr require 5.006; use ExtUtils::MakeMaker; WriteMakefile( 'NAME' => 'Data::Float', 'AUTHOR' => 'Robert Rothenberg ', 'ABSTRACT_FROM' => 'lib/Data/Float.pm', 'VERSION_FROM' => 'lib/Data/Float.pm', 'PREREQ_PM' => { "Carp" => 0, "Exporter" => 0, "constant" => 0, "integer" => 0, "parent" => 0, "perl" => "5.006", "strict" => 0, "warnings" => 0, }, TEST_REQUIRES => { 'Test::More' => 0, }, 'INSTALLDIRS' => 'site', 'EXE_FILES' => [], 'PL_FILES' => {}, 'SIGN' => 1, 'META_MERGE' => { resources => { repository => 'git://github.com/robrwo/Data-Float.git', bugtracker => 'https://github.com/robrwo/Data-Float/issues', }, } );