Recent

Author Topic: FPC dealing with overflow  (Read 2281 times)

K.IO.s

  • New Member
  • *
  • Posts: 11
FPC dealing with overflow
« on: July 07, 2022, 09:15:35 pm »
Hi all, I'm testing how the compiler handles overflow scenarios i came across an interesting situation.

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$Q+}{$R+}
  2.  
  3. program MyProgram;
  4.  
  5. var
  6.   X: uint8;
  7.   s: int8;
  8. begin
  9.   s := -3;
  10.   X := 15 div s;
  11.   WriteLn('X is now: ', X);
  12. end.
Doing it this way compiles fine! But with the proper compiler flags, at runtime it gives me:
Code: Pascal  [Select][+][-]
  1. Runtime error 201 at $0000000000400235
  2.   $0000000000400235
  3.   $00000000004001BC
  %)

If i switch s for "-3" proper it gives me the expected compilation error:
Code: Pascal  [Select][+][-]
  1. main.pas(10,11) Error: range check error while evaluating constants (-5 must be between 0 and 255)
This is an ideal error, with line numbers and everything.

It's strange that just because I wrapped it around a variable the compiler ignores the overflow. Also, I thought Pascal would not allow this purely based on them being different types, given the strictness of the language. I guess all integers are of a same base type.

I tested wrapping the code around a try except block, and it did output the range error:
Code: Pascal  [Select][+][-]
  1. exception occurred: ERangeError, message: Range check error
I couldn't get it to output the line number where the error occurred, I'm sure there are ways to do it, I didn't find out how. But regardless, this would create a dependency on exceptions. Also, if i simply import sysutils it tells me the error type, but with the memory addresses still.

  • So, what's the best way to handle these types of scenarios?
  • And also, why the compiler ignores the overflow when it's wrapped in a variable?

On a side note, it's crazy how fast fpc compiler is.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6692
Re: FPC dealing with overflow
« Reply #1 on: July 07, 2022, 10:34:08 pm »
I've not worked through that in detail I'm afraid, but a couple of comments.

First, you might possibly do better using the fundamental types listed at https://www.freepascal.org/docs-html/current/ref/refsu4.html#x26-250003.1.1 since things like int8 are really there for compatibility with e.g. OS-level APIs and (I'll probably get in trouble for saying this) might have quirks.

Second, I believe constants default to int64 unless explicitly cast, irrespective of the type of other variables etc. in an expression.

Freebie: an intermediate value's type is determined by the operands, never by the variable to which it's being assigned.

On a side note, it's crazy how fast fpc compiler is.

Well, I remember when it used to take a week to compile and (in particular) link Lazarus on an ARM with 32Mb RAM. But I'd bet that no other comparable development environment would run on that, let alone build :-)

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

Thausand

  • Sr. Member
  • ****
  • Posts: 292
Re: FPC dealing with overflow
« Reply #2 on: July 07, 2022, 11:11:20 pm »

Doing it this way compiles fine! But with the proper compiler flags, at runtime it gives me:
Code: Pascal  [Select][+][-]
  1. Runtime error 201 at $0000000000400235
  2.   $0000000000400235
  3.   $00000000004001BC
Can get rangecheck error when use sysutils in myprogram
can get line number when compile fpc -gl myprogram.pas

K.IO.s

  • New Member
  • *
  • Posts: 11
Re: FPC dealing with overflow
« Reply #3 on: July 07, 2022, 11:40:58 pm »
can get line number when compile fpc -gl myprogram.pas

Indeed this works. I had tried -gl, but for some reason it wasn't working and now it did.

I'm still curious about the absence of the compilation warning. And as MarkMLl mentioned, how integer types are converted upon operations. C/C++ does all sorts of type conversions on most ops, and it's horrible. I was hoping Pascal would be more strict.

Either way, thanks guys, for the help.

Thausand

  • Sr. Member
  • ****
  • Posts: 292
Re: FPC dealing with overflow
« Reply #4 on: July 07, 2022, 11:57:32 pm »
Indeed this works. I had tried -gl, but for some reason it wasn't working and now it did.
Is better use fpc -B -gl name.pas

can use -glh and have stacktrace / memory leak

-B mean build submodule (in case have separate unit) to always build.

More information fpc options can read: https://www.freepascal.org/docs-html/user/userap1.html

Quote
I'm still curious about the absence of the compilation warning.
Compiler can alone do warning with const and not variable. I can not explain good because my bad English. But is write/explain in forum by developer. Compiler not know if invalid when compile time. Is logic for human to read and not so logic for compiler :)

Quote
And as MarkMLl mentioned, how integer types are converted upon operations. C/C++ does all sorts of type conversions on most ops, and it's horrible. I was hoping Pascal would be more strict.

Link MarkMLI very good. it write how pascal do convert:
Quote
As a pascal compiler, Free Pascal does automatic type conversion and upgrading in expressions where different kinds of integer types are used:

1. Every platform has a “native” integer size, depending on whether the platform is 8-bit, 16-bit, 32-bit or 64-bit. E. g. on AVR this is 8-bit.
2. Every integer smaller than the “native” size is promoted to a signed version of the “native” size. Integers equal to the “native” size keep their signedness.
3. The result of binary arithmetic operators (+, -, *, etc.) is determined in the following way:
a) If at least one of the operands is larger than the native integer size, the result is chosen to be the smallest type that encompasses the ranges of the types of both operands. This means that mixing an unsigned with a smaller or equal in size signed will produce a signed type that is larger than both of them.
b) If both operands have the same signedness, the result is the same type as them. The only exception is subtracting (-): in the case of unsigned - unsigned subtracting produces a signed result in FPC (as in Delphi, but not in TP7).
c) Mixing signed and unsigned operands of the “native” int size produces a larger signed result. This means that mixing longint and longword on 32-bit platforms will produce an int64. Similarly, mixing byte and shortint on 8-bit platforms (AVR) will produce a smallint.

K.IO.s

  • New Member
  • *
  • Posts: 11
Re: FPC dealing with overflow
« Reply #5 on: July 08, 2022, 01:39:51 am »
Thanks for the commands Thausand, and for pointing out the part in the reference.
For those who may come looking for similar answers, I also found this.
Your english is fine btw, don't worry about it. We all do what we can, and you guys in the Pascal community really are the golden standard.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6692
Re: FPC dealing with overflow
« Reply #6 on: July 08, 2022, 09:18:49 am »
C/C++ does all sorts of type conversions on most ops, and it's horrible. I was hoping Pascal would be more strict.

Frankly, I agree. In actual fact Wirth significantly tightened things up when he moved from Pascal to Modula-2, and that effectively predates C++.

This isn't something which is going to happen (and I don't have enough decades left to do it justice), but my preference would be for a strongly-typed lean'n'mean base language with traits indicating the conditions under which types became compatible (and how that was to be achieved, and whether the conversion was to be inherited).

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

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: FPC dealing with overflow
« Reply #7 on: July 08, 2022, 02:25:56 pm »
I was hoping Pascal would be more strict.
It is more strict, provided you define an expected range.
I wrote about it in the wiki: https://wiki.freepascal.org/Defensive_programming_techniques
I suggest you read that. It covers your problem.
One addition: The range can start at a negative value, so -3 will also compile in such case.
E.g.
Code: Pascal  [Select][+][-]
  1. program untitled;
  2. {$mode objfpc}{$R+}
  3. type
  4.   SiRange = low(shortint)..High(ShortInt); // range is -128 to 127 in this example
  5. var
  6.   MyArray:Array[SiRange] of shortint;
  7. begin
  8. end.
See also https://www.freepascal.org/docs-html/prog/progsu154.html but do not forget that within the bounds of any natively -CPU - supported integer type (signed or unsigned) you can define your own range to precision and range errors will be caught at compile time, not run time.

You need a modern Pascal for that, C++ would be impossible to handle it or at least way more cumbersome and less concise. C++ has no direct compiler support for this.
So what @MarkMLi wrote is simply not true.
« Last Edit: July 08, 2022, 06:04:16 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: FPC dealing with overflow
« Reply #8 on: July 08, 2022, 06:53:23 pm »

C, C++ and other languages cycle unsigned datatypes automatically without warnings or errors.

#include<stdio.h>
int main()
{
   unsigned char x;
   signed char s=-3;
   x=(15/s);
   printf("%d\n",x);   
}

gives 251

FreePascal can also cycle unsigned datatypes
Code: Pascal  [Select][+][-]
  1.  uses Math;
  2. var
  3.   X: uint8;
  4.   s: int8;
  5. begin
  6. SetExceptionMask([exOverflow]);
  7.   s := -3;
  8.   X := 15 div s;
  9.   WriteLn('X is now: ', X);
  10.   writeln('Press return to end . . .');
  11.   readln;
  12. end.
So it is always another option.

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: FPC dealing with overflow
« Reply #9 on: July 08, 2022, 09:09:25 pm »
Freepascal will also recycle without warning or error in{$R-} state but not in {$R+} state. {$R-} is the default.
I occasionally use that, e.g. for LFO's or basically any waveform code.

And, btw, you can also force C++ code to throw an error on overflow or range. It is just more cumbersome.

Also note {$R+/-} are local, not global, so you can use it on a per routine base. Even on code fragments.
(Do not confuse that with the command line option: that would be global, unless the above: it adheres to the local define when applied.)
So, @BobDog : be careful.... Overflowing and resetting relies on context of the settings....
« Last Edit: July 08, 2022, 09:25:01 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

K.IO.s

  • New Member
  • *
  • Posts: 11
Re: FPC dealing with overflow
« Reply #10 on: July 08, 2022, 10:59:08 pm »
Code: Pascal  [Select][+][-]
  1. program untitled;
  2. {$mode objfpc}{$R+}

I have a question about the this ^. In the document you linked you always add the $mode after program, on my example I placed it before, does it make a difference, isn't it global?
I also noticed you used {R+}  after the program. In the docs, it says that directives like R are set until the are unset or end of unit;
So if I wanted to restrict it to a certain region, i guess I'd have to do {R+} ... {R-}. Correct?

I also saw, in the docs, this example:
Code: Pascal  [Select][+][-]
  1. {$BITPACKING ON}  
  2. Type  
  3.   TMyRecord = packed record  
  4.     B1,B2,B3,B4 : Boolean;  
  5.   end;
I'm guessing the directive ends on "end;" But does it, shouldn't it follow the above requirements?
I think this is related hence the question.


Now back to my original post, your piece on avoiding range error and overflows is great, but it doesn't really solve my problem.
The real scenario for what my example was simulating, is a situation where you receive a variable, that you have no control over, and you expect it to be a positive value, but it's actually a negative value, you don't know it and it screws your system. There are lots of unexpected bugs like that in c/c++, and sure you can clearly catch it at runtime, and now I know a lot more about how to do it in pascal.

But my statement about strictness was about language design, and that in my view, ideally, operations between signed and unsigned should not be permitted at all. Actually, if you ask me, I think no auto conversions should happen ever, if you wanna add a uint8 to a uin16, in a uint32, that's you problem, you call the functions to convert them. Of course that's not how modern languages work, I guess for convenience. And if we analise the design that pascal takes(based on the docs listed above, about the base types), it's(the compiler) clearly trying to make sure that all operations will execute, as correctly as possible, to the limit of it's ability.

Thanks again for all the responses, and feel free to correct me on anything if you think I'm wrong.

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: FPC dealing with overflow
« Reply #11 on: July 09, 2022, 09:02:09 am »
1. The mode place can be both on top or after the program name, but nowhere else
2. The correct documentation is here: https://www.freepascal.org/docs-html/current/prog/progsu65.html#x72-710001.2.65
E.g.
Code: Pascal  [Select][+][-]
  1. program pupo;
  2. {$mode objfpc} // works in more modes
  3. {$push}{$R+}  // use Rangechecks local
  4. procedure testme;
  5. begin
  6. end;
  7. {$pop} // restore original mode, which is {$R-}
  8. begin end.

3. Use a bitpacked record, not packed on a reccord with only booleans. And use {$push/pop} like I demonstrated. It does not end with end;
4. It only is interpreted as a negative value if you declared your variable as a signed integer type. It will be interpreted as a positive value when you declare an unsigned integer type. The example I give above shows how to range check signed integers btw. In my example you can also byte. The value -3 then becomes 253, which is correct
5. It is not really a conversion, but the way the bit pattern is interpreted
« Last Edit: July 09, 2022, 09:27:24 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6692
Re: FPC dealing with overflow
« Reply #12 on: July 09, 2022, 11:02:29 am »
1. The mode place can be both on top or after the program name, but nowhere else

The Lazarus IDE makes the arbitrary choice that it goes after the program line... which doesn't in itself generate any code, so with the /possible/ exception of setting the default codepage the positioning isn't relevant.

Some of the other points Thaddy's made (correctly) argue against my assertion that the types of components in an expression didn't affect the result. When I wrote that I was more thinking of the case where the type of the result of an expression wasn't affected by the variable etc. to which it was being assigned.

But my statement about strictness was about language design, and that in my view, ideally, operations between signed and unsigned should not be permitted at all. Actually, if you ask me, I think no auto conversions should happen ever, if you wanna add a uint8 to a uin16, in a uint32, that's you problem, you call the functions to convert them. Of course that's not how modern languages work, I guess for convenience. And if we analise the design that pascal takes(based on the docs listed above, about the base types), it's(the compiler) clearly trying to make sure that all operations will execute, as correctly as possible, to the limit of it's ability.

I'm inclined to agree, but the sheer convenience of being able to say <signed> + <unsigned> (e.g. as part of a for-do block) takes some beating.

We have to look at history here, and at convention and reasonable expectation: languages such as APL, Smalltalk and Forth failed- at least in part- because they flew in the face of established algebraic notation. High-level languages including FORTRAN and ALGOL have been around for more than the length of any reasonable career, and telling everybody that one of their fundamental expectations is in fact /wrong/ will not go down well.

Hence my comment about a trait system, so that e.g. the variable declared as part of a for-do block can be of a type compatible with both signed and unsigned with errors caught by runtime checking.

And I KNOW that there are multiple things in that last sentence which will cause Sven to go up the wall.

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

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: FPC dealing with overflow
« Reply #13 on: July 09, 2022, 01:10:25 pm »
The last sentence is simply computer science based. I wonder if Sarah even to bothers to answer.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

K.IO.s

  • New Member
  • *
  • Posts: 11
Re: FPC dealing with overflow
« Reply #14 on: July 10, 2022, 12:08:52 am »
Thank you all for all the great responses, and for the patience. This really helped, great information.

 

TinyPortal © 2005-2018