Skip to content

Commit

Permalink
Merge pull request #16 from pryrt/v0.018bugfix
Browse files Browse the repository at this point in the history
v0.018004: final bugfix for convertToHexString() with ivsize=4; ready to reintegrate to trunk
  • Loading branch information
pryrt authored Oct 1, 2017
2 parents 010148a + aa5aa86 commit 327c107
Show file tree
Hide file tree
Showing 12 changed files with 977 additions and 216 deletions.
21 changes: 21 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
Revision history for Perl module Data::IEEE754::Tools.

v0.018003 2017-Oct-01
- binary64_convertToHexString(): bugfix verified; now works on ivsize=4;
remove debug code

v0.018003 2017-Sep-29
- Completely reworked the function, so it deals with one nibble at a time,
thus avoiding any overflow (easier to code than doing sub-32b groupings)
- possible bug in t/01*.t, but unverified and unstudied

v0.018002 2017-Sep-25
- Change from README.pod to README.md, because *.pod are built and put in
the module documentation directory
- Debugging roundoff on certain systems (not sure yet what the common
aspect of the systems are, because bsd or mswin for the same perl version
will both pass and fail -- my guess is 32bit vs 64bit machines, maybe
in the integer processing, but I need data that will show me what's
different, so I'm doing another "official" release that turns on debug
printing during the test suite for known-buggy values. (I would do alpha,
but the smoke testers didn't run when I tried v0.017_xxx alpha versions,
so sorry. This release should be no more broken than v0.018 or v0.018001)

v0.018001 2017-Sep-24
- for some reason, `make test` was invoking `make README.pod`: trying to fix

Expand Down
2 changes: 1 addition & 1 deletion MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ LICENSE
Makefile.PL
MANIFEST This list of files
lib/Data/IEEE754/Tools.pm
README.pod
README.md
RELEASE.md
t/00-load.t
t/01-internalstring.t
Expand Down
4 changes: 3 additions & 1 deletion Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ realclean ::
$(NOECHO) ( $(TEST_D) cover_db && $(RM_RF) cover_db ) || $(ECHO) realclean:: skip "rm -rf cover_db"
# auto-generate the README from the lib/Data/IEEE754/Tools.pm
README.pod :: lib/Data/IEEE754/Tools.pm
README.md :: lib/Data/IEEE754/Tools.pm
podselect -section "NAME|SYNOPSIS|DESCRIPTION|COMPATIBILITY|INSTALLATION|AUTHOR|COPYRIGHT|LICENSE/!IEEE 754 Encoding" lib/Data/IEEE754/Tools.pm > README.pod
pod2markdown README.pod README.md
$(RM_F) README.pod
POSTAMBLE
}
Expand Down
106 changes: 106 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# NAME

Data::IEEE754::Tools - Various tools for understanding and manipulating the underlying IEEE-754 representation of floating point values

# SYNOPSIS

use Data::IEEE754::Tools qw/:convertToString :ulp/;

# return -12.875 as strings of decimal or hexadecimal floating point numbers ("convertTo*Character" in IEEE-754 parlance)
convertToDecimalString(-12.875); # -0d1.6093750000000000p+0003
convertToHexString(-12.875); # -0x1.9c00000000000p+0003

# shows the smallest value you can add or subtract to 16.16 (ulp = "Unit in the Last Place")
print ulp( 16.16 ); # 3.5527136788005e-015

# toggles the ulp: returns a float that has the ULP of 16.16 toggled
# (if it was a 1, it will be 0, and vice versa);
# running it twice should give the original value
print $t16 = toggle_ulp( 16.16 ); # 16.159999999999997
print $v16 = toggle_ulp( $t16 ); # 16.160000000000000

# DESCRIPTION

These tools give access to the underlying IEEE 754 floating-point 64bit representation
used by many instances of Perl (see [perlguts](https://metacpan.org/pod/perlguts)). They include functions for converting
from the 64bit internal representation to a string that shows those bits (either as
hexadecimal or binary) and back, functions for converting that encoded value
into a more human-readable format to give insight into the meaning of the encoded
values, and functions to manipulate the smallest possible change for a given
floating-point value (which is the [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place) or
"Unit in the Last Place").

## Justification for the existence of **Data::IEEE754::Tools**

[Data::IEEE754](https://metacpan.org/pod/Data::IEEE754), or the equivalent ["pack" in perlfunc](https://metacpan.org/pod/perlfunc#pack) recipe [d>](https://metacpan.org/pod/d>), do a
good job of converting a perl floating value (NV) into the big-endian bytes
that encode that value, but they don't help you interpret the value.

[Data::Float](https://metacpan.org/pod/Data::Float) has a similar suite of tools to **Data::IEEE754::Tools**, but
uses numerical methods rather than accessing the underlying bits. It [has been
shown](http://perlmonks.org/?node_id=1167146) that its interpretation function can take
an order of magnitude longer than a routine that manipulates the underlying bits
to gather the information.

This **Data::IEEE754::Tools** module combines the two sets of functions, giving
access to the raw IEEE 754 encoding, or a stringification of the encoding which
interprets the encoding as a sign and a coefficient and a power of 2, or access to
the ULP and ULP-manipulating features, all using direct bit manipulation when
appropriate.

## Compatibility

**Data::IEEE754::Tools** works with 64bit floating-point representations.

If you have a Perl setup which uses a larger representation (for example,
`use [Config](https://metacpan.org/pod/Config); print $Config{nvsize}; # 16 => 128bit`), values reported by
this module will be reduced in precision to fit the 64bit representation.

If you have a Perl setup which uses a smaller representation (for example,
`use [Config](https://metacpan.org/pod/Config); print $Config{nvsize}; # 4 => 32bit`), the installation
will likely fail, because the unit tests were not set up for lower precision
inputs. However, forcing the installation _might_ still allow coercion
from the smaller Perl NV into a true IEEE 754 double (64bit) floating-point,
but there is no guarantee it will work.

# INSTALLATION

To install this module, use your favorite CPAN client.

For a manual install, type the following:

perl Makefile.PL
make
make test
make install

(On Windows machines, you may need to use "dmake" or "gmake" instead of "make", depending on your setup.)

# AUTHOR

Peter C. Jones `<petercj AT cpan DOT org>`

Please report any bugs or feature requests emailing `<bug-Data-IEEE754-Tools AT rt.cpan.org>`
or thru the web interface at [http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-IEEE754-Tools](http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-IEEE754-Tools),
or thru the repository's interface at [https://github.com/pryrt/Data-IEEE754-Tools/issues](https://github.com/pryrt/Data-IEEE754-Tools/issues).

<div>
<a href="https://metacpan.org/pod/Data::IEEE754::Tools><img src="https://img.shields.io/cpan/v/Data-IEEE754-Tools.svg?colorB=00CC00" alt="" title="metacpan"></a>
<a href="http://matrix.cpantesters.org/?dist=Data-IEEE754-Tools"><img src="http://cpants.cpanauthors.org/dist/Data-IEEE754-Tools.png" alt="" title="cpan testers"></a>
<a href="https://github.com/pryrt/Data-IEEE754-Tools/releases"><img src="https://img.shields.io/github/release/pryrt/Data-IEEE754-Tools.svg" alt="" title="github release"></a>
<a href="https://github.com/pryrt/Data-IEEE754-Tools/issues"><img src="https://img.shields.io/github/issues/pryrt/Data-IEEE754-Tools.svg" alt="" title="issues"></a>
<a href="https://travis-ci.org/pryrt/Data-IEEE754-Tools"><img src="https://travis-ci.org/pryrt/Data-IEEE754-Tools.svg?branch=master" alt="" title="build status"></a>
<a href="https://coveralls.io/github/pryrt/Data-IEEE754-Tools?branch=master"><img src="https://coveralls.io/repos/github/pryrt/Data-IEEE754-Tools/badge.svg?branch=master" alt="" title="test coverage"></a>
</div>

# COPYRIGHT

Copyright (C) 2016-2017 Peter C. Jones

# LICENSE

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See [http://dev.perl.org/licenses/](http://dev.perl.org/licenses/) for more information.
109 changes: 0 additions & 109 deletions README.pod

This file was deleted.

4 changes: 2 additions & 2 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ I use a local svn client to checkout the GitHub repo. All these things can be d
* `pod2text lib/Data/IEEE754/Tools.pm README`, then edit so that only
NAME, DESCRIPTION, COMPATIBILITY, INSTALLATION, AUTHOR, COPYRIGHT, LICENSE
remain
* or, with README.pod instead: `podselect -section "NAME|SYNOPSIS|DESCRIPTION|COMPATIBILITY|INSTALLATION|AUTHOR|COPYRIGHT|LICENSE/!IEEE 754 Encoding" lib\Data\IEEE754\Tools.pm > README.pod`
* or `dmake README.pod`
* or, with README.pod instead: `podselect -section "NAME|SYNOPSIS|DESCRIPTION|COMPATIBILITY|INSTALLATION|AUTHOR|COPYRIGHT|LICENSE/!IEEE 754 Encoding" lib\Data\IEEE754\Tools.pm > README.pod`, then `pod2markdown README.pod README.md`
* or `dmake README.md`
* verify CHANGES (history)

* **Build Distribution**
Expand Down
4 changes: 4 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
TODO
- bugfix:
- fix the v0.018 t/02*.t roundoff bug: it was overflowing 32bit on {ivsize}=4 systems;
new method should not
- testing on my 32b linux box, found failures in t/01*.t suite; needs to be investigated
- long-term: implement more functions from IEEE754-2007, especially
+ IEEE754 5.4.2 => 5.12
☐ <format>-convertFormat
Expand Down
72 changes: 20 additions & 52 deletions lib/Data/IEEE754/Tools.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use Carp;
use Exporter 'import'; # just use the import() function, without the rest of the overhead of ISA
use Config;

our $VERSION = '0.018001';
our $VERSION = '0.018004';
# use rrr.mmm_aaa, where rrr is major revision, mmm is ODD minor revision, and aaa is alpha sub-revision (for ALPHA code)
# use rrr.mmmsss, where rrr is major revision, mmm is EVEN minor revision, and sss is a sub-revision (usually sss=000) (for releases)

Expand Down Expand Up @@ -404,13 +404,16 @@ hex-digits or 16 decimal-digits).
sub binary64_convertToHexString {
# thanks to BrowserUK @ http://perlmonks.org/?node_id=1167146 for slighly better decision factors
# I tweaked it to use the two 32bit words instead of one 64bit word (which wouldn't work on some systems)
# v0.018003: to fix integer overflow on ivsize=4, reworked to pure nibbles
my $v = shift;
my $p = defined $_[0] ? shift : 13;
my ($msb,$lsb) = $_helper64_arr2x32b->($v);
my $sbit = ($msb & 0x80000000) >> 31;
my $sign = $sbit ? '-' : '+';
my $exp = (($msb & 0x7FF00000) >> 20) - 1023;
my $mant = sprintf '%05x%08x', $msb & 0x000FFFFF, $lsb & 0xFFFFFFFF;
my $mhex = sprintf '%05x', $msb & 0x000FFFFF;
my $lhex = sprintf '%08x', $lsb & 0xFFFFFFFF;
my $mant = $mhex . $lhex;
if($exp == 1024) {
my $z = "0"x (($p<5?4:$p)-4);
return $sign . "0x1.#INF${z}p+0000" if $mant eq '0000000000000';
Expand All @@ -423,57 +426,22 @@ sub binary64_convertToHexString {
$implied = 0;
$exp = $mant eq '0000000000000' ? 0 : -1022; # 0 for zero, -1022 for denormal
}
if($p<13) {
my $m = $msb & 0xFFFFF;
my $l = $lsb;
my $o = 0;
if($p>=5) { # use all of MSB, and move into LSB
my $one = 1 << 4*( 8 - ($p-5) );
my $haf = $one >> 1;
my $eff = $one - 1;
my $msk = 0xFFFFFFFF ^ $eff;
if( ($l & $eff) >= $haf) {
$l = ($l & $msk) + $one;
my $l32 = $l & 0xFFFFFFFF;
if($l32 < $one) {
$l = 0;
$m++;
}
} else {
$l = ($l & $msk);
}
if($m >= 0x1_0_0000) {
$o = 1;
$m -= 0x1_0_0000;
}
if($o) {
$implied++;
}
} else { # thus p<5
$l = 0; # don't need the lowest 8 nibbles...
my $one = 1 << 4*( 5 - $p );
my $haf = $one >> 1;
my $eff = $one - 1;
my $msk = 0xFFFFF ^ $eff;
if( ($m & $eff) >= $haf) {
$m = ($m & $msk) + $one;
my $m20 = $m & 0xFFFFF;
if($m20 < $one) {
$m = 0;
$o++;
}
} else {
$m = ($m & $msk);
}
if($o) {
$implied++;
}
}

my $f = substr( sprintf('%05x%08x', $m, $l), 0, $p);
return sprintf '%s0x%1u%s%*sp%+05d', $sign, $implied, $p?'.':'', $p, $f, $exp;
} else { # thus, p>=13:
if( $p>12 ) {
return sprintf '%s0x%1u.%13.13sp%+05d', $sign, $implied, $mant . '0'x($p-13), $exp;
} else {
my $roundhex = substr $mant, 0, $p;
my $nibble = substr $mant, $p, 1;
my $carry = hex($nibble)>7 ? 1 : 0;
foreach my $cp ( 1 .. $p ) {
$nibble = substr $roundhex, -$cp, 1;
my $v = hex($nibble)+$carry;
($carry, $v) = (16==$v) ? (1,0) : (0, $v);
$nibble = sprintf '%01x', $v;
substr($roundhex, -$cp, 1) = $nibble;
}
$implied += $carry;
my $ret = sprintf '%s0x%1u%s%*sp%+05d', $sign, $implied, $p?'.':'', $p, $roundhex, $exp;
return $ret;
}
}
*convertToHexString = \&binary64_convertToHexString;
Expand Down
Loading

0 comments on commit 327c107

Please sign in to comment.