Recent

Author Topic: [solved] RoundTo delivers too many digits  (Read 3184 times)

Muso

  • Sr. Member
  • ****
  • Posts: 356
[solved] RoundTo delivers too many digits
« on: April 21, 2021, 02:51:02 am »
I have this code:

Code: Pascal  [Select][+][-]
  1. var
  2.  jj : double;
  3. ...
  4.  jj:= 1.2345;
  5.  jj:= RoundTo(jj, -2);

The result is not 1.23 but 1.2300000000000002. Since I output this result to a text file, this is annoying. How can I prevent of getting 16 digits instead of the desired 2?

Lazarus 2.1.0 r65039 FPC 3.2.0 x86_64-win64-win32/win64
« Last Edit: April 22, 2021, 04:29:36 pm by Muso »

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: RoundTo delivers too many digits
« Reply #1 on: April 21, 2021, 02:54:18 am »
Hmm, using SimpleRoundTo instead of RoundTo does the trick, but why?

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: RoundTo delivers too many digits
« Reply #2 on: April 21, 2021, 03:00:53 am »
Hmm, using SimpleRoundTo instead of RoundTo does the trick, but why?

But not in every case.

And as strange as it is, also this code does not do the trick:

Code: Pascal  [Select][+][-]
  1. jj:= trunc(jj * 100) / 100;
  %)

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: RoundTo delivers too many digits
« Reply #3 on: April 21, 2021, 03:12:39 am »
OK, for the string output I can use formatfloat
(https://www.freepascal.org/docs-html/current/rtl/sysutils/formatfloat.html)

but I googled at first around and found in different forums that RoundTo should do the trick as well. So does anybody know why RoundTo  has now too many digits. Could it be a wrong setting in my FPC 3.2.0 setup?

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Re: RoundTo delivers too many digits
« Reply #4 on: April 21, 2021, 03:19:28 am »
RoundTo returns a floating number.

But (binary) floating numbers simply can not store some of the decimal numbers. Some numbers (iirc 0.4 is one of them) are periodic in binary. So in a finite length format (extended, double, single) only an approximation can be stored.

And that is probably what happens to you to. The number gets rounded to the nearest representable binary floating number.

Handoko

  • Hero Member
  • *****
  • Posts: 5122
  • My goal: build my own game engine using Lazarus
Re: RoundTo delivers too many digits
« Reply #5 on: April 21, 2021, 06:51:05 am »
Could it be a wrong setting in my FPC 3.2.0 setup?

Float rounding error happens not only in Pascal. It is caused by the limitation of current computing technology. You can search the Internet for more information.

How can I prevent of getting 16 digits instead of the desired 2?

There are many solutions for it, for example you can use FormatFloat so you can better control how the data will be converted to string before showing to users.

https://www.freepascal.org/docs-html/current/rtl/sysutils/formatfloat.html

dseligo

  • Hero Member
  • *****
  • Posts: 1177
Re: RoundTo delivers too many digits
« Reply #6 on: April 21, 2021, 10:58:26 am »
You can use 'Currency' type:

Code: Pascal  [Select][+][-]
  1. var
  2.  jj : Currency;
  3. ...
  4.  jj:= 1.2345;
  5.  jj:= Round(jj*100)/100;

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: RoundTo delivers too many digits
« Reply #7 on: April 21, 2021, 07:23:14 pm »
The number gets rounded to the nearest representable binary floating number.

This sounds reasonable. However, the problem for me was that googling lead me to pages like this where they write RoundTo does the job in Delphi:
http://docwiki.embarcadero.com/Libraries/Sydney/en/System.Math.RoundTo
https://borland.public.delphi.vcl.components.using.narkive.com/KcnPqLk3/roundto-problem-with-delphi
https://stackoverflow.com/questions/5191235/why-is-the-result-of-roundto87-285-2-87-28
...
 and thus I thought it will also do the job with FreePascal.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: RoundTo delivers too many digits
« Reply #8 on: April 22, 2021, 09:18:45 am »
The number gets rounded to the nearest representable binary floating number.

This sounds reasonable. However, the problem for me was that googling lead me to pages like this where they write RoundTo does the job in Delphi:
http://docwiki.embarcadero.com/Libraries/Sydney/en/System.Math.RoundTo
https://borland.public.delphi.vcl.components.using.narkive.com/KcnPqLk3/roundto-problem-with-delphi
https://stackoverflow.com/questions/5191235/why-is-the-result-of-roundto87-285-2-87-28
...
 and thus I thought it will also do the job with FreePascal.

It would result in the same with Delphi. 123.45 can simply not be correctly represented with floating point numbers. See this nifty site that shows the exact Single, Double and Extended presentation of that decimal number (though with Extended you might have luck more often than not, but that type is not available on all platforms).

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: RoundTo delivers too many digits
« Reply #9 on: April 22, 2021, 04:29:26 pm »
See this nifty site that shows the exact Single, Double and Extended presentation of that decimal number (though with Extended you might have luck more often than not, but that type is not available on all platforms).

Thanks. Unfortunately the link is dead but I understood now.

CM630

  • Hero Member
  • *****
  • Posts: 1076
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: [solved] RoundTo delivers too many digits
« Reply #10 on: April 22, 2021, 05:45:14 pm »
Quite offtopic, but just to make sure that you are aware:
   RoundTo (1.5,0); returns 2.
But
   RoundTo (2.5,0); also returns 2.

Actually, I have no idea what these functions can be used for.
Лазар 3,0 32 bit (sometimes 64 bit); FPC3,2,2; rev: Lazarus_3_0 on Win10 64bit.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: [solved] RoundTo delivers too many digits
« Reply #11 on: April 22, 2021, 06:43:44 pm »
Quite offtopic, but just to make sure that you are aware:
   RoundTo (1.5,0); returns 2.
But
   RoundTo (2.5,0); also returns 2.

Actually, I have no idea what these functions can be used for.

Hi!

We had that discussion in last summer:

https://forum.lazarus.freepascal.org/index.php/topic,50438.msg368109.html#msg368109

Winni

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: [solved] RoundTo delivers too many digits
« Reply #12 on: April 22, 2021, 07:19:30 pm »
Quite offtopic, but just to make sure that you are aware:
   RoundTo (1.5,0); returns 2.
But
   RoundTo (2.5,0); also returns 2.

Actually, I have no idea what these functions can be used for.
Round to just multiplies by the decimal factor, calls round and divides by the factor again, so basically RoundTo(val, digits) is basically just Round(val*10^digits)/10^digits.
With digits = 0 this means, it is identical to calling Round(val).

The behavior you observe is part of the functionality of how round is implemented on the FPU which is (usually configurable, but the default is) round to nearest even.
So whenever encountering .5, the round function will round to the nearest even number, in 1.5 this is 2, in 2.5 this is also 2 because 1 and 3 are odd.
So doing something like this:
Code: Pascal  [Select][+][-]
  1.   for i := 1 to 10 do
  2.     WriteLn(Roun(i+0.5));
Will result in the following:
Code: Text  [Select][+][-]
  1. 2
  2. 2
  3. 4
  4. 4
  5. 6
  6. 6
  7. 8
  8. 8
  9. 10
  10. 10
The rationale behind this is to minimize the average rounding error.
All other numbers have an "opposite" rounding error, e.g. 1.7 has a rounding error of -0.3 and 1.3 has one of 0.3. So assuming you uniformly sample the numbers to be rounded on average, there will occur as many x.7 as x.3 meaning the average rounding error cancels out.
But for 0.5 the opposite element is itself, when rounding x.5 up you have a rounding error of -0.5, when rounding it down you have a rounding error of +0.5. Therefore it was decided to simply round half of all numbers down, and half of all numbers up. This way the rounding error still averages out to 0, meaning on average computations using round will be more precise then using any other rounding method

dseligo

  • Hero Member
  • *****
  • Posts: 1177
Re: [solved] RoundTo delivers too many digits
« Reply #13 on: April 22, 2021, 07:45:31 pm »
Quite offtopic, but just to make sure that you are aware:
   RoundTo (1.5,0); returns 2.
But
   RoundTo (2.5,0); also returns 2.

Actually, I have no idea what these functions can be used for.

This is called bankers' rounding - if number is exactly at half (like 1.5 and 2.5) it will always round to even number. I think this is default rounding for x86 processors, but it can be changed.
https://en.wikipedia.org/wiki/Rounding

VTwin

  • Hero Member
  • *****
  • Posts: 1215
  • Former Turbo Pascal 3 user
Re: [solved] RoundTo delivers too many digits
« Reply #14 on: April 22, 2021, 10:23:45 pm »
You say when you "output this result to a text file" you get "1.2300000000000002". Have you tried using the Format function to limit the decimal places?

https://www.freepascal.org/docs-html/rtl/sysutils/format.html
« Last Edit: April 22, 2021, 10:28:42 pm by VTwin »
“Talk is cheap. Show me the code.” -Linus Torvalds

Free Pascal Compiler 3.2.2
macOS 12.1: Lazarus 2.2.6 (64 bit Cocoa M1)
Ubuntu 18.04.3: Lazarus 2.2.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.2.6 (64 bit on VBox)

 

TinyPortal © 2005-2018