tgbs 14 Posted July 20, 2023 (edited) program Project2; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Math; begin var a,b:double; a:=1.015; b:=2.275; writeln(round(a*Power(10,2))); writeln(round(b*Power(10,2))); readln; end. Please run as 32 and 64bit I split this off to its own topic, so that future readers will find this tightly packaged. Please consider creating a new thread for any question/issue/remark that is not exactly the same as the opening post of a thread - Sherlock Edited July 20, 2023 by Sherlock Please create new threads for new questions Share this post Link to post
David Heffernan 2345 Posted July 20, 2023 12 minutes ago, tgbs said: program Project2; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Math; begin var a,b:double; a:=1.015; b:=2.275; writeln(round(a*Power(10,2))); writeln(round(b*Power(10,2))); readln; end. Please run as 32 and 64bit Why should we do this? Share this post Link to post
Stefan Glienke 2002 Posted July 20, 2023 (edited) On 32bit the compiler keeps the value of the multiplication in the FPU to pass it to Round (which takes its parameter as Extended), on 64bit the precision of Extended is the same as Double. You get the same result on 32bit if you store the result of the multiplication into a double variable and then pass that one to round. Edited July 20, 2023 by Stefan Glienke 2 Share this post Link to post
tgbs 14 Posted July 20, 2023 I still can't figure out why 1.015 * 100 and rounded is different than 3.015 * 100 and rounded. The first is counted down and the second up - 101 and 302? Share this post Link to post
David Heffernan 2345 Posted July 20, 2023 (edited) 16 minutes ago, tgbs said: I still can't figure out why 1.015 * 100 and rounded is different than 3.015 * 100 and rounded. The first is counted down and the second up - 101 and 302? You are going to lose your mind when you learn that 1.015 is not representable, and when you write 1.015 in your delphi source code, the compiler turns it into the closest representable number which happens to be 1.0149999999999999023003738329862244427204132080078125 You might want to take a look at this famous question on SO: https://stackoverflow.com/questions/588004/is-floating-point-math-broken Edited July 20, 2023 by David Heffernan Share this post Link to post
David Heffernan 2345 Posted July 20, 2023 (edited) 21 minutes ago, tgbs said: I still can't figure out why 1.015 * 100 and rounded is different than 3.015 * 100 and rounded. The first is counted down and the second up - 101 and 302? Here's some Python output that should help: >>> from decimal import Decimal >>> Decimal.from_float(1.015) Decimal('1.0149999999999999023003738329862244427204132080078125') >>> Decimal.from_float(3.015) Decimal('3.015000000000000124344978758017532527446746826171875') >>> Decimal.from_float(1.015*100) Decimal('101.4999999999999857891452847979962825775146484375') >>> Decimal.from_float(3.015*100) Decimal('301.5') And @Stefan Glienke explained why results differ on 32 bit because that uses an intermediate 80 bit extended register that will have different values for the closest representable value. Edited July 20, 2023 by David Heffernan 1 Share this post Link to post
tgbs 14 Posted July 20, 2023 The numbers are indicative and taken from a database. I expect Delphi to behave as described in the documentation (help). I have not read anywhere that it is possible to have a difference between the same code for a Windows 32 and 64 bit application. I don't think if I interfere with the platforms. And again I ask why, for example, in a 64-bit program, rounding depends on specific numbers. Using the same types of variables and functions one rounds up and the other down. I haven't touched the rounding settings and debugged that is rmNearest Share this post Link to post
David Heffernan 2345 Posted July 20, 2023 It's exactly as @Stefan Glienke said. The 32 and 64 bit hardware is different. On 32 bit there are 80 bit floating point registers used for intemediate values. On 64 bit there are not and all FP registers are 64 bit. Did you read the link I gave you? If you have not read that yet then this conversation is rather pointless? You first of all have to appreciate that these values cannot be represented exactly in binary floating point format. Perhaps your real problem is that you are using the wrong data types. Maybe you should be using a decimal data type so that you can represent these values exactly? What are these values? Sometimes floating point is correct, sometimes decimal is correct. Without context we can't know. Or perhaps you already know about floating point representability issues. Do you? Share this post Link to post
Sherlock 663 Posted July 20, 2023 And for this reason floating point numbers should not be checked for equality without an 𝛆. So in (our) Delphi world: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Floating-Point_Comparison_Routines Share this post Link to post
David Heffernan 2345 Posted July 20, 2023 4 minutes ago, Sherlock said: And for this reason floating point numbers should not be checked for equality without an 𝛆. Not strictly true. It depends on where they are coming from. It's much more complex than this blanket advice which I always regard as poor advice. Share this post Link to post
Sherlock 663 Posted July 20, 2023 Oh, darn. I'm just lucky nobody depends on my numbers then... 1 Share this post Link to post
Leif Uneus 43 Posted July 20, 2023 (edited) 3 hours ago, Sherlock said: Oh, darn. I'm just lucky nobody depends on my numbers then... Threads can have different rounding modes. Raymond Chen. https://stackoverflow.com/q/72244777/576719 Are floating point operations deterministic when running in multiple threads? Some of your code may change the floating point settings, or even change them without restoring them. That goes for third party code in your program as well. And don´t get @David started with what dll's can surprise you with. Edited July 20, 2023 by Leif Uneus Share this post Link to post
David Heffernan 2345 Posted July 20, 2023 39 minutes ago, Leif Uneus said: Some of your code may change the floating point settings, or even change them without restoring them. The Delphi RTL can do this in multiple ways, as discussed here, and elsewhere, by me, ad nauseum. Share this post Link to post
A.M. Hoornweg 144 Posted July 24, 2023 (edited) If the number of decimal places is not greater than 4, OP might consider using the currency data type. Currencies are exact fixed-point numbers having four decimals after the dot so a value like 1.015 can be stored with perfect precision. Adding, subtracting and comparing currencies always gives an exact result. Internally, they are 64-bit signed integers with an implicit divisor of 10,000. [edit] functions like "power" return a double or extended. Prior to comparing the results of such functions, store them in currencies. Edited July 24, 2023 by A.M. Hoornweg 1 Share this post Link to post
David Heffernan 2345 Posted July 24, 2023 1 hour ago, A.M. Hoornweg said: functions like "power" return a double or extended. Prior to comparing the results of such functions, store them in currencies. Seems like bad advice. Why would you do this? Share this post Link to post
A.M. Hoornweg 144 Posted July 24, 2023 One would only do this if one wants to convert floating point numbers to fixed point numbers. Fixed point numbers can be compared with exactness and there are use cases for that. Share this post Link to post
David Heffernan 2345 Posted July 24, 2023 2 hours ago, A.M. Hoornweg said: One would only do this if one wants to convert floating point numbers to fixed point numbers. Fixed point numbers can be compared with exactness and there are use cases for that. Why would you be calling power and then truncating to 4dp and comparing for exactness? Share this post Link to post
A.M. Hoornweg 144 Posted July 25, 2023 I wouldn't. But I certainly do have a use for fixed comma numbers that must be compared for exactness. As in being part of the composite primary key of a database table. So I cherish the fact that this numeric type can be tested with exactness. Share this post Link to post
David Heffernan 2345 Posted July 25, 2023 Floating point can be tested for exactness too. Your idea of doing floating point calcs and then rounding to 4dp before equality testing is still subject to issues. If there are differences after the floating point calc, then the round to 4dp could fall on different sides of the rounding boundary. Always in these discussions there needs to be more specifics of the calcs involved. Share this post Link to post
A.M. Hoornweg 144 Posted July 25, 2023 You misinterpret my idea. OP merely used power() and rounding to demonstrate that 32- and 64 bit code do not have identical FP accuracy. Unfortunately people think in decimal digits and want those represented and rounded accurately. I simply recommend to use a fixed point numeric variable type for storage if this is the purpose and if the number of decimals is <=4. A soon as there is a FP calculation involved there is the risk that the result (a double, single or extended) isn't an exact representation anymore. But still, replacing "double" by "currency" in OP's example produces the expected result in both 32 and 64 bit mode. Share this post Link to post
David Heffernan 2345 Posted July 25, 2023 20 minutes ago, A.M. Hoornweg said: But still, replacing "double" by "currency" in OP's example produces the expected result in both 32 and 64 bit mode. There will be other calculations where that isn't the case Share this post Link to post
Tommi Prami 130 Posted July 28, 2023 On 7/20/2023 at 11:17 AM, tgbs said: writeln(round(a*Power(10,2))); writeln(round(b*Power(10,2))); Could you try will these routines work for you; https://github.com/TommiPrami/DecimalRound/ 1 Share this post Link to post
Tommi Prami 130 Posted July 28, 2023 51 minutes ago, tgbs said: Rounds perfectly. I test and Ex Thanks... -Tee- Share this post Link to post