Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2019-11

Cross-compile for Linux strange behaviour with floating point constants

7 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

R Smith

Hi All,

I'm a long time Lazarus user (since Kylix failed) and a big fan. This is however my first post on any FPC/Lazarus forum.

The problem I've run into is a simple program (made in Lazarus) where I use floating point constants. It compiles and runs perfectly on Windows, but when cross-compiling for Linux, it fails for specific floating point values.

I realise immediately that, if this is a bug, it's probably an FPC bug and not Lazarus specific, but the problem is so obviously wrong, I'm thinking it cannot possibly be a compiler bug, and since I am cross-compiling from Lazarus, figured I would start asking here and rule out the obvious things first.

1. My Setup:
Fresh install of Lazarus IDE (SVN Revision 60307 - previous install also had the problem, so I updated) with FPC 3.04 installed on Windows 10 64bit (x86_64-win64) via fpcupdeluxe with compilers for Windows x86_64 and for Linux x86_64.  I run WSL Ubuntu 19.4 and also 18.4 in a VM and CentOS7 (because web), and a Linux Mint on my laptop, all of which get the same error. It compiles and runs perfectly correct in/for Windows.

2. Test case Program:

---------------------------------------------------

program fpbug;
// {$mode objfpc}{$H+}
uses
   Classes, SysUtils, Crt;
// {$R *.res}
var
   x: double;
begin
   // Successful Test:  (Win64 + Linux)
   WriteLn('');
   WriteLn('Setting Variable: x := 0.5; ');
   x := 0.5;
   WriteLn('FloatToStr( x )    --> ',FloatToStr(x));
   WriteLn('FloatToStr( 0.5 )  --> ',FloatToStr(0.5));
   // Failing Test:     (Win64 Pass,  Linux Fail (Ubuntu LTS)
   WriteLn('');
   WriteLn('Setting Variable: x := 0.35; ');
   x := 0.35;
   WriteLn('FloatToStr( x )    --> ',FloatToStr(x));
   WriteLn('FloatToStr( 0.35 ) --> ',FloatToStr(0.35));
end.
----------------------------------------------

I compile with standard debug options first, then production options (both Windows and Linux with CPU family x86_64 and target Processor Default)  and had an exhaustive run of enabling/disabling every compiler switch available in the standard Lazarus "Compilation and Linking" settings - all to no avail, the problem persists.

3. Windows console Output:

C:\Projects\[LAZARUS]\Temp\ConsoleAppTest>dir /w
 Volume in drive C has no label.
 Volume Serial Number is C234-0116

 Directory of C:\Projects\[LAZARUS]\Temp\ConsoleAppTest

[.]          [..]         [backup]     fpbug        fpbug.dbg fpbug.exe    fpbug.lpi    fpbug.lpr    fpbug.lps fpbug.res    fptest       fptest.dbg   fptest.exe   fptest.lpi fptest.lpr   fptest.lps   fptest.res   [lib] rc           rc.exe       rc.lpi       rc.lpr       rc.lps               19 File(s)      2 547 273 bytes                4 Dir(s)  88 585 740 288 bytes free

C:\Projects\[LAZARUS]\Temp\ConsoleAppTest>fpbug

Setting Variable: x := 0.5;
FloatToStr( x )    --> 0.5
FloatToStr( 0.5 )  --> 0.5

Setting Variable: x := 0.35;
FloatToStr( x )    --> 0.35
FloatToStr( 0.35 ) --> 0.35

C:\Projects\[LAZARUS]\Temp\ConsoleAppTest>

4. Linux console Output:

cuzzy at CUZ-PC64:~/temp/ConsoleAppTest$ ls
backup  fpbug.dbg  fpbug.lpi  fpbug.lps  fptest fptest.exe  fptest.lpr  fptest.res  rc      rc.lpi  rc.lps
fpbug   fpbug.exe  fpbug.lpr  fpbug.res  fptest.dbg fptest.lpi  fptest.lps  lib         rc.exe  rc.lpr
cuzzy at CUZ-PC64:~/temp/ConsoleAppTest$ ./fpbug

Setting Variable: x := 0.5;
FloatToStr( x )    --> 0.5
FloatToStr( 0.5 )  --> 0.5

Setting Variable: x := 0.35;
FloatToStr( x )    --> 0.35
FloatToStr( 0.35 ) --> Nan
cuzzy at CUZ-PC64:~/temp/ConsoleAppTest$

As the comments indicate, if I use the constant value 0.5 inside almost any function that takes a float (Double, Extended, Real, whatever), it works, both in Windows and Linux.

If I use the value 0.35 (or 0.66667 or 0.333333 - seemingly any number that has a non-exact round-trip IEEE754 representation, but this is merely a guess, no exhaustive testing) then in Windows on x86-64 it still works as expected, but on any Linux version, it returns NaN (as in my code example) or segfaults or such.

It is perfectly happy for me to assign the value to a variable and then use the variable (x in the example), but when I use the value directly, the skullduggery starts.

Tested functions (they all fail for a constant like 0.35): FloatToStr(), Trunc(), Round(), Ceil(), Floor()

Is there something I can check on my side? Does this work for others?  Might this be a cross-compile bug of sorts?  Is this some FPC, FPU or Linux peculiarity I'm not aware of?

Any help or insight would be appreciated - thanks, Cuz

Reply by email

Sven Barth
to R Smith
R Smith wrote
Is there something I can check on my side? Does this work for others? Might this be a cross-compile bug of sorts?  Is this some FPC, FPU or Linux peculiarity I'm not aware of?

Win64 is essentially the only x86 platform that does *not* support and use the Extended type. Thus a cross compiler from x86_64-win64 to any other i8086, i386 or x86_64 target will result in strange or undefined behavior due to it not providing the Extended constants correctly. To solve this you need to compile your code on a Linux system (you can use WSL without any problems) or make sure that you only use Double (you also need to cast floating point constants to Double in that case as otherwise FPC tries to use Extended nevertheless, which is why the variable in your example works, but the constant does not).

There is the plan to add software floating point support for Extended to the compiler for platforms like Win64, but it's not a trivial endeavour. Please note that cross compiling from any other platform that does not support Extended to x86 will have the same problem (except when cross compiling to Win64).

Regards,
Sven

Reply by email

Giuliano Colla
to R Smith: I'm a long time Lazarus user (since Kylix failed) and a big fan. This is however my first…

Il 20/11/2019 19:19, R Smith via lazarus ha scritto:

Show quoted text 10 lines
The problem I've run into is a simple program (made in Lazarus) where I use floating point constants. It compiles and runs perfectly on Windows, but when cross-compiling for Linux, it fails for specific floating point values. I realise immediately that, if this is a bug, it's probably an FPC bug and not Lazarus specific, but the problem is so obviously wrong, I'm thinking it cannot possibly be a compiler bug, and since I am cross-compiling from Lazarus, figured I would start asking here and rule out the obvious things first.

I tested your program by compiling natively in Linux, (Lazarus 1.8 and fpc 3.04) and it works just fine. Output is:

Show quoted text 7 lines
Setting Variable: x := 0.5; FloatToStr( x )    --> 0.5 FloatToStr( 0.5 )  --> 0.5 Setting Variable: x := 0.35; FloatToStr( x )    --> 0.35 FloatToStr( 0.35 ) --> 0.35

As with the given code Lazarus does pretty much nothing, and all the job is done by the fpc compiler,
it must be an fpc cross-compiler specific issue.
Target processor "Default" is the right choice? Maybe a more specific choice will tell the cross-compiler to pick up the right choice for fp math. Just a guess.

Giuliano

Do not do to others as you would have them do to you.They might have different tastes.

Reply by email

Giuliano Colla
to Sven Barth: Win64 is essentially the only x86 platform that does *not* support and use the Extended…

Il 24/11/2019 01:17, Sven Barth via lazarus ha scritto:

There is the plan to add software floating point support for Extended to the compiler for platforms like Win64, but it's not a trivial endeavour.

Maybe a silly question, but wouldn't it much simpler to have the compiler not use the Extended type on platforms which do not support it?

Giuliano

Do not do to others as you would have them do to you.They might have different tastes.

Reply by email

Florian Klämpfl
to Giuliano Colla
Giuliano Colla wrote 8 lines
Il 24/11/2019 01:17, Sven Barth via lazarus ha scritto: > There is the plan to add software floating point support for Extended > to the compiler for platforms like Win64, but it's not a trivial > endeavour. Maybe a silly question, but wouldn't it much simpler to have the compiler not use the Extended type on platforms which do not support it?

Yes. This is what we do. However, it bites us when cross compiling. What to do with extended constants and their handling in the compiler if the target platform has it but the target platform does not support it?

Reply by email

Giuliano Colla
to Florian Klämpfl

Il 24/11/2019 11:48, Florian Klämpfl via lazarus ha scritto:

What to do with extended constants and their handling in the compiler if the target platform has it but the target platform does not support it?

Maybe an AND condition? Use extended type only if both platforms support it?

Giuliano

Do not do to others as you would have them do to you.They might have different tastes.

Reply by email

Florian Klämpfl
to Giuliano Colla
Giuliano Colla wrote 8 lines
Il 24/11/2019 11:48, Florian Klämpfl via lazarus ha scritto: > What to do with extended constants and their handling in the compiler > if the target platform has it but the target platform does not support > it? Maybe an AND condition? Use extended type only if both platforms support it?

This is a no-go. The resulting code/executable must be agnostic regarding the host where it is generated.

Reply by email