Lazarus

Programming => General => Topic started by: Muso on April 15, 2021, 05:16:22 pm

Title: [solved] How to create a signed integer from bytes
Post by: Muso on April 15, 2021, 05:16:22 pm
I am stuck with a simple task: create a signed 16 bit integer from two bytes. I have this code since I think one must simply combine the two bytes:

Code: Pascal  [Select][+][-]
  1. var
  2.  byte1, byte2 : Byte;
  3.  tempInt16 : Int16;
  4.  
  5. procedure test;
  6. begin
  7.  byte1:= 200;
  8.  byte2:= 255;
  9.  tempInt16:= (byte1 shl 8) + byte2;
  10. end;

But this code raises a range exception. Why?
When the hi-byte is larger than 127 the exception is raised but since Int16 is a signed integer, I don't understand this.

How is the signed integer creation done correctly? (I googled already around without success.)
Title: Re: How to create a signed integer from bytes
Post by: alpine on April 15, 2021, 05:37:09 pm
Perhaps it is too big to fit into -32768..32767 range (it is unsigned). Use Int16() typecast.
Title: Re: How to create a signed integer from bytes
Post by: Muso on April 15, 2021, 05:38:50 pm
Hmm, I tried now a simple cast and this worked:

   
Code: Pascal  [Select][+][-]
  1. var
  2.      byte1, byte2 : Byte;
  3.      tempInt16 : Int16;
  4.      HiLowArray : array[0..1] of Byte;
  5.      
  6.     procedure test;
  7.     begin
  8.      byte1:= 200;
  9.      byte2:= 255;
  10.      HiLowArray[0]:= byte2;
  11.      HiLowArray[1]:= byte1;
  12.      tempInt16:= Int16(HiLowArray);
  13.     end;

I would still like to understand why my initial attempt failed.
Title: Re: How to create a signed integer from bytes
Post by: alpine on April 15, 2021, 05:42:48 pm
200*256+255=51455, which is greater than 32767  :)

Code: [Select]
tempInt16 := Int16((byte1 shl 8) + byte2);
Title: Re: How to create a signed integer from bytes
Post by: howardpc on April 15, 2021, 05:49:32 pm
Mixing signed (Int16) and unsigned (Byte) generally leads to trouble.
Try this:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. var
  6.  byte1 : Byte;
  7.  tempInt16 : Int16;
  8.  
  9. procedure test;
  10. begin
  11.  byte1:= 127;
  12.  tempInt16 := (byte1 shl 8);
  13.  WriteLn('127 in binary is ',BinStr(127, 16));
  14.  WriteLn('128 in binary is ',BInStr(128, 16));
  15.  
  16.  WriteLn('127 shifted by 8 is ',BinStr(tempInt16, 16));
  17.  WriteLn('128 shifted by 8 is ',BinStr(128 shl 8, 16));
  18.  WriteLn('High(Int16) as binary is ',BinStr(High(Int16), 16));
  19. end;
  20.  
  21. begin
  22.   test;
  23. end.
Title: Re: How to create a signed integer from bytes
Post by: Muso on April 15, 2021, 06:12:05 pm
200*256+255=51455, which is greater than 32767  :)

But this is what I don't understand. 200 > 127, so the sign bit is 1. Thus, as the two bytes represent a signed int, I should get a negative value: -14081

But my clarification that the two bytes represent a signed, not an unsigned int is ignored.  :-\
Title: Re: How to create a signed integer from bytes
Post by: alpine on April 15, 2021, 06:25:43 pm
200*256+255=51455, which is greater than 32767  :)

But this is what I don't understand. 200 > 127, so the sign bit is 1. Thus, as the two bytes represent a signed int, I should get a negative value: -14081

But my clarification that the two bytes represent a signed, not an unsigned int is ignored.  :-\

You must put that clarification to the compiler, not to me ;)

So, byte1 and byte2 are of type byte which is unsigned.
Expression  (byte1 shl 8 ) + byte2 which is equal to 51455, will be widened to some unsigned type, I don't know exactly which, may be Word with range 0..65535 instead of 32768..32767 and thus the assignment of 51455 to Int16 is incorrect since it cannot hold such a big value. That's it.
Title: Re: How to create a signed integer from bytes
Post by: howardpc on April 15, 2021, 06:29:18 pm
The compiler simply does what you tell it do.
You tell it to shift the value of byte1 by 8.

When byte1 is greater than 127 (decimal) the expression
Code: Pascal  [Select][+][-]
  1. (byte1 shl 8)
is too large to fit in a signed 16 bit integer, and so the compiler warns you of that fact if range checks are enabled.
It is as simple as that.
Title: Re: How to create a signed integer from bytes
Post by: alpine on April 15, 2021, 06:36:26 pm
@howardpc
Do you know where the FPC widening rules for expressions are described, since I can't find them in https://freepascal.org/docs-html/ref/ref.html. May be I'm not looking at the right place.

Thanks,
Title: Re: How to create a signed integer from bytes
Post by: NickyTi on April 15, 2021, 06:58:20 pm
Try this

type
  Tbytes2int16 = packed record
    case integer of
    0: (byte2, byte1 : Byte);
    1: (tempInt16 : Int16);
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    bytes2int16: Tbytes2int16;
  public

  end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  bytes2int16.byte1:=200;
  bytes2int16.byte2:=255;
  ShowMessage(IntToStr(bytes2int16.tempInt16));
  //or
  ShowMessage(bytes2int16.tempInt16.ToString);
end;
Title: Re: How to create a signed integer from bytes
Post by: MarkMLl on April 15, 2021, 07:13:02 pm
type
  Tbytes2int16 = packed record
    case integer of
    0: (byte2, byte1 : Byte);
    1: (tempInt16 : Int16);
  end;

That is the classic way of doing it, and avoids much grief particularly when when starts messing about with 64-bit words.

But beware of endianness issues.

MarkMLl
Title: Re: How to create a signed integer from bytes
Post by: NickyTi on April 15, 2021, 07:19:27 pm
MarkMLl,
I agree with you.
It would be more correct to write;

  TLoHiWord = packed record
    case integer of
    0: (Lower, Lo, Hi, Higher: byte);
    1: (WordLo, WordHi: word);
    2: (aInt: Integer);
    3: (Str: array[0..3] of char);
  end;
Title: Re: How to create a signed integer from bytes
Post by: alpine on April 15, 2021, 07:23:49 pm
But beware of endianness issues.

IMHO,
Code: Pascal  [Select][+][-]
  1. tempInt16 := Int16((byte1 shl 8) + byte2)

has no chance to get the endianness wrong.
Title: Re: How to create a signed integer from bytes
Post by: Muso on April 15, 2021, 07:43:18 pm
When byte1 is greater than 127 (decimal) the expression
Code: Pascal  [Select][+][-]
  1. (byte1 shl 8)
is too large to fit in a signed 16 bit integer, and so the compiler warns you of that fact if range checks are enabled.
It is as simple as that.

Well, that was not clear to me and its not documented as such. I have 16 bits and Int16 has 16 bit, so it should fit in.

However, I learned now that one has take care in a different was about the sign bit. Many thanks for all your help.
Title: Re: [solved] How to create a signed integer from bytes
Post by: NickyTi on April 15, 2021, 08:10:34 pm
Muso,
try reverse

type
  Tbytes2int16 = packed record
    case integer of
    0: (byte2, byte1 : Byte);
    1: (tempInt16 : Int16);
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    bytes2int16: Tbytes2int16;
  public

  end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  bytes2int16.tempInt16:=-14081;
  ShowMessage('byte1 = '+IntToStr(bytes2int16.byte1));
  ShowMessage('byte2 = '+IntToStr(bytes2int16.byte2));
end;
Title: Re: How to create a signed integer from bytes
Post by: MarkMLl on April 15, 2021, 10:46:22 pm
has no chance to get the endianness wrong.

...but it depends on whether your intention is to combine bytes and words (which do not necessarily represent specific numbers) or to combine numbers using arithmetic/logical operations. The distinction is obviously subtle, but is very much relevant when one is tinkering with e.g. CPU simulation.

MarkMLl
Title: Re: [solved] How to create a signed integer from bytes
Post by: jamie on April 15, 2021, 10:52:56 pm
Code: Pascal  [Select][+][-]
  1. MysmallInt := Int16(WORD((UpperByte shl 8)+LowByte));
  2.  

Seems I remember the logical shift operators don't work like you would think with integers.
Title: Re: How to create a signed integer from bytes
Post by: alpine on April 15, 2021, 11:12:28 pm
has no chance to get the endianness wrong.

...but it depends on whether your intention is to combine bytes and words (which do not necessarily represent specific numbers) or to combine numbers using arithmetic/logical operations. The distinction is obviously subtle, but is very much relevant when one is tinkering with e.g. CPU simulation.

MarkMLl

@MarkMLI, I honestly confess, I didn't get you. The Muso's intent was to combine two bytes in a signed word (Int16). What do you mean by making the 'subtle' distinction between combining bytes/words and combining numbers? Can you please explain.

Title: Re: How to create a signed integer from bytes
Post by: MarkMLl on April 15, 2021, 11:55:14 pm
@MarkMLI, I honestly confess, I didn't get you. The Muso's intent was to combine two bytes in a signed word (Int16). What do you mean by making the 'subtle' distinction between combining bytes/words and combining numbers? Can you please explain.

What is a byte? The industry has eventually agreed that's it's an eight-bit "octet", and that a word is sixteen bits, but those aren't necessarily numbers.

If you want to concatenate two bytes into a word, with absolutely no interpretation, then you use a variant record and are careful about endianness.

If you want to assemble 16 bits in memory from two 8-bit quantities then you use a shift and an "or" operation.

If you want to merge two numbers of a certain "bitedness" into one with a larger "bitedness", then you use multiplication and addition. But you'd better be damned sure of the representation convention, and be prepared to take sign into consideration.

Now, if we go back to @Muso's OP. "Two bytes"... but are those signed or unsigned? A byte isn't (necesarliy) a number. "Into a signed word"... but that's unambiguously a number.

So it really boils down to what one knows about the source values, and what one requires from the result.

MarkMLl
Title: Re: How to create a signed integer from bytes
Post by: alpine on April 16, 2021, 01:20:22 am
MarkMLl,

Now I get it. Thank you for your explanation for something that, I believe, is quite obvious for both of us.
I don't think the Muso's question goes so abstract and deep, and I would recommend him to find some good book for computer data representation, binary systems, processor architectures, etc.

Regarding your remarks, please see my notes below.

What is a byte? The industry has eventually agreed that's it's an eight-bit "octet", and that a word is sixteen bits, but those aren't necessarily numbers.
Actually, they are whole numbers in a specific range. At a certain level of abstraction, you can consider them as a domain of bijection to another (finite) set, e.g. characters, enums, floats, etc.

If you want to concatenate two bytes into a word, with absolutely no interpretation, then you use a variant record and are careful about endianness.
Concatenation means there is interpretation. That is why the endianness is important.

If you want to assemble 16 bits in memory from two 8-bit quantities then you use a shift and an "or" operation.
You are utmost correct. But since we know well that the addition (the adder) is an "or" combined with a "xor" for the carry-bit to the next cell, and also, the SHL operation (same instruction) fills with zeroes on the right, we're quite sure that "+" works the same way as the "or" and there will be no carry-bit from the low byte.

If you want to merge two numbers of a certain "bitedness" into one with a larger "bitedness", then you use multiplication and addition. But you'd better be damned sure of the representation convention, and be prepared to take sign into consideration.
Quite correct again. But we also know that SHL/SHR are ignoring the signednness of the item, which is not relevant in the case when we're working with unsigned variables, and then they behaves just like multiplication/division by the power of two. This is our particular case (variables of type 'byte').

Now, if we go back to @Muso's OP. "Two bytes"... but are those signed or unsigned? A byte isn't (necesarliy) a number. "Into a signed word"... but that's unambiguously a number.

So it really boils down to what one knows about the source values, and what one requires from the result.
IMO, The Muso wants to transfer some signed words through a communication link, but I can't be absolutely sure from his writings.

And yes, the one must knows what heis doing, especially when trying to combine some low level artifacts, plays with shifts, bytes and so.

Regards,
Title: Re: [solved] How to create a signed integer from bytes
Post by: speter on April 16, 2021, 02:31:05 am
Muso,

To achieve what (I tihnk) you are after, use (something like):
Code: Pascal  [Select][+][-]
  1. var
  2.   b1 : byte = 200;
  3.   b2 : byte = 255;
  4.   w : word;
  5.   i : int16;
  6. begin
  7.   w := b1 shl 8 + b2;
  8.  
  9.   if w > maxsmallint then
  10.     i := -int16(w and maxsmallint)
  11.   else
  12.     i := int16(w);
  13.  
  14.   memo1.append('w='+w.tostring+'; i='+i.tostring);
  15. end;

The above gives me:
Code: [Select]
w=51455; i=-18687
Note that "maxsmallint" may be OS (etc) dependant; and of course my example assumes there is a memo. :)

cheers
S.
Title: Re: [solved] How to create a signed integer from bytes
Post by: egsuh on April 16, 2021, 04:32:18 am
This is tagged as "Solved" but I found this subject is interesting.  Following code shows -1, which I thing is corrent. Right?

procedure TForm1.Button1Click(Sender: TObject);
var
   b1, b2: byte;
   i: int16;
begin
   b1:= $FF;
   b2:= $FF;

   i:= b1 * $100 + b2;
   showmessage(IntToStr(i));
end;
TinyPortal © 2005-2018