Recent

Author Topic: [solved] How to create a signed integer from bytes  (Read 2566 times)

Muso

  • Sr. Member
  • ****
  • Posts: 356
[solved] How to create a signed integer from bytes
« 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.)
« Last Edit: April 15, 2021, 07:43:31 pm by Muso »

alpine

  • Hero Member
  • *****
  • Posts: 1062
Re: How to create a signed integer from bytes
« Reply #1 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.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to create a signed integer from bytes
« Reply #2 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.

alpine

  • Hero Member
  • *****
  • Posts: 1062
Re: How to create a signed integer from bytes
« Reply #3 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);
« Last Edit: April 15, 2021, 05:44:33 pm by y.ivanov »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: How to create a signed integer from bytes
« Reply #4 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.

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to create a signed integer from bytes
« Reply #5 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.  :-\
« Last Edit: April 15, 2021, 06:13:36 pm by Muso »

alpine

  • Hero Member
  • *****
  • Posts: 1062
Re: How to create a signed integer from bytes
« Reply #6 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.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: How to create a signed integer from bytes
« Reply #7 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.

alpine

  • Hero Member
  • *****
  • Posts: 1062
Re: How to create a signed integer from bytes
« Reply #8 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,
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

NickyTi

  • Newbie
  • Posts: 5
Re: How to create a signed integer from bytes
« Reply #9 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;

MarkMLl

  • Hero Member
  • *****
  • Posts: 6683
Re: How to create a signed integer from bytes
« Reply #10 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
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

NickyTi

  • Newbie
  • Posts: 5
Re: How to create a signed integer from bytes
« Reply #11 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;

alpine

  • Hero Member
  • *****
  • Posts: 1062
Re: How to create a signed integer from bytes
« Reply #12 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.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to create a signed integer from bytes
« Reply #13 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.

NickyTi

  • Newbie
  • Posts: 5
Re: [solved] How to create a signed integer from bytes
« Reply #14 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;

 

TinyPortal © 2005-2018