|
perlfaq4 - Data Manipulation ($Revision: 8539 $)
This section of the FAQ answers questions related to manipulating
numbers, dates, strings, arrays, hashes, and miscellaneous data issues.
Internally, your computer represents floating-point numbers in binary.
Digital (as in powers of two) computers cannot store all numbers
exactly. Some real numbers lose precision in the process. This is a
problem with how computers store numbers and affects all computer
languages, not just Perl.
the perlnumber manpage shows the gory details of number representations and
conversions.
To limit the number of decimal places in your numbers, you can use the
printf or sprintf function. See the "Floating Point Arithmetic" for more details.
printf "%.2f", 10/3;
my $number = sprintf "%.2f", 10/3;
Your int() is most probably working just fine. It's the numbers that
aren't quite what you think.
First, see the answer to "Why am I getting long decimals
(eg, 19.9499999999999) instead of the numbers I should be getting
(eg, 19.95)?".
For example, this
print int(0.6/0.2-2), "\n";
will in most computers print 0, not 1, because even such simple
numbers as 0.6 and 0.2 cannot be presented exactly by floating-point
numbers. What you think in the above as 'three' is really more like
2.9999999999999995559.
Perl only understands octal and hex numbers as such when they occur as
literals in your program. Octal literals in perl must start with a
leading 0 and hexadecimal literals must start with a leading 0x.
If they are read in from somewhere and assigned, no automatic
conversion takes place. You must explicitly use oct() or hex() if you
want the values converted to decimal. oct() interprets hexadecimal (0x350),
octal (0350 or even without the leading 0, like 377) and binary
(0b1010) numbers, while hex() only converts hexadecimal ones, with
or without a leading 0x, such as 0x255, 3A, ff, or deadbeef.
The inverse mapping from decimal to octal can be done with either the
<%o> or %O sprintf() formats.
This problem shows up most often when people try using chmod(),
mkdir(), umask(), or sysopen(), which by widespread tradition
typically take permissions in octal.
chmod(644, $file);
chmod(0644, $file);
Note the mistake in the first line was specifying the decimal literal
644, rather than the intended octal literal 0644. The problem can
be seen with:
printf("%#o",644);
Surely you had not intended chmod(01204, $file); - did you? If you
want to use numeric literals as arguments to chmod() et al. then please
try to express them as octal constants, that is with a leading zero and
with the following digits restricted to the set 0..7.
Remember that int() merely truncates toward 0. For rounding to a
certain number of digits, sprintf() or printf() is usually the
easiest route.
printf("%.3f", 3.1415926535);
The POSIX module (part of the standard Perl distribution)
implements ceil(), floor(), and a number of other mathematical
and trigonometric functions.
use POSIX;
$ceil = ceil(3.5);
$floor = floor(3.5);
In 5.000 to 5.003 perls, trigonometry was done in the Math::Complex
module. With 5.004, the Math::Trig module (part of the standard Perl
distribution) implements the trigonometric functions. Internally it
uses the Math::Complex module and some functions can break out from
the real axis into the complex plane, for example the inverse sine of
2.
Rounding in financial applications can have serious implications, and
the rounding method used should be specified precisely. In these
cases, it probably pays not to trust whichever system rounding is
being used by Perl, but to instead implement the rounding function you
need yourself.
To see why, notice how you'll still have an issue on half-way-point
alternation:
for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}
0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7
0.8 0.8 0.9 0.9 1.0 1.0
Don't blame Perl. It's the same as in C. IEEE says we have to do
this. Perl numbers whose absolute values are integers under 2**31 (on
32 bit machines) will work pretty much like mathematical integers.
Other numbers are not guaranteed.
As always with Perl there is more than one way to do it. Below are a
few examples of approaches to making common conversions between number
representations. This is intended to be representational rather than
exhaustive.
Some of the examples later in the perlfaq4 manpage use the Bit::Vector
module from CPAN. The reason you might choose Bit::Vector over the
perl built in functions is that it works with numbers of ANY size,
that it is optimized for speed on some operations, and for at least
some programmers the notation might be familiar.
- How do I convert hexadecimal into decimal
-
Using perl's built in conversion of 0x notation:
-
$dec = 0xDEADBEEF;
-
Using the hex function:
-
$dec = hex("DEADBEEF");
-
Using pack:
-
$dec = unpack("N", pack("H8", substr("0" x 8 . "DEADBEEF", -8)));
-
Using the CPAN module Bit::Vector:
-
use Bit::Vector;
$vec = Bit::Vector->new_Hex(32, "DEADBEEF");
$dec = $vec->to_Dec();
- How do I convert from decimal to hexadecimal
-
Using sprintf:
-
$hex = sprintf("%X", 3735928559);
$hex = sprintf("%x", 3735928559);
-
Using unpack:
-
$hex = unpack("H*", pack("N", 3735928559));
-
Using Bit::Vector:
-
use Bit::Vector;
$vec = Bit::Vector->new_Dec(32, -559038737);
$hex = $vec->to_Hex();
-
And Bit::Vector supports odd bit counts:
-
use Bit::Vector;
$vec = Bit::Vector->new_Dec(33, 3735928559);
$vec->Resize(32);
$hex = $vec->to_Hex();
- How do I convert from octal to decimal
-
Using Perl's built in conversion of numbers with leading zeros:
-
$dec = 033653337357;
-
Using the oct function:
-
$dec = oct("33653337357");
-
Using Bit::Vector:
-
use Bit::Vector;
$vec = Bit::Vector->new(32);
$vec->Chunk_List_Store(3, split(//, reverse "33653337357"));
$dec = $vec->to_Dec();
- How do I convert from decimal to octal
-
Using sprintf:
-
$oct = sprintf("%o", 3735928559);
-
Using Bit::Vector:
-
use Bit::Vector;
$vec = Bit::Vector->new_Dec(32, -559038737);
$oct = reverse join('', $vec->Chunk_List_Read(3));
- How do I convert from binary to decimal
-
Perl 5.6 lets you write binary numbers directly with
the 0b notation:
-
$number = 0b10110110;
-
Using oct:
-
my $input = "10110110";
$decimal = oct( "0b$input" );
-
Using pack and ord:
-
$decimal = ord(pack('B8', '10110110'));
-
Using pack and unpack for larger strings:
-
$int = unpack("N", pack("B32",
substr("0" x 32 . "11110101011011011111011101111", -32)));
$dec = sprintf("%d", $int);
-
-
Using Bit::Vector:
-
$vec = Bit::Vector->new_Bin(32, "11011110101011011011111011101111");
$dec = $vec->to_Dec();
- How do I convert from decimal to binary
-
Using sprintf (perl 5.6+):
-
$bin = sprintf("%b", 3735928559);
-
Using unpack:
-
$bin = unpack("B*", pack("N", 3735928559));
-
Using Bit::Vector:
-
use Bit::Vector;
$vec = Bit::Vector->new_Dec(32, -559038737);
$bin = $vec->to_Bin();
-
The remaining transformations (e.g. hex -> oct, bin -> hex, etc.)
are left as an exercise to the inclined reader.
The behavior of binary arithmetic operators depends on whether they're
used on numbers or strings. The operators treat a string as a series
of bits and work with that (the string "3" is the bit pattern
00110011). The operators work with the binary form of a number
(the number 3 is treated as the bit pattern 00000011).
So, saying 11 & 3 performs the "and" operation on numbers (yielding
3). Saying "11" & "3" performs the "and" operation on strings
(yielding "1").
Most problems with & and | arise because the programmer thinks
they have a number but really it's a string. The rest arise because
the programmer says:
if ("\020\020" & "\101\101") {
}
but a string consisting of two null bytes (the result of "\020\020"
& "\101\101") is not a false value in Perl. You need:
if ( ("\020\020" & "\101\101") !~ /[^\000]/) {
}
Use the Math::Matrix or Math::MatrixReal modules (available from CPAN)
or the PDL extension (also available from CPAN).
To call a function on each element in an array, and collect the
results, use:
@results = map { my_func($_) } @array;
For example:
@triple = map { 3 * $_ } @single;
To call a function on each element of an array, but ignore the
results:
foreach $iterator (@array) {
some_func($iterator);
}
To call a function on each integer in a (small) range, you can use:
@results = map { some_func($_) } (5 .. 25);
but you should be aware that the .. operator creates an array of
all integers in the range. This can take a lot of memory for large
ranges. Instead use:
@results = ();
for ($i=5; $i < 500_005; $i++) {
push(@results, some_func($i));
}
This situation has been fixed in Perl5.005. Use of .. in a for
loop will iterate over the range, without creating the entire range.
for my $i (5 .. 500_005) {
push(@results, some_func($i));
}
will not create a list of 500,000 integers.
Get the http://www.cpan.org/modules/by-module/Roman module.
If you're using a version of Perl before 5.004, you must call srand
once at the start of your program to seed the random number generator.
BEGIN { srand() if $] < 5.004 }
5.004 and later automatically call |