Recent

Author Topic: Math.RoundTo incorrect sources?  (Read 653 times)

Avinash

  • Full Member
  • ***
  • Posts: 126
Math.RoundTo incorrect sources?
« on: April 22, 2025, 10:52:01 am »
fpcbuild-3.2.0\fpcsrc\rtl\objpas\math.pp
Code: Pascal  [Select][+][-]
  1. function RoundTo(const AValue: Double; const Digits: TRoundToRange): Double;
  2. var
  3.   RV : Double;
  4. begin
  5.   RV:=IntPower(10,Digits);
  6.   Result:=Round(AValue/RV)*RV;
  7. end;

Does it look like something is wrong here? Shouldn't it be something like this instead:
Code: Pascal  [Select][+][-]
  1. Result := Round(AValue*RV)/RV;
or even something else?

Josh

  • Hero Member
  • *****
  • Posts: 1374
Re: Math.RoundTo incorrect sources?
« Reply #1 on: April 22, 2025, 11:01:25 am »
i would thought if you multiply first it is likely to go out of range of double with high digits value.
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Thaddy

  • Hero Member
  • *****
  • Posts: 16945
  • Ceterum censeo Trump esse delendam
Re: Math.RoundTo incorrect sources?
« Reply #2 on: April 22, 2025, 11:02:29 am »
@Avinash No, your second example is the naive way.
The implementation in math is better since it need no expansion.
@Josh posts crossed. Same answer.
« Last Edit: April 22, 2025, 11:05:38 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Avinash

  • Full Member
  • ***
  • Posts: 126
Re: Math.RoundTo incorrect sources?
« Reply #3 on: April 22, 2025, 11:11:57 am »
Code: Pascal  [Select][+][-]
  1. function Round(d: ValReal):Int64;
I meant that Round(AValue/RV) will always be an integer, and for AValue/RV < 1 it will be zero at all.

Khrys

  • Full Member
  • ***
  • Posts: 227
Re: Math.RoundTo incorrect sources?
« Reply #4 on: April 22, 2025, 11:17:10 am »
This has nothing to do with the range of  Double,  if that was the reason then the formula would indeed be wrong.

The actual reason is that  Digits  is negative for decimal places, e.g.:

Code: Pascal  [Select][+][-]
  1. RoundTo(314.1592,  2) = 300    // RV = 100
  2. RoundTo(314.1592,  1) = 310    // RV = 10
  3. RoundTo(314.1592,  0) = 314    // RV = 1
  4. RoundTo(314.1592, -1) = 314.2  // RV = 0.1
  5. RoundTo(314.1592, -2) = 314.16 // RV = 0.01

This means that when decimal places are required,  RV  is less than one. Hence the division comes first - to shift the decimals out of the fractional part prior to calling  Round.

« Last Edit: April 22, 2025, 11:19:16 am by Khrys »

Avinash

  • Full Member
  • ***
  • Posts: 126
Re: Math.RoundTo incorrect sources?
« Reply #5 on: April 22, 2025, 11:21:00 am »
I understand, thank you.

Thaddy

  • Hero Member
  • *****
  • Posts: 16945
  • Ceterum censeo Trump esse delendam
Re: Math.RoundTo incorrect sources?
« Reply #6 on: April 22, 2025, 12:47:45 pm »
It has to do with both: division first is the rule.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

BeniBela

  • Hero Member
  • *****
  • Posts: 927
    • homepage
Re: Math.RoundTo incorrect sources?
« Reply #7 on: April 22, 2025, 07:42:15 pm »
it is incorrect/inaccurate

the problem is intpower for negative numbers

Code: Pascal  [Select][+][-]
  1. function intpower(base : float;exponent : longint) : float;
  2.   begin
  3.     if exponent<0 then
  4.       begin
  5.         base:=1.0/base;
  6.         exponent:=-exponent;
  7.       end;
  8.     intpower:=1.0;
  9.     while exponent<>0 do
  10.       begin
  11.         if exponent and 1<>0 then
  12.           intpower:=intpower*base;
  13.         exponent:=exponent shr 1;
  14.         base:=sqr(base);
  15.       end;
  16.   end;
  17.  

base:=1.0/base; is   already a rounded result and if you then multiply it  the rounding error gets bigger and bigger.

to be correct, it should  do the multiplications first and then calculate the inverse of the result

It's bad on win64 where float is  double. if it is extended, then it is maybe good enough for roundto
« Last Edit: April 22, 2025, 07:44:05 pm by BeniBela »

Paolo

  • Hero Member
  • *****
  • Posts: 584
Re: Math.RoundTo incorrect sources?
« Reply #8 on: April 23, 2025, 12:45:54 pm »
I don't know what is the best approach, float numbers are always "rounded" values of decimals.

the code below shows that, for this input (0.1 maybe coming from other computations), the current intpower implementation is more accurate than do first the power and then the division

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   X, Y : double;
  4. begin
  5.   X:=0.1;
  6.   X:=IntPower(X, -10);           // direct computation "intpower"
  7.  
  8.   Y:=0.1;
  9.   Y:=IntPower(Y, 10);             // do first power...
  10.   Y:=1/Y;                               //.. then divide
  11. end;
  12.  

but X = 10000000000.00 Exactly (00 00 00 20 5F A0 02 42)
and Y =   9999999999.99            (FA FF FF 1F 5F A0 02 42)

here win64, no back and forth with extended during the computation.

missed something ?

BeniBela

  • Hero Member
  • *****
  • Posts: 927
    • homepage
Re: Math.RoundTo incorrect sources?
« Reply #9 on: April 23, 2025, 04:07:40 pm »
then this is not a general intpower problem  but only in combination with roundto

0.1 is the problem.

the call in roundto is IntPower(10,Digits);

ten is stored without rounding, but if it is calculated as 0.1 then it gets rounded. For positive digits, IntPower(10,Digits); is an exact number.


Maybe intpower should check  whether the first parameter is greater or less than one and adjust accordingly

 

TinyPortal © 2005-2018