Jump to content

Recommended Posts

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 by Sherlock
Please create new threads for new questions

Share this post


Link to post
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

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 by Stefan Glienke
  • Like 2

Share this post


Link to post

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
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 by David Heffernan

Share this post


Link to post
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 by David Heffernan
  • Like 1

Share this post


Link to post

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

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
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

Oh, darn. I'm just lucky nobody depends on my numbers then...

  • Like 1

Share this post


Link to post
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 by Leif Uneus

Share this post


Link to post
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

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 by A.M. Hoornweg
  • Like 1

Share this post


Link to post
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

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
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

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

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

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
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

Rounds perfectly. I test and Ex

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×