Using Perl to split a float - perl

Why did I lose precision using split?
My goal is to get just the fractional part, all of it.
$a = 123456789.123456789;
#b = split(/\./, $a);
$baseDec = "." . $b[1];
Above gives $baseDec == .123457
But this gives the correct precision:
Is it the right way to do it? CORRECTION: THIS GIVES THIS SAME BAD PRECISION!
I did not properly test the code. Sorry!
$a = 123456789.123456789;
#b = split(/\./, $a);
$baseInt = $b[0];
$baseDec = $a - $baseInt;
Should I be using Math::BigFloat?
Edit: $a should be a string $a = "123456789.123456789"; and then the original code works. Until I figure out how to get my Perl to work with longdouble, I can't test the original question. The answer seems to be that I lost precision because $a is being stored in a double (52 bits ~ 15 decimal digits, as #Ben stated below). print $a gives 123456789.123457.

You can use int:
$a = 123456789.123456789;
$baseDec = $a - int($a);

You lost precision because 123456789.123456789 is a numeric literal which, by default, the Perl compiler stores in a double (52 bits, ~15 decimal digits) $a. Next, when #b = split(/\./, $a); runs, $a is implicitly coerced into a string before it can be split.
If your Perl were compiled to use longdoubles (See: perl -V:uselongdouble and perl -V:doublesize), the numeric literal would have been represented with 80 bits (~21 decimal digits) and string coercion.

IF you're going to treat it as a string, do so all the way through. You wouldn't assign a string without quoting, right?
my $a = "123456789.123456789";

You can get it by using sprintf:
my $a = 123456789.123456789;
my #b = split(/\./, sprintf("%.8f",$a));
my $baseDec = "." . $b[1];
print $baseDec;

Related

How to do bitwise compare of two hex strings?

I have two strings representing hex numbers which I need to make a bitwise comparison of. Each hex number equates to 256 bits. I need to determine how many of the bits are different. How can I do this in perl?
$hash1 = "7ff005f88270898ec31359b9ca80213165a318f149267e4c2f292a00e216e4ef";
$hash2 = "3fb40df88a78890e815251b1fb8021356da330f149266f453f292a11e216e4ee";
My question is similar to this question but I need to do it in perl.
my $bytes1 = pack('H*', $hash1);
my $bytes2 = pack('H*', $hash2);
my $xor = unpack('B*', $bytes1 ^ $bytes2);
my $count = $xor =~ tr/1//;
pack('H*', ...) converts the hex strings into byte strings. The byte strings are then XORed and converted to a bit string with unpack('B*', ...). The tr operator is used to count the number of 1s (different bits) in the bit string.
Or, using the checksum trick described here:
my $bytes1 = pack('H*', $hash1);
my $bytes2 = pack('H*', $hash2);
my $count = unpack('%32B*', $bytes1 ^ $bytes2);
$hash1 =~ s/([a-f0-9][a-f0-9])/unpack('B*',pack('H*',$1))/egi;
$hash2 =~ s/([a-f0-9][a-f0-9])/unpack('B*',pack('H*',$1))/egi;
$count = ($hash1 ^ $hash2) =~ tr/\0//c;

Appending hex value to string

$temp = 'abc'.0x12;
print $temp; # prints abc18
While appending the hex value to string it is getting converted to decimal value and final result is string. what I want is that the ascii value of the hex should get appended to the string.
for e.g. 0x12 in ascii is DC2 (device control 2).
Try using chr:
my $a = 'abc'.chr 0x3e;
print $a;
I think Sly Raskal was on the right track but instead of %x use %c, so:
my $hex = 0x12;
my $ascii = sprintf "%c", $hex;
my $temp = 'abc' . $ascii;
I think this gives the result you were looking for. I got it from http://www.perlmonks.org/?node_id=191039 .

Difference between $var = 500 and $var = '500'

In Perl, what is the difference between
$status = 500;
and
$status = '500';
Not much. They both assign five hundred to $status. The internal format used will be different initially (IV vs PV,UTF8=0), but that's of no importance to Perl.
However, there are things that behave different based on the choice of storage format even though they shouldn't. Based on the choice of storage format,
JSON decides whether to use quotes or not.
DBI guesses the SQL type it should use for a parameter.
The bitwise operators (&, | and ^) guess whether their operands are strings or not.
open and other file-related builtins encode the file name using UTF-8 or not. (Bug!)
Some mathematical operations return negative zero or not.
As already #ikegami told not much. But remember than here is MUCH difference between
$ perl -E '$v=0500; say $v'
prints 320 (decimal value of 0500 octal number), and
$ perl -E '$v="0500"; say $v'
what prints
0500
and
$ perl -E '$v=0900; say $v'
what dies with error:
Illegal octal digit '9' at -e line 1, at end of line
Execution of -e aborted due to compilation errors.
And
perl -E '$v="0300";say $v+1'
prints
301
but
perl -E '$v="0300";say ++$v'
prints
0301
similar with 0x\d+, e.g:
$v = 0x900;
$v = "0x900";
There is only a difference if you then use $var with one of the few operators that has different flavors when operating on a string or a number:
$string = '500';
$number = 500;
print $string & '000', "\n";
print $number & '000', "\n";
output:
000
0
To provide a bit more context on the "not much" responses, here is a representation of the internal data structures of the two values via the Devel::Peek module:
user#foo ~ $ perl -MDevel::Peek -e 'print Dump 500; print Dump "500"'
SV = IV(0x7f8e8302c280) at 0x7f8e8302c288
REFCNT = 1
FLAGS = (PADTMP,IOK,READONLY,pIOK)
IV = 500
SV = PV(0x7f8e83004e98) at 0x7f8e8302c2d0
REFCNT = 1
FLAGS = (PADTMP,POK,READONLY,pPOK)
PV = 0x7f8e82c1b4e0 "500"\0
CUR = 3
LEN = 16
Here is a dump of Perl doing what you mean:
user#foo ~ $ perl -MDevel::Peek -e 'print Dump ("500" + 1)'
SV = IV(0x7f88b202c268) at 0x7f88b202c270
REFCNT = 1
FLAGS = (PADTMP,IOK,READONLY,pIOK)
IV = 501
The first is a number (the integer between 499 and 501). The second is a string (the characters '5', '0', and '0'). It's not true that there's no difference between them. It's not true that one will be converted immediately to the other. It is true that strings are converted to numbers when necessary, and vice-versa, and the conversion is mostly transparent, but not completely.
The answer When does the difference between a string and a number matter in Perl 5 covers some of the cases where they're not equivalent:
Bitwise operators treat numbers numerically (operating on the bits of the binary representation of each number), but they treat strings character-wise (operating on the bits of each character of each string).
The JSON module will output a string as a string (with quotes) even if it's numeric, but it will output a number as a number.
A very small or very large number might stringify differently than you expect, whereas a string is already a string and doesn't need to be stringified. That is, if $x = 1000000000000000 and $y = "1000000000000000" then $x might stringify to 1e+15. Since using a variable as a hash key is stringifying, that means that $hash{$x} and $hash{$y} may be different hash slots.
The smart-match (~~) and given/when operators treat number arguments differently from numeric strings. Best to avoid those operators anyway.
There are different internally:)
($_ ^ $_) ne '0' ? print "$_ is string\n" : print "$_ is numeric\n" for (500, '500');
output:
500 is numeric
500 is string
I think this perfectly demonstrates what is going on.
$ perl -MDevel::Peek -e 'my ($a, $b) = (500, "500");print Dump $a; print Dump $b; $a.""; $b+0; print Dump $a; print Dump $b'
SV = IV(0x8cca90) at 0x8ccaa0
REFCNT = 1
FLAGS = (PADMY,IOK,pIOK)
IV = 500
SV = PV(0x8acc20) at 0x8ccad0
REFCNT = 1
FLAGS = (PADMY,POK,pPOK)
PV = 0x8c5da0 "500"\0
CUR = 3
LEN = 16
SV = PVIV(0x8c0f88) at 0x8ccaa0
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 500
PV = 0x8d3660 "500"\0
CUR = 3
LEN = 16
SV = PVIV(0x8c0fa0) at 0x8ccad0
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 500
PV = 0x8c5da0 "500"\0
CUR = 3
LEN = 16
Each scalar (SV) can have string (PV) and or numeric (IV) representation. Once you use variable with only string representation in any numeric operation and one with only numeric representation in any string operation they have both representations. To be correct, there can be also another number representation, the floating point representation (NV) so there are three possible representation of scalar value.
Many answers already to this question but i'll give it a shot for the confused newbie:
my $foo = 500;
my $bar = '500';
As they are, for practical pourposes they are the "same". The interesting part is when you use operators.
For example:
print $foo + 0;
output: 500
The '+' operator sees a number at its left and a number at its right, both decimals, hence the answer is 500 + 0 => 500
print $bar + 0;
output: 500
Same output, the operator sees a string that looks like a decimal integer at its left, and a zero at its right, hence 500 + 0 => 500
But where are the differences?
It depends on the operator used. Operators decide what's going to happen. For example:
my $foo = '128hello';
print $foo + 0;
output: 128
In this case it behaves like atoi() in C. It takes biggest numeric part starting from the left and uses it as a number. If there are no numbers it uses it as a 0.
How to deal with this in conditionals?
my $foo = '0900';
my $bar = 900;
if( $foo == $bar)
{print "ok!"}
else
{print "not ok!"}
output: ok!
== compares the numerical value in both variables.
if you use warnings it will complain about using == with strings but it will still try to coerce.
my $foo = '0900';
my $bar = 900;
if( $foo eq $bar)
{print "ok!"}
else
{print "not ok!"}
output: not ok!
eq compares strings for equality.
You can try "^" operator.
my $str = '500';
my $num = 500;
if ($num ^ $num)
{
print 'haha\n';
}
if ($str ^ $str)
{
print 'hehe\n';
}
$str ^ $str is different from $num ^ $num so you will get "hehe".
ps, "^" will change the arguments, so you should do
my $temp = $str;
if ($temp ^ $temp )
{
print 'hehe\n';
}
.
I usually use this operator to tell the difference between num and str in perl.

How to convert hex to string of hex

I have a problem understanding and using the 'vec' keyword.
I am reading a logpacket in which values are stored in little endian hexadecimal. In my code, I have to unpack the different bytes into scalars using the unpack keyword.
Here's an example of my problem:
my #hexData1 = qw(50 65);
my $data = pack ('C*', #hexData1);
my $x = unpack("H4",$data); # At which point the hexadecimal number became a number
print $x."\n";
#my $foo = sprintf("%x", $foo);
print "$_-> " . vec("\x65\x50", $_, 1) . ", " for (0..15); # This works.
print "\n";
But I want to use the above statement in the way below. I don't want to send a string of hexadecimal in quotes. I want to use the scalar array of hex $x. But it won't work. How do I convert my $x to a hexadecimal string. This is my requirement.
print "$_-> " . vec($x, $_, 1).", " for (0..15); # This doesn't work.
print "\n";
My final objective is to read the third bit from the right of the two byte hexadecimal number.
How do I use the 'vec' command for that?
You are making the mistake of unpacking $data into $x before using it in a call to vec. vec expects a string, so if you supply a number it will be converted to a string before being used. Here's your code
my #hexData1 = qw(50 65);
my $data= pack ('C*', #hexData1);
The C pack format uses each value in the source list as a character code. It is the same as calling chr on each value and concatenating them. Unfortunately your values look like decimal, so you are getting chr(50).chr(65) or "2A". Since your values are little-endian, what you want is chr(0x65).chr(0x50) or "\x65\x50", so you must write
my $data= pack ('(H2)*', reverse #hexData1);
which reverses the list of data (to account for it being little-endian) and packs it as if it was a list of two-digit hex strings (which, fortunately, it is).
Now you have done enough. As I say, vec expects a string so you can write
print join ' ', map vec($data, $_, 1), 0 .. 15;
print "\n";
and it will show you the bits you expect. To extract the the 3rd bit from the right (assuming you mean bit 13, where the last bit is bit 15) you want
print vec $data, 13, 1;
First, get the number the bytes represent.
If you start with "\x50\x65",
my $num = unpack('v', "\x50\x65");
If you start with "5065",
my $num = unpack('v', pack('H*', "5065"));
If you start with "50","65",
my $num = unpack('v', pack('H*', join('', "50","65"));
Then, extract the bit you want.
If you want bit 10,
my $bit = ($num >> 10) & 1;
If you want bit 2,
my $bit = ($num >> 2) & 1;
(I'm listing a few possibilities because it's not clear to me what you want.)

Perl pack/unpack/shift

I've been having this problem in Perl for a few days now, and after scouring countless man pages, perldocs and googling too many search terms, hopefully someone here can help me out.
I am given two strings which represent hex values, i.e. "FFFF", not the Perl hex number 0xFFFF. Given two of these strings, I wish to convert them to binary form, perform a bitwise AND of the two, then take the output of this and examine each bit from LSB to MSB.
I have two problems right now; converting the hex string into a hex number, and shifting
the result of the bitwise AND.
For converting the hex string into a hex number, I've tried the following approaches which don't seem to work when I print them out to examine:
$a = unpack("H*", pack("N*", $a));
$a = sprintf("%H", $a);
Using a 'print' to examine each of these does not show a correct value, nor does using 'sprintf' either...
The second problem I have occurs after I perform a bitwise AND, and I want to examine each bit by shifting right by 1. To avoid the previous problem, I used actual Perl hex numbers instead of hex strings (0xffff instead of "ffff"). If I try to perform a shift right as follows:
#Convert from hex number to binary number
$a = sprintf("%B", $a);
$b = sprintf("%B", $b);
$temp = pack("B*", $a) & pack("B*", $b);
$output = unpack("B*", $temp);
At this point everything looks fine, and using a 'print' I can see that the values of the AND operation look right, but when I try to shift as follows:
$output = pack("B*", $output);
$output = $output >> 1;
$output = unpack("B*", $output);
The resulting value I get is in binary form but not correct.
What is the correct way of performing this kind of operation?
There's no such thing as a "hex number". A number is a number, a hexadecimal representation of a number is just that - a representation.
Just turn it into a number and use bitwise and.
my $num = (hex $a) & (hex $b);
print ($num & 1, "\n") while ($num >>= 1)