Lazarus

Programming => General => Topic started by: McDoob on November 13, 2024, 09:25:57 pm

Title: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: McDoob on November 13, 2024, 09:25:57 pm
Hello, world.

I am confused by FPC's refusal to understand my intent:

  if (MaxVal mod NumSegments = 0)
    then SegmentVal:=MaxVal div NumSegments
    else SegmentVal:= int(MaxVal/NumSegments);

Error: Incompatible types: got "Double" expected "LongInt"

Having already perused the forum, I've changed my vars to double...but...

  FullSegments:=CurVal div SegmentVal;

Error: Operator is not overloaded: "LongInt" div "Double"

Oh, right, Because being NOT overloaded is a reason to complain? o_O
Whiskey Tango Foxtrot, over?
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 13, 2024, 09:36:21 pm
First the question in the title: Int does not return an integer value because thats not what it is intended to do if you look at the documentation of the int function: https://www.freepascal.org/docs-html/rtl/system/int.html
Quote
Int returns the integer part of any Real X, as a Real.
What you are looking for is the trunc function: https://www.freepascal.org/docs-html/rtl/system/trunc.html
Quote
Trunc returns the integer part of X, which is always smaller than (or equal to) X in absolute value.

Second the question of the thread: If you look at the documentation of the language: https://www.freepascal.org/docs-html/current/ref/refsu44.html#x151-17500012.8.1
Quote
With the exception of Div and Mod, which accept only integer expressions as operands, all operators accept real and integer expressions as operands.
The reason div is not overloaded for doubles, is very simply because it's the integer division it's an operation thats solely for dividing integers by integers and returning integers.

Lastly to solve your problem:
Code: Pascal  [Select][+][-]
  1.   if (MaxVal mod NumSegments = 0)
  2.     then SegmentVal:=MaxVal div NumSegments
  3.     else SegmentVal:= int(MaxVal/NumSegments);
Assuming MaxVal and NumSegments are both integers, just use:
Code: Pascal  [Select][+][-]
  1. SegmentVal:=MaxVal div NumSegments;
If they are doubles use:
Code: Pascal  [Select][+][-]
  1. SegmentVal:=trunc(MaxVal / NumSegments);

It's very easy if you read the documentation :)
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: McDoob on November 13, 2024, 09:46:28 pm
Hi, Warfley!
Thanks for the quick response!

If I understand you correctly, I need to RTFM, yes?  8)

But, I don't think trunc will work here. Is there a rounding func, instead? Later on in my program, I use

   PartSegment:=int(((CurVal mod SegmentVal)/SegmentVal)*100);

and I must assume that would be a problem, even after applying your response...
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 13, 2024, 09:54:00 pm
So trunc just "truncates" the number, so it removes everything behind the decimal point. So 0.2, 0.8 and 0.99 all become 0 while -3.2, -3.7 and -3.99 all become -3.

There is also round (https://www.freepascal.org/docs-html/rtl/system/round.html) which does the "usual" rounding, so 0.3 becomes 0 and 0.7 becomes 1.
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: McDoob on November 13, 2024, 10:02:17 pm
Clearly, your status as 'Hero Member' is well deserved, sir.

Assuming I actually RTFM, and then apply the round func to my program...

What happens when the variable approaches 0.5? Or 0.05? Or whatever? Does 'round' round down, or round up?
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: marcov on November 13, 2024, 10:04:02 pm
Default banker's rounding. So x.5 up when x is even and down when x is odd
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: McDoob on November 13, 2024, 10:10:19 pm
I've never heard of 'banker's rounding' before this.

I was taught that x.5 is always up...thanks for your help, marcov, and Warfley!

How do I mark this problem as [Solved]?
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 13, 2024, 10:17:30 pm
It applies round to nearest even, meaning 0.5 is rounded down, because 0 is an even number. 1.5 is rounded up because 2 is an even number. This is generally how the FPU (floating point unit) in your CPU is wired and is basically how all computers do rounding nowadays. It's counter intuitive to what you may have learned in school, but it reduces the average rounding error because if half the time it is rounded up and half the time it is rounded down, on average it evens out.

Example:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses SysUtils;
  6.  
  7. function RoundUp(const d: Double): Integer; inline;
  8. begin
  9.   // This will round down when <.5 and round up for >=.5
  10.   Result := trunc(d+0.5);
  11. end;
  12.  
  13. const
  14.   iter = 10000000;
  15. var
  16.   sum: Double;
  17.   roundedsum: Integer;
  18.   i: Integer;
  19.   rndval: Double;
  20.   start: QWord;
  21. begin
  22.   Randomize;
  23.   sum:=0;
  24.   roundedsum:=0;
  25.   start:=GetTickCount64;
  26.   for i:=1 to iter do
  27.   begin
  28.     // random value between 0 and 10 with 1 decimal digit
  29.     rndval:=Random(100)/10;
  30.     sum+=rndval;
  31.     roundedsum+=round(rndval);
  32.   end;
  33.   WriteLn('Nearest Even time: ', GetTickCount64-start,'ms');
  34.   WriteLn('Nearest Even error: ', (sum-roundedsum).tostring);
  35.   // Now the same for roundup
  36.   sum:=0;
  37.   roundedsum:=0;
  38.   start:=GetTickCount64;
  39.   for i:=1 to iter do
  40.   begin
  41.     // random value between 0 and 10 with 1 decimal digit
  42.     rndval:=Random(100)/10;
  43.     sum+=rndval;
  44.     roundedsum+=roundup(rndval);
  45.   end;
  46.   WriteLn('Round up time: ', GetTickCount64-start,'ms');
  47.   WriteLn('Round up error: ', (sum-roundedsum).tostring);
  48.   ReadLn;
  49. end.
  50.  
Result:
Code: Pascal  [Select][+][-]
  1. Nearest Even time: 93ms
  2. Nearest Even error: -107,90000513196
  3. Round up time: 157ms
  4. Round up error: -498955,69999671
For n operations the rounding error of nearest even rounding is about 0, while for rounding up it is around -n/2 and for rounding down it's around n/2.
And because it directly utilizes the hardware implementation of the FPU it's also much faster
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: McDoob on November 13, 2024, 10:21:25 pm
Warfley, thank you for the deeper dive.

I can expect my program to respond better to 'round' than 'int' for sure. Assuming that 'round's output is an integer...

Again, how do I mark this as [Solved]?
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: carl_caulkett on November 13, 2024, 10:55:52 pm
Just modify the first message in your thread; as well as the body of the message, you'll also be able to amend the title where you can add [SOLVED] as a prefix or suffix.
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: McDoob on November 13, 2024, 11:23:38 pm
Just modify the first message in your thread; as well as the body of the message, you'll also be able to amend the title where you can add [SOLVED] as a prefix or suffix.

thx..if it builds, it ships. XD

If not FUBAR
  then SNAFU;
end.
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: jollytall on November 14, 2024, 01:26:38 pm
What I do is
Code: Pascal  [Select][+][-]
  1. i := round(int(r));
round alone is not the same because of rounding up for r> or >=0.5.
trunc is not good because of negative numbers.
So, if you want an int() function that gives back an integer, AFAIK the best is the above. You can make it a function or a macro if you prefer.
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 14, 2024, 02:07:17 pm
What I do is
Code: Pascal  [Select][+][-]
  1. i := round(int(r));
...
trunc is not good because of negative numbers.

What do you mean?
Your code produces the same as
Code: Pascal  [Select][+][-]
  1. i := trunc(r);

Title: Re: Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 14, 2024, 02:28:46 pm
Code: Pascal  [Select][+][-]
  1.   iter = 10000000;
Result:
Code: Pascal  [Select][+][-]
  1. Round up error: -498955,69999671
For n operations the rounding error of nearest even rounding is about 0, while for rounding up it is around -n/2 and for rounding down it's around n/2.

n(iter) is 107, so ~n/20  ;)
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: jollytall on November 14, 2024, 04:18:36 pm
What do you mean?

Mixed up... I don't know why it came to my head. I even looked in some of my code now, and did not find when I used this or something similar. Suddenly I felt that int() is rounding toward negative infinity (in which case I could use floor()). So, I am not sure why it got stuck in my memory.
Post crossed out.
Title: Re: Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 14, 2024, 05:14:21 pm
n(iter) is 107, so ~n/20  ;)

Oh yes you are absolutely right, the chance of landing on .5 is 1 in 10 (as there are 10 possible digits 0-9) so in 10% of cases you have an error of 0.5, while all other cases cancle out (0.2 has the inverted rounding error of 0.8, 0.6 has the negative error of 0.4 and so on) so in 90% of cases you have no rounding error and in the 10% of cases you have 0.5 error resulting in 0.5*0.1=0.05 error per operation
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: TRon on November 14, 2024, 05:26:51 pm
Please forgive my ignorance as I did not follow this topic but SetRoundMode (https://docs.freepascal.org/docs-html/3.0.0/rtl/math/setroundmode.html) ?
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: Thaddy on November 14, 2024, 08:09:04 pm
 ;) ;) ;) ;) ;) ;) 8-) Yes.
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 15, 2024, 10:05:46 am
Please forgive my ignorance as I did not follow this topic but SetRoundMode (https://docs.freepascal.org/docs-html/3.0.0/rtl/math/setroundmode.html) ?

Yes, when you change RoundingMode from rmNearest (which is the default) to something else, the problem of rounding .5 will disappear.
But in most cases one needs rmNearest, and so deals with banker's rounding. And while it's good to count money (as it follows from its name), for many mathematical and physical tasks it's not correct.
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 15, 2024, 11:34:38 am
But in most cases one needs rmNearest, and so deals with banker's rounding. And while it's good to count money (as it follows from its name), for many mathematical and physical tasks it's not correct.
I mean there is no "correct way" for rounding, rounding by definition is creating an error. The question is just what kind of error you want to minimize. Nearest Even rounding minimizes the commulative error. And it's not just for counting money, it's whenever you perform multiple operations and are interested in keeping the commulative error as low as possible. For example if you do a finite difference time domain simulation in physics, where you simulate in small time steps, and rounding is performed on each step, the result of which is based on the rounded result from the first step, you also want to have the rounding error to be as small as possible.

The only area that I know of where this is not wanted is statistics, because this rounding mode favors even numbers over odd numbers (basically increasing their prevalence by 10%), so you trade off commulative error for distribution error, and in statistics when the distribution of certain values is important, this can be an issue.

General rule of thumb: Nearest even rounding is preferrable when you do multiple computations that build on top of each other. So basically pretty much anything that models complex systems over time
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 15, 2024, 01:05:49 pm
I mean there is no "correct way" for rounding, rounding by definition is creating an error.

You are absolutely right in this. But it's hard to say anything without seeing formulae. And there is mathematical rule for .5 rounding, and it's not "banker's". I suspect that fpc's choice (following Delphi?) reflects the field which fpc mainly aimed to.

And not all choose "banker's". F.e. see the result of this Fortran code
Code: Fortran  [Select][+][-]
  1. print *, nint(1.5), nint(2.5)
  2. end

or the picture:
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 15, 2024, 01:50:04 pm
You are absolutely right in this. But it's hard to say anything without seeing formulae. And there is mathematical rule for .5 rounding, and it's not "banker's". I suspect that fpc's choice (following Delphi?) reflects the field which fpc mainly aimed to.
Has very little to do with Delphi, but it's simply the default as specified in the IEEE 754 standard for floating point operations. The reason Fortran doesn't do it this way is probably just because Frotran predates this standard. But Every language that has been implemented since then usually follows the IEEE 754 standard. And Backwards compatibility is king. You can also see this in C, where the original round function that is part of the language since the beginning rounds away from zero, but later the standard introduced a new function rint, which is compliant with IEEE 754.

Also as I said it's the default configuration of the FPU. While you can change the FPU mode, I won't advise for doing it, because this changes the behavior for everything not just that one operation in your code. So enforcing a different behavior just for your code requires emulating it in software, which is for one more difficult to implement, but more importantly also slower. So most languages don't bother.

But if you really want it, you can always just link against libc and use their round function. it's also fairly optimized and only like 10%-20% slower than the bare metal rint function
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 15, 2024, 02:38:46 pm
Has very little to do with Delphi, but it's simply the default as specified in the IEEE 754 standard for floating point operations. The reason Fortran doesn't do it this way is probably just because Frotran predates this standard.
But standard defines 5 modes. And one of them "Round to nearest, ties away from zero". Why TFPURoundingMode has only 4 modes?

Also as I said it's the default configuration of the FPU. While you can change the FPU mode, I won't advise for doing it, because this changes the behavior for everything not just that one operation in your code. So enforcing a different behavior just for your code requires emulating it in software, which is for one more difficult to implement, but more importantly also slower.
You mean using SetRoundMode? If this is so, why it exists at all?
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 15, 2024, 04:21:31 pm
Probably to be compatible with the C API for fesetround and fegetround which decided to not implement on tie away from zero
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: Zoran on November 15, 2024, 06:21:18 pm
Simply use
Code: Pascal  [Select][+][-]
  1. N := Trunc(X + 0.5);

Edit: Sorry, I see Warfley gave this solution (https://forum.lazarus.freepascal.org/index.php/topic,69325.msg538080.html#msg538080) already.
Title: Re: Why doesn’t the 𝚒𝚗𝚝 function return an integer value?
Post by: Kays on November 15, 2024, 06:52:51 pm
Why doesn’t the 𝚒𝚗𝚝 function return an integer value?
I have not really read the answer in this thread:

System.int (https://www.FreePascal.org/docs-html/current/rtl/system/int.html) returns a real because if it was defined to return an integer, it should be an error if a value int(x) does not exist in integer.
Code: Pascal  [Select][+][-]
  1. { Two times the assignment of the value 10²⁰ − 1 to a variable, yet (potentially) different behavior. }
  2. myRealVariable    := 99999999999999999999.0; { ✔ if a reasonable approximation can be achieved }
  3. myIntegerVariable := 99999999999999999999;   { ↯ if > maxInt }
If you want to provoke an error if the value’s magnitude exceeds that of integer is capable of, you need to use trunc (https://www.FreePascal.org/docs-html/current/rtl/system/trunc.html).
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 15, 2024, 07:40:45 pm
Simply use
Code: Pascal  [Select][+][-]
  1. N := Trunc(X + 0.5);

It's not so simple, as X may be negative (though it's still simple  :D ).
PS. And it is slower, compared to "Round to nearest, ties away from zero" implemented in FPU.
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 15, 2024, 07:59:44 pm
But if you really want it, you can always just link against libc and use their round function. it's also fairly optimized and only like 10%-20% slower than the bare metal rint function


When I really want it, I use Fortran.  :)
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: Warfley on November 15, 2024, 08:15:14 pm
The only reason Fortran is sometimes faster than C is because it does not allow aliasing and therefore doesn't need stuff like write barriers and can utilize registers more effectively. In cases where the GCC or clang knows there is no aliasing conflict (iirc you can set a Compiler switch to promise the Compiler that you don't do aliasing with you pointers) it produces equally efficient code. And with the round function being implemented in the glibc directly, which is shipped with the system and usually contains different implementations optimized for different CPUs, it shouldn't be less efficient than the Fortran round function.

The reason it's slower is because either there are multiple computations, or it must set and reset the rounding mode of the fpu and this is equally true for Fortran.
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 15, 2024, 08:26:20 pm
Of course gfortran relies on gcc, but I remember the times when Fortran was much faster than C in number crunching on PC, and not only PC.
Now I use Fortran as a hobby, some kind of nostalgy.  :'(

EDIT. Yes, gfortran's nint simply alias for lround.  :(  O tempora, o mores...
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 15, 2024, 10:02:02 pm
@Warfley
You didn't comment the picture of Microsoft calculator. It thinks that round(2.5) = 3, BTW  ;)
Title: Re: [SOLVED] Why doesn't the 'int' function return an integer value?
Post by: tetrastes on November 21, 2025, 11:25:52 pm
This is not for restarting the discussion, but I think it's worth to note.

From ISO 7185 Pascal (point 6.6.6.3) and ISO 10206 Extended Pascal (6.7.6.3):
Quote
round(x)
From the expression x that shall be of real-type, this function shall return a result of integer-type.
If x is positive or zero, round(x) shall be equivalent to trunc(x+0.5); otherwise, round(x)
shall be equivalent to trunc(x-0.5).

And indeed, in modes ISO and ExtendedPascal the result of round(2.5) is 3.
TinyPortal © 2005-2018