Inconsistency between Windows and Linux compiling? lazarus / 23-Operating Systems ---------------------------------------------------------------------- Alex Kidd · 2011-06-11 16:13 I began experimenting with Lazarus, using Moraz Abdel Azeem's Start programming using Object Pascal. I stumbled upon this issue compiling this simple program (excerpt): Code: [Select]var   x,y:integer;   res:double; begin   x:=5;   y:=0;   try [color=red]    res:=x/y;[/color]   except on e:exception do       writeln('Exception:',e.message); [actually, I'm using a Memo]   end;   writeln('Res:',floattostr(res));  [actually, I'm using a Memo] end;                                   If I compile and run this code in Windows, a DivideByZero exception is raised, which is correct. On the other hand, if I compile and run this code in Linux, no exception is raised: the variable res is set to "+inf" value. Is there a particular compiler flag I have to set in Linux to obtain the correct behaviour? Please, let me know! ---------------------------------------------------------------------- eny · 2011-06-11 17:00 Quote from: Alex Kidd on June 11, 2011, 06:13:26 pmIf I compile and run this code in Windows, a DivideByZero exception is raised, which is correct.  What Lazarus and FPC version? When I do this the program crashes horribly. (BTW: dividing through zero is not the best of ideas...) ---------------------------------------------------------------------- Alex Kidd · 2011-06-12 06:08 Quote from: eny on June 11, 2011, 07:00:51 pmQuote from: Alex Kidd on June 11, 2011, 06:13:26 pmIf I compile and run this code in Windows, a DivideByZero exception is raised, which is correct.  What Lazarus and FPC version? When I do this the program crashes horribly. (BTW: dividing through zero is not the best of ideas...) Lazarus 0.9.30 and FPC 2.4.2. I agree "divide by zero" is not the best of ideas  , I was just trying the samples in the book and make a comparison between Linux Mint 10 and Windows XP SP3 to verify the behaviour is exactly the same. Apparently they differ: WinXP raises the exception, while Linux says the result is +Infinite. This means in this particular case I cannot write the code once and compile everywhere. I think this is normal when coding using DLLs or OS specific features, but not when using simple math functions. ---------------------------------------------------------------------- Zoran · 2011-06-12 07:08 Alex, what output do you get from this program if exception doesn't get raised? I have just tried and compiled this example on Linux (Ubuntu 11.04). The exception gets raised as expected. When I enter zero for y, I receive the message "An error occurred: Division by zero", which means that the execution fell into "except" branch of try-except block. Further, if I change the writeln to Code: [Select]Writeln('An error occurred: ', e.ClassName, ' - ', e.message); (that is, add e.ClassName to output), I can see that EDivByZero got raised (I get: "An error occurred: EDivByZero - Division by zero"). ---------------------------------------------------------------------- Blaazen · 2011-06-12 07:44 Here I also get: Code: [Select]Res:+Inf I think it is correct because "/" is used and "res" is double. If I replace "/" with "div" project gives SIGFPE - invalid floating point operation. It is also correct because result of "div" cannot be +Inf and therefore there is no result which can be assigned to "Res". ---------------------------------------------------------------------- Alex Kidd · 2011-06-12 08:14 Blaazen, which Linux distro are you using? It seems you and Zoran are experimenting different outputs. I tried also Mint 11, and still I get the +inf behaviour (with no exception). I get the SIGFPE error too when using DIV instead of "/" (the exception is EInvalidOp, Invalid floating point operation).  Zoran, what disto / fpc version are you using by the way? I think something strange is going on here... ---------------------------------------------------------------------- Alex Kidd · 2011-06-12 08:36 Well, maybe the title I used in the topic is incorrect. I think I found out the difference is not between the Linux and WinXP version, but between "Application" and "Program". The application shows the +inf behaviour, while the program behaves like Zoran's test. Here are the two codes: APPLICATION Code: [Select]unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls; type   { TForm1 }   TForm1 = class(TForm)     Memo1: TMemo;     procedure FormCreate(Sender: TObject);   private     { private declarations }   public     { public declarations }   end; var   Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); var   x,y:integer;   res:double; begin   x:=5;   y:=0;   try     res:=x / y;   except on e:exception do     memo1.append(e.message);   end;   memo1.append(floattostr(res)); end; end.             PROGRAM Code: [Select]program Project1; {$mode objfpc}{$H+} uses   {$IFDEF UNIX}{$IFDEF UseCThreads}   cthreads,   {$ENDIF}{$ENDIF}   Classes,sysutils   { you can add units after this }; {$R *.res} var   x,y:integer;   res:double; begin   x:=5;   y:=0;   try     res:=x / y;   except on e:exception do     writeln(e.message);   end;   writeln(floattostr(res)); end.       Later on I'm going to reboot to winxp and test if my theory is confirmed (i.e. I was really wrong suspecting Lin and Win compile differently...  ). ---------------------------------------------------------------------- Alex Kidd · 2011-06-12 09:04 Back on line. I tested the two codes in WinXP and I always got the Divide By Zero exception. My suspect is that in the Application version in Linux Mint 10 and 11 (and the distro used by Blazeen) the compiler somehow think 5/0 is a valid operation, and the risult is +inf. Help!!! ---------------------------------------------------------------------- Blaazen · 2011-06-12 09:25 My first attempt was in Chakra (Arch-based linux). I rebooted to Kubuntu with the same result (Res:+Inf). (both are 64-bit) Then I tried compile it without Lazarus from console (fpc filename.pas - without compiler options). I tried both as program and as unit - both gives Division by Zero. So it is not distro related. It must be some special compiler option which Lazarus uses but I don't know which, they are so many. ---------------------------------------------------------------------- Bart · 2011-06-12 09:45 Might have something to do with the exceptionmask (unit math)? Bart ---------------------------------------------------------------------- Laksen · 2011-06-12 09:53 It's probably libraries of the host system that are overriding the exception mask, as Bart suggests. If it's a big problem then include the math unit, and in the start of the program do SetExceptionMask([]); ---------------------------------------------------------------------- Alex Kidd · 2011-06-12 15:36 Quote from: Laksen on June 12, 2011, 11:53:48 amIt's probably libraries of the host system that are overriding the exception mask, as Bart suggests. If it's a big problem then include the math unit, and in the start of the program do SetExceptionMask([]); I tried the SetExceptionMask suggestion and it works  Thank you so much to all users who kindly answered my question. Lazarus is a fantastic project and I think it's gonna replace very soon Java, Mono and Python for my cross-platform needs (especially the GUI related). I just need to experiment some more. In the meanwhile I'm spreading the voice with my colleagues, showing the capabilities of Lazarus. A couple of Delphi nostalgic didn't even know there was an open source project going on and now are thanking me for the hint! ---------------------------------------------------------------------- Zoran · 2011-07-01 17:24 What is actually this ExceptionMask? I can't find anything in documentation. And I think that this diferrent behavior is not a minor thing, I realy would like to understand what it is about. ---------------------------------------------------------------------- Laksen · 2011-07-01 20:23 The exception mask is the mask for exceptions coming from the FPU So if it's entirely unmasked(as is the default for FreePascal) all exceptional behaviour will generate an exception. However if it's masked, either entirely or partly(as is the default for some C programs), the operation will go unnoticed: 1/0 will become +Inf, log(0) will be NaN, etc The FreePascal RTL does clear this exception mask when the program starts, but if the program subsequently loads DLLs or SOs that override the exception mask for the process then the program won't be notified. And it can go the other way too. Some C DLL/SOs might "randomly" generate exceptions because they might rely on unsafe FPU operations ---------------------------------------------------------------------- Zoran · 2011-07-03 10:23 Quote from: Laksen on June 12, 2011, 11:53:48 amIt's probably libraries of the host system that are overriding the exception mask, as Bart suggests. If I understand correctly, this means that if some library is called by the application and that library changes this exception mask for its own purposes, than the whole program will change its exception handling?! Quote from: Laksen on June 12, 2011, 11:53:48 amIf it's a big problem then include the math unit, and in the start of the program do SetExceptionMask([]); Does not work. Setting the ExceptionMask to empty set in program start creates many new exceptions and the application crashes. There are problems even if the ExceptionMask is set for this simple isolated line: Code: [Select]procedure TForm1.Button1Click(Sender: TObject); var   X, Y: Integer;   Res: Double;   FPUExceptionMask: TFPUExceptionMask; begin   X := StrToInt(Edit1.Text);   Y := StrToInt(Edit2.Text);   try     FPUExceptionMask := SetExceptionMask([]);     try       Res := X / Y; // I set ExceptionMask only for this line of code     finally      SetExceptionMask(FPUExceptionMask); // and restore it back     end;     Label3.Caption := FloatToStr(Res);   except     on E: Exception do       Label3.Caption := E.ClassName + ' - ' + E.Message;   end; end; Now it raises EInvalidOp, when I use non-divisible integers, although the result is regularly stored in Double, something which works well if I do not mess with ExceptionMask (see the picture). ---------------------------------------------------------------------- Laksen · 2011-07-03 10:36 Quote from: Zoran on July 03, 2011, 12:23:19 pmDoes not work. Setting the ExceptionMask to empty set in program start creates many new exceptions and the application crashes. Masking something means you don't want it to throw exceptions. So when you provide an empty exception mask it'll throw exceptions for every bad operation If you set the mask to [exInvalidOp,exZeroDivide] then it won't throw exceptions when those things occur ---------------------------------------------------------------------- Zoran · 2011-07-03 10:44 Quote from: Laksen on July 03, 2011, 12:36:33 pmQuote from: Zoran on July 03, 2011, 12:23:19 pmDoes not work. Setting the ExceptionMask to empty set in program start creates many new exceptions and the application crashes. Masking something means you don't want it to throw exceptions. So when you provide an empty exception mask it'll throw exceptions for every bad operation If you set the mask to [exInvalidOp,exZeroDivide] then it won't throw exceptions when those things occur Okay, it certanly is a logical explanation. However, I still find that this is not easy to control, I believe that this floating point behavior should be consistent on all platforms and widgetsets, I mean that it must be known what the default is and programmer should not set this exception mask depending on widgeset/OS, unless (s)he explicitely wants non-default behavior, don't you agree? So, I am going to make a bug report about this. Thank you, Laksen. ---------------------------------------------------------------------- Zoran · 2011-07-03 11:41 I created bug report: http://bugs.freepascal.org/view.php?id=19674 ---------------------------------------------------------------------- Zoran · 2011-07-03 19:15 This simple test: Code: [Select]uses   ... math,... var   FPUException: TFPUException;   FPUExceptionMask: TFPUExceptionMask;  begin   FPUExceptionMask := GetExceptionMask;   writeln('Masked:');   for FPUException := Low(TFPUException) to High(TFPUException) do begin     if FPUException in FPUExceptionMask then begin       write(' ', FPUException);     end;   end;   writeln;   writeln('Not masked:');   for FPUException := Low(TFPUException) to High(TFPUException) do begin     if not (FPUException in FPUExceptionMask) then begin       write(' ', FPUException);     end;   end; end;  gives this output when we don't use Gtk2: Masked:  exDenormalized exUnderflow exPrecision Not masked:  exInvalidOp exZeroDivide exOverflow With Gtk2, only exOverflow is not masked. ---------------------------------------------------------------------- Vincent Snijders · 2011-07-06 22:38 Nice write up in the wiki. ---------------------------------------------------------------------- Zoran · 2011-07-07 20:30 Quote from: Vincent Snijders on July 07, 2011, 12:38:56 amNice write up in the wiki.  Thanks, Vincent.