[solved] tiny differences with StrToFloat lazarus / 64-FPC development ---------------------------------------------------------------------- kegge13 · 2020-05-19 21:40 I use StrToFloat to convert text data to extended. All further processing is done as Extended (changing the storage data type to Double made no difference). In Delphi7 a string like '9.1' converts to the value 9.1 exactly. In the current FPC the same string converts to 9.09999999999996 on a Windows10/64bit platform with a i7-3770 processor. I get the same results on a Win10/64 i5-2540 cpu. In both cases Delphi is exact. In my test data set this ultimately leads to small deviations in the sixt significant digit, not really important, but  making it hard to show that the code in FPC performs as good as the original code. I am working in debug mode, with default options for the platform. Should I use some smart rounding to correct this? Thanks for any suggestions. ---------------------------------------------------------------------- winni · 2020-05-19 21:50 Hi! '9.1' results in 9.1 on Linux64 /AMD64 So this is either a Windows problem. Or an Intel problem. Or false alarm. Winni ---------------------------------------------------------------------- kegge13 · 2020-05-20 07:32 I introduced a smart rounding right after the StrToFloat on n significant digits, with n from 7-10. This had no effect at all to my surprise. The test data represent a noisy curve. It is filtered with a second order local polynomial. Yesterday I did a side-by-side comparison with Delphi7 and indeed those tiny differences pile up to much more serious levels. Had only heard of this effect as saga talk, but now I self stumbled upon this. ---------------------------------------------------------------------- PascalDragon · 2020-05-20 07:40 Quote from: kegge13 on May 19, 2020, 11:40:40 pmI use StrToFloat to convert text data to extended. All further processing is done as Extended (changing the storage data type to Double made no difference). In Delphi7 a string like '9.1' converts to the value 9.1 exactly. In the current FPC the same string converts to 9.09999999999996 on a Windows10/64bit platform with a i7-3770 processor. I get the same results on a Win10/64 i5-2540 cpu. In both cases Delphi is exact. The problem is that on any x86 target except Win64 floating point calculations are done using Extended (even if you declare them as Double internal calculations like for StrToFloat are still done with 80-bit). However on Win64 the x87 FPU was declared deprecated by Microsoft and thus only SSE is used thus the calculations are restricted to Double and will thus result in different values. You could try whether using TryStrToFloat results in better results as that provides overloads for Single and Double (though I did not test that). ---------------------------------------------------------------------- Thaddy · 2020-05-20 08:06 Well, Delphi64 seems to have the same bias as FPC compared to Delphi32. Which is logical since the win64 abi aliases extended to double as Sven explained. There is a slight difference in format, though. The exponent is different between FPC win64 and Delphi 64, but that does not affect the actual underlying value. You can test that for yourself using the Delphi 64 bit community edition 10.3.3. Remember Delphi7 is strictly 32 bit Windows, and the WIN32 abi uses fpu 80 bit floats, whereas any Windows D64 also aliases to double as per ms specification. I believe that FPC can be coerced into using the fpu on 64 bit intel, though. Florian once explained that here or on the mailing list. That is explicitly not Delphi64 nor win64 abi compatible, though, and not recommended. ---------------------------------------------------------------------- nanobit · 2020-05-20 08:39 FPC_SUPPORT_X87_TYPES_ON_WIN64 is discouraged, because it affects all fp types (single, double, extended). If someone needs X87 only for extended precision, then one can use something like this: uTExtendedX87.pas from https://sourceforge.net/p/biop/git/ci/master/tree/ Or use some software library. ---------------------------------------------------------------------- Thaddy · 2020-05-20 09:09 Quote from: nanobit on May 20, 2020, 10:39:41 amFPC_SUPPORT_X87_TYPES_ON_WIN64 is discouraged, Yes. (because it is not win64 abi compliant) QuoteIf someone needs X87 only for extended precision, then one can use something like this: uTExtendedX87.pas from https://sourceforge.net/p/biop/git/ci/master/tree/ Or use some software library. Why? the compiler supports it if needed. I would trust the compiler.... A rather gratuite one. Maybe to push an external library? Code written in vain? There are exactly the same caveats. ---------------------------------------------------------------------- nanobit · 2020-05-20 09:38 https://www.agner.org/optimize/calling_conventions.pdf "6.1 Can floating point registers be used in 64-bit Windows? .... The conclusion is that it is safe to use floating point registers and MMX registers in 64-bit Windows, except in kernel mode drivers." x87 usage can be abi-compliant, but is discouraged on win64, and the discouragement is based on the fact that most apps need only double precision which is more powerful with SSE. There exists no deprecation-notice for x87, thus Microsoft cannot remove this option, because the removal would break many apps produced by different compilers. ---------------------------------------------------------------------- Thaddy · 2020-05-20 16:10 That's all well known and also why FPC can support it. But it is NOT Win64 ABI compliant. It is Windows, not FPC, that deprecated 80 bit for win64. And FPC will happily comply against good advice... As pointed out. ---------------------------------------------------------------------- nanobit · 2020-05-20 18:09 The Win64 ABI specifies requirements about x87, to which clients should comply if they use x87. This is officially, and is fully supported by 64-bit Windows. Some compilers support 80bit precision via x87, others not. Before Intel/AMD removes (which is not announced) the FPU instructions, operating systems would provide emulators to compensate this. I do not recommend the full switch (to x87 single, double, extended), and the degree of implemented FPC compliance with Win64 ABI is unknown to me. Better would be an option to add extended precision (80bit), either x87 80bit or SoftFpu 80bit. ---------------------------------------------------------------------- julkas · 2020-05-21 10:08 On Win 10 64bit subsystem for Linux gcc strtod() from "9.1" and then printf() gives "9.100000". FPC 3.0.4 StrToFloat and then Format('%g', ...) gives "9.0999999999999996". ---------------------------------------------------------------------- PascalDragon · 2020-05-21 10:47 Quote from: julkas on May 21, 2020, 12:08:19 pmOn Win 10 64bit subsystem for Linux gcc strtod() from "9.1" and then printf() gives "9.100000" As is remarked here the default precision when using printf with floating point values is 6. The precision of Double is 16 however. Thus if you use printf("%.16f\n", d); you'll get 9.0999999999999996 just like with Pascal. The other way round you can get the same value that C prints by using either Str(d:8:6, strvar) or Format('%.6f', [d]) (default default precision of Format for %f is 2; also note that Format respects FormatSettings). ---------------------------------------------------------------------- julkas · 2020-05-21 10:57 Quote from: PascalDragon on May 21, 2020, 12:47:27 pmQuote from: julkas on May 21, 2020, 12:08:19 pmOn Win 10 64bit subsystem for Linux gcc strtod() from "9.1" and then printf() gives "9.100000" As is remarked here the default precision when using printf with floating point values is 6. The precision of Double is 16 however. Thus if you use printf("%.16f\n", d); you'll get 9.0999999999999996 just like with Pascal. The other way round you can get the same value that C prints by using either Str(d:8:6, strvar) or Format('%.6f', [d]) (default default precision of Format for %f is 2; also note that Format respects FormatSettings). Yes. printf("%.16f\n", d); - same result. ---------------------------------------------------------------------- Thaddy · 2020-05-21 11:09 Quote from: nanobit on May 20, 2020, 08:09:21 pmThe Win64 ABI specifies requirements about x87, Where? Exactly where? That's pulling a Trump. Does not exist. Except for deprecating 80 bit floats to 64 bits floats (double). A mild version of my thoughts is https://en.wikipedia.org/wiki/Liar_Liar Also note that things like writeln/printf ("\n"  ) and family already have issues rounding to exact possible precision given a known number. They are in effect parsed with less precision. You confuse visual representation with computational representation. You will find that the computational representation is always correct within the boundaries of the CPU/FPU and a well written ABI to match. If you can read Pi, you are dead before you finished.... You can never read the whole book... But I am extremely curious to your response of my first - and only - question... where is that official documentation.... That differs from mine (an official one, a version from Microsoft msdn) ---------------------------------------------------------------------- nanobit · 2020-05-21 15:19 You are confusing things, you refer to the long-double type which is equal to double type (64bit) since MSVC win32. This MSVC limitation should not be confused with ABI (for all compilers), which contains things about registers, mostly when calling a function: "The x87 register stack is unused, and may be used by the callee, but must be considered volatile across function calls." "The MMX and floating-point stack registers (MM0-MM7/ST0-ST7) are preserved across context switches. There is no explicit calling convention for these registers. The use of these registers is strictly prohibited in kernel mode code." I should add, the mentioned kernel mode code limitation is not new, but exists also for win32. https://docs.microsoft.com/en-us/cpp/build/x64-calling- convention?view=vs-2019 For decision help, SSE (new code) is preferred over x87 (old code) for common precision (single, double). This advice is not surprising if two options exist with different efficiency. But such preference should not be overinterpreted for x87 support in all aspects. And more x87 misunderstandings occur if MS docs are read out of context, overlooking when they refer to MS compilers only or to API level. ---------------------------------------------------------------------- Jonas Maebe · 2020-05-21 16:39 At least one official Microsoft page explicitly mentions that the x87 is deprecated on Win64: https://docs.microsoft.com/en- us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game- developers QuoteThe x87, MMX, and 3DNow! instruction sets are deprecated in 64-bit modes. The instructions sets are still present for backward compatibility for 32-bit mode; however, to avoid compatibility issues in the future, their use in current and future projects is discouraged. ---------------------------------------------------------------------- nanobit · 2020-05-21 16:45 Jonas, your Assembly Code paragraph refers to MS compilers. ---------------------------------------------------------------------- Jonas Maebe · 2020-05-21 16:49 "The instructions sets are still present for backward compatibility for 32-bit mode" makes no sense in the context of a (64 bit) compiler. This is about operating system (and possibly cpu) support. ---------------------------------------------------------------------- nanobit · 2020-05-23 05:19 Instructions need support from MS dev tools (in context of article), operating system and hardware. One could speculate whether the paragraph was written before Win ABI docs became stable about x87, but this does not really matter at the end. The support by the operating system is specified in the current Win ABI. The support of hardware FPU will not disappear before emulators step in. That's enough to make decisions. ---------------------------------------------------------------------- Thaddy · 2020-05-23 05:48 Quote from: nanobit on May 23, 2020, 07:19:06 amOne could speculate whether the paragraph was written before Win ABI docs became stable about x87, but this does not really matter at the end. On the contrary: it is essential. And the win64 ABI is fully documented and not an article. I would focus on the fact that x87 registers and mmx overlap....and mmx is at most 64 bit. (not to be confused with later additions like SSE family) https://en.wikipedia.org/wiki/MMX_(instruction_set) ---------------------------------------------------------------------- kegge13 · 2020-05-24 21:41 Thank all you hero members for the valuable input. I found the needle in an adjacent haystack. In pseudo code my moving filter looks something like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if Abs(Pos[P1]-Pos[c])>L then  AddData(Pos[P1],Signal[P1]);if Abs(Pos[P2]-Pos[c])<=L then  AddData(Pos[P2],Signal[P2]);where array Pos, Signal and L are 80 bit Extended in Delphi7. On one of my test data sets the value of L happens to be a multiple of the stepsize (in general there is no constant stepsize). Therefore  Abs(Pos[P1]-Pos[c]) can be exactly L. Again, this is no problem in D7. However in  64 bit Doubles the positions deviate slightly around the intended value (i.e string '9.1' becomes 9.09999...). Therefore the number of points in the filter changes randomly, giving noticable effects in the end. My new code looks something like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if Abs(Round(1000*(Pos[P1]-Pos[c])))>kL then  AddData(Pos[P1],Signal[P1]);if Abs(Round(1000*(Pos[P2]-Pos[c])))<=kL then  AddData(Pos[P2],Signal[P2]); where integer kL:= Round(1000); The multiplication with 1000 suits for my type of data but is rather arbitrary. The second order polynomal filter indeed degrades slightly with 64 bit Doubles for internal data but this certainly has not a magnitude of multiple orders in the data. So this degradation stays invisible on the level of my data. Some history: in the eighties I had the second desktop with a 80387 coprocessor at the former Philips physics labs, so I grew up with this kind of hardware. Why is this abandoned? Once I wrote one line of Pascal in assembler for speed reasons. Now there appear to be a lot of undocumented registers for unclear reasons. Then why not maintain the 80 bits cpu facilties? ---------------------------------------------------------------------- PascalDragon · 2020-05-25 07:51 Quote from: kegge13 on May 24, 2020, 11:41:12 pmSome history: in the eighties I had the second desktop with a 80387 coprocessor at the former Philips physics labs, so I grew up with this kind of hardware. Why is this abandoned? Once I wrote one line of Pascal in assembler for speed reasons. Now there appear to be a lot of undocumented registers for unclear reasons. Then why not maintain the 80 bits cpu facilties? Because due to the history the x87 coprocessor still works in an asynchronous way (exceptions are only reported upon executing the next floating point instruction, which can be far away) and you can only work an a single value. With modern SIMD instructions you can do calculations on multiple values at once (SIMD = Single Instruction Multiple Data) and exceptions are reported right away. Also why do you say that there are a lot of undocumented registers? The instruction set including SSE is fully documented. ---------------------------------------------------------------------- kegge13 · 2020-05-25 21:06 Quote from: PascalDragon on May 25, 2020, 09:51:27 amBecause due to the history the x87 coprocessor still works in an asynchronous way (exceptions are only reported upon executing the next floating point instruction, which can be far away) and you can only work an a single value. With modern SIMD instructions you can do calculations on multiple values at once (SIMD = Single Instruction Multiple Data) and exceptions are reported right away. Also why do you say that there are a lot of undocumented registers? The instruction set including SSE is fully documented. Thank you PascalDragon for your remarks. My (computational) problems are not large enough to go into parallel programming, but it is a nice thought anyway. The "unknown" registers were mentioned in a paper on speculative cpu cache reading strategies to get information from other processes. The author claimed that there were many registers (up to 150 or so) with unknown use or application. It is not at all my comptence area though. I was just interested in how these leakage strategies work. Also I do not remember the specific cpu type.