Peeking at natural floats with perl
July 18, 2007
If I say "floating point arithmetic", is your hair rising on your head?
If it is, you probably have experienced the kind of precision issues in arithmetic calculations that I have been troubled with lately.
If it isn't, take a long break and read about floating point and IEEE 754.
As a note to self and anyone interested, I thought I would gather in one post a few techniques that are useful to access the inner representation of numbers in perl.
Let $a be a scalar holding a number:
$a = 1.73696;
In perl, a scalar value (SV) is somewhat of an hybrid beast, and is really up to 3 different things at the same time, baked together within the SV structure (see perldoc perlguts).
An SV can contain an integer value (IV), a native float (NV) or/and a string (PV). Perl converts one representation into one of the others transparantly, depending on the operations performed on that SV.
To see what $a contains, you can use Devel::Peek:
use Devel::Peek;
my $a = 1.73696;
Dump($a);
This will output:
SV = NV(0x8f58810) at 0x8f4993c
REFCNT = 1
FLAGS = (PADBUSY,PADMY,NOK,pNOK)
NV = 1.73696
And you can see that $a is a scalar value (SV) holding only 1 representation, a natural float NV. But what Devel::Peek shows as being the value of this NV is more or less just a printf. You may want to know exactly what this float's sign, exponent and significand are. Data::Float does it for you:
use Data::Float qw(float_parts);
my ($s,$e,$f) = float_parts($a);
print "sign: $s\n";
print "exponent: $e\n";
print "significand: $f\n";
And the result is:
sign: +
exponent: 0
significand: 1.73696
Finally, you may want to know the exact binary representation of this float. Here is how you could do it:
my $bin = unpack("B64",reverse pack("d",$a));
print "sign: ".substr($bin,0,1)."\n";
print "exponent: ".substr($bin,1,11)."\n";
print "significand: ".substr($bin,12)."\n";
Resulting in:
sign: 0
exponent: 01111111111
significand: 1011110010101001011010010001101001110101110011010001
Note that this works for 64bits floats. You may have to adjust this code on platforms where floats are not 64 bits long.
If it is, you probably have experienced the kind of precision issues in arithmetic calculations that I have been troubled with lately.
If it isn't, take a long break and read about floating point and IEEE 754.
As a note to self and anyone interested, I thought I would gather in one post a few techniques that are useful to access the inner representation of numbers in perl.
Let $a be a scalar holding a number:
$a = 1.73696;
In perl, a scalar value (SV) is somewhat of an hybrid beast, and is really up to 3 different things at the same time, baked together within the SV structure (see perldoc perlguts).
An SV can contain an integer value (IV), a native float (NV) or/and a string (PV). Perl converts one representation into one of the others transparantly, depending on the operations performed on that SV.
To see what $a contains, you can use Devel::Peek:
use Devel::Peek;
my $a = 1.73696;
Dump($a);
This will output:
SV = NV(0x8f58810) at 0x8f4993c
REFCNT = 1
FLAGS = (PADBUSY,PADMY,NOK,pNOK)
NV = 1.73696
And you can see that $a is a scalar value (SV) holding only 1 representation, a natural float NV. But what Devel::Peek shows as being the value of this NV is more or less just a printf. You may want to know exactly what this float's sign, exponent and significand are. Data::Float does it for you:
use Data::Float qw(float_parts);
my ($s,$e,$f) = float_parts($a);
print "sign: $s\n";
print "exponent: $e\n";
print "significand: $f\n";
And the result is:
sign: +
exponent: 0
significand: 1.73696
Finally, you may want to know the exact binary representation of this float. Here is how you could do it:
my $bin = unpack("B64",reverse pack("d",$a));
print "sign: ".substr($bin,0,1)."\n";
print "exponent: ".substr($bin,1,11)."\n";
print "significand: ".substr($bin,12)."\n";
Resulting in:
sign: 0
exponent: 01111111111
significand: 1011110010101001011010010001101001110101110011010001
Note that this works for 64bits floats. You may have to adjust this code on platforms where floats are not 64 bits long.