high and low bytes of a decimal

S

Susanne West

i need to split/switch high and low bytes from a decimal,
my head is spinning and i'm really not sure how to
'properly' do it, because every search to this topic
yields another 10 different methods. so maybe once and
for all someone could help:


foreach $decimal (0..65025){
$lowbyte = ???;
$highbyte = $decimal >> 8; #fails for $decimal>65000?

#ex. to specify order:
$int16_lowhigh = $lowbyte . $highbyte;
$int16_highlow = $highbyte . $lowbyte;

}

.... i bet there's a more efficient way with pack or sprintf,
but i'm not sure how this all behaves for the different
ranges (0-255, 255-65025, 65025-...)

the reason for all this is a bytestream specification
that i need to meet that switches byteorders for different
values...

thanks!!!
 
M

Mirco Wahab

Susanne said:
.... i bet there's a more efficient way with pack or sprintf,
but i'm not sure how this all behaves for the different
ranges (0-255, 255-65025, 65025-...)

I don't know if it't efficient for
your purpose, but you could play
w/pack and unpack and keep whats
necessary:

One (somehow extended) example:

...
foreach my $decimal (0..65025){
my ($lowbyte, $highbyte) = unpack "B8 B8", pack "J", $decimal;
print "$lowbyte, $highbyte => ";

my $int16_lowhigh = unpack "V", pack "B32", $lowbyte.$highbyte;
my $int16_highlow = unpack "V", pack "B32", $highbyte.$lowbyte;
print "$int16_highlow, $int16_lowhigh\n"
}
...

(http://perldoc.perl.org/functions/pack.html)

Regards

M.
 
J

jl_post

i need to split/switch high and low bytes from a decimal,
my head is spinning and i'm really not sure how to
'properly' do it, because every search to this topic
yields another 10 different methods. so maybe once and
for all someone could help:

...

the reason for all this is a bytestream specification
that i need to meet that switches byteorders for different
values...


Dear Susanne,

If you're trying to fit integers into a bytestream I'm guessing
that you're trying to work through the issue of little-endian vs. big-
endian byte ordering and sending the integer to the stream one byte at
a time.

If that's the case, you need to encode your integers into a string
of bytes, and then send that string into the bytestream. But you have
to decide first if you want to encode that integer in little- or big-
endian order.

Assuming you have an integer to send, such as:

my $integerToSend = 2008;

you can encode it in big-endian order like this:

my $stringToSend = pack('N', $integerToSend);

or you can encode it in little-endian order like this:

my $stringToSend = pack('V', $integerToSend);

Chances are you're going to want to use big-endian order, as that's
what's favored by many networks. But in the end it really depends on
the specifications for the bytestream you're writing to. If the
bytestream expects the high-byte to be first, use big-endian
encoding. But if it expects the low-byte first, use little-endian.

(Note: These pack() calls assume you want to encode the numbers as 32-
bit (4-byte) integers. If you only want 16-bit (2-byte) integers,
change 'N' and 'V' to 'n' and 'v', respectively. You can also read
"perldoc -f pack" for more details.)

Once you have the string to send, you can just print() it to your
file or socket handle, like this:

print FILE_OR_SOCKET_HANDLE $stringToSend;

Also, be aware that you probably want your file or socket handle to
be writing out in binary mode, so be sure to call binmode() on it,
like this:

binmode(FILE_OR_SOCKET_HANDLE);

before printing to it. Otherwise integers with bytes holding a value
of 10 (such as 10, 266, and 522) might get extra bytes sent along with
them.

I hope this helps, Susanne.

-- Jean-Luc
 
J

jl_post

Assuming you have an integer to send, such as:

my $integerToSend = 2008;

you can encode it in big-endian order like this:

my $stringToSend = pack('N', $integerToSend);

or you can encode it in little-endian order like this:

my $stringToSend = pack('V', $integerToSend);


In case you want to see (or extract) the values of the bytes in the
$stringToSend, you can do so easily with this:

print ord, "\n" foreach split //, $stringToSend;

This will print out the value of each byte.

For example, if you were to write:

my $integerToSend = 2008;
my $stringToSend = pack('N', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

you would see the following output:

0
0
7
216

(You get 2008 from that by adding 0*256^3 + 0*256^2 + 7*256 + 216.)

But if you were to write it this way, with little-endian ordering:

my $integerToSend = 2008;
my $stringToSend = pack('V', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

you would see the following output:

216
7
0
0

Because you encoded the integer in little-endian order, the bytes are
reversed (with the low-byte shown first).

I hope this helps, Susanne.

-- Jean-Luc
 
S

Susanne West

hi jean-luc


thanks very much for clarifying! yes, you are more
or less right. but i do need all the variants:
- encoded little endian (goes into bytestream)
- encoded big endian (goes into bytestream)
- only low byte (for other reasons)
- only high byte (for other reasons)


problem 1: bytestream
the point with pack() is that i have a hard time to
understand size and system specifics: i need to pack
an int16 so i would indeed do
$integerToSend = 2008;
my $stringToSend = pack('n', $integerToSend);
but what happens if for unexpected reasons:
$integerToSend = 200000;
my $stringToSend = pack('n', $integerToSend);
is it correct, that this is truncated to 65025?


problem 2: only high and low bytes (similar story)
what is the best (fastest, safest) way to extract
the lowest two bytes of an decimal of unknown length?
i'm currently using
my $integerToSend = 2008;
my $lowbyte = $decimal % 256;
my $highbyte = $decimal >> 8;
but i doubt that this is the 'proper' way to do it.
especially when (again) for uexpected reasons:
my $integerToSend = 200000;
my $lowbyte = $decimal % 256;
my $highbyte = $decimal >> 8;


thanks for your comments. you've almost put me back
on track...

susanne.
 
S

Susanne West

This will print out the value of each byte.

For example, if you were to write:

my $integerToSend = 2008;
my $stringToSend = pack('N', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

you would see the following output:

0
0
7
216

(You get 2008 from that by adding 0*256^3 + 0*256^2 + 7*256 + 216.)

But if you were to write it this way, with little-endian ordering:

my $integerToSend = 2008;
my $stringToSend = pack('V', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

you would see the following output:

216
7
0
0

wow... that's a very helpful example! thanks!
 
S

Susanne West

for the record and those reading along:

thanks to jean-luc's example it's easy to reproduce the
problem with oversizes:

my $integerToSend = 200000;
my $stringToSend = '';

print "\n2 bytes big endian:\n";
$stringToSend = pack('n', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

print "\n2 bytes little endian:\n";
$stringToSend = pack('v', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

will yield to output:

----
2 bytes big endian:
13
64

2 bytes little endian:
64
13
 
J

jl_post

thanks very much for clarifying! yes, you are more
or less right. but i do need all the variants:
- encoded little endian (goes into bytestream)
- encoded big endian (goes into bytestream)
- only low byte (for other reasons)
- only high byte (for other reasons)


Now there's one thing you have to be aware of with bytes, or else
you'll mix things up: An 8-bit byte is basically just one
"character." Bytes have numerical "values," however, which are often
expressed in hexadecimal or decimal representation. What's important
to know is that these numerical "values" may represent the byte (or
"character"), but they are NOT the same.

Let me clarify with an example. Let's say you had a byte, like
this:

my $byte = 'A';

The $byte itself equals the character 'A', so this would be true:

if ($byte eq 'A') # evaluates to true (note the use of "eq")

and this would print out 'A':

print $byte; # prints out 'A'

but its "value" is actually 65 (or 0x41 in hexadecimal
representation). You can see this with the following code:

my $byteValue = ord($byte);
print $byteValue; # prints out "65"

As you might already know, the ord() function takes a single byte
and returns its numeric value. (The opposite of ord() is chr(), and
you can read about both in "perldoc -f ord" and "perldoc -f chr".)

So I have a question for you: When you say you need the high/low
byte, does that mean you need the literal byte "character," or the
byte's numerical "value"?

If you only need the numerical byte values, this is easy to do, as
it just involves doing mathematical operations on the $integerToSend,
like this:

my $integerToSend = 2008;
my $lowByteValue = integerToSend % 256; # 216
my $nextByteValue = (integerToSend >> 8) % 256; # 7

But if instead of the numerical byte value, you need the literal
byte character, you would just extract out the characters from the
$stringToSend, like this:

my $integerToSend = 2008;
my $stringToSend = pack('v', $integerToSend); # note little-
endian
my $lowByte = substr($stringToSend, 0, 1);
my $nextByte = substr($stringToSend, 1, 1);

(To learn more about the substr() function, read "perldoc -f substr".)

Note that if you print() out the $lowByte and $nextByte variables
you won't necessarily see numbers; what you'll see are characters that
correspond to values 7 and 216.

Also note that I've avoided using the term $highByte (even though I
used the term $lowByte). The reason for this is because if you try to
pack() a large value (like 200,000) into a two-byte string, the high-
bytes will be lost (meaning that the byte next to the low byte isn't
really the high byte, since the high byte(s) weren't recorded). To
avoid the confusion, I use $nextByte instead.

but what happens if for unexpected reasons:
$integerToSend = 200000;
my $stringToSend = pack('n', $integerToSend);
is it correct, that this is truncated to 65025?

I'm curious: Why do you say "65025"? Is it because you mean the
largest unsigned two-byte (16-bit) integer? If so, that's actually
256^2-1, which is actually 65535.

At any rate, no, it doesn't truncate it to either 65025 nor 65535.
What it does is convert the value 200000 to bytes whose values are 0,
3, 13, and 64, but since it can only keep two of them, it discards the
first two bytes and only keeps the lower two bytes (the ones whose
values are 13 and 64). And so 13*256 + 64 equals 3392.

So if you modified the code you gave a little while ago to this
code and ran it:

my $integerToSend = 200000;
my $stringToSend = '';

print "\n4 byte-values (big-endian):\n";
$stringToSend = pack('N', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

print "\n2 byte-values (also big-endian):\n";
$stringToSend = pack('n', $integerToSend);
print ord, "\n" foreach split //, $stringToSend;

you would see this output:

4 byte-values (big-endian):
0
3
13
64

2 byte-values (also big-endian):
13
64

(Note that the first two values are discarded when displaying only two
byte-values.)

problem 2: only high and low bytes (similar story)
what is the best (fastest, safest) way to extract
the lowest two bytes of an decimal of unknown length?
i'm currently using
my $integerToSend = 2008;
my $lowbyte = $decimal % 256;
my $highbyte = $decimal >> 8;
but i doubt that this is the 'proper' way to do it.
especially when (again) for uexpected reasons:
my $integerToSend = 200000;
my $lowbyte = $decimal % 256;
my $highbyte = $decimal >> 8;

If you're looking for byte "values," you can use what I did above:

my $integerToSend = 20000;
my $lowByteValue = integerToSend % 256; # 32
my $nextByteValue = (integerToSend >> 8) % 256; # 78

If you're looking for the literal byte "characters," you can
convert the values to byte-characters with the chr() function, like
this:

my $lowByte = chr($lowByteValue);
my $nextByte = chr($nextByteValue);

or you can pack() the $integerToSend to a $stringToSend with little-
endian ordering, and just extract out the first and second characters,
like this:

my $stringToSend = pack('v', $integerToSend); # note: little-
endian
my $lowByte = substr($stringToSend, 0, 1); # gets first byte
my $nextByte = substr($stringToSend, 1, 1); # gets next byte

thanks for your comments. you've almost put me back
on track...

You're very welcome.

Don't forget the distinction between the byte values and the bytes
themselves. Otherwise, if you want to send the bytes for the number
2008 over a bytestream, but send the strings "7" and "216" over
instead, you'll be sending the string "7216" (a four-byte string)
instead of $stringToSend (a two-byte string).

I hope this helps, Susanne.

-- Jean-Luc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top