Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Just starting out? Need help? Post your questions and find answers here.
Olli
Addict
Addict
Posts: 1194
Joined: Wed May 27, 2020 12:26 pm

Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Olli »

Code: Select all

x.d = 0 50
y.d = 0.51

a.i = Round(x, #PB_Round_Nearest)
b.i = x

Debug a
Debug b
Why ?
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Michael Vogel »

As defined.
x.i=f.d equals x.i=Int(f.d)
AZJIO
Addict
Addict
Posts: 2141
Joined: Sun May 14, 2017 1:48 am

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by AZJIO »

1<>0
All right.
Olli
Addict
Addict
Posts: 1194
Joined: Wed May 27, 2020 12:26 pm

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Olli »

Thank you my friend. So why ?
User avatar
Demivec
Addict
Addict
Posts: 4257
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Demivec »

Olli wrote: Sun May 18, 2025 9:27 pm Thank you my friend. So why ?
Michael Vogel wrote: Sun May 18, 2025 8:59 pm As defined.
x.i=f.d equals x.i=Int(f.d)
AZJIO
Addict
Addict
Posts: 2141
Joined: Sun May 14, 2017 1:48 am

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by AZJIO »

Round(x, #PB_Round_Nearest) = 1.0
The variable x.i transforms 1.0 into 1
integer or int throws out the fractional part. If you remove the fractional part from the number 0.5, then 0 remains.
Olli
Addict
Addict
Posts: 1194
Joined: Wed May 27, 2020 12:26 pm

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Olli »

This...

Code: Select all

Macro test(xxx)

x.d = xxx
a.i = Round(x, #PB_Round_Nearest)
b.i = x

Debug a
Debug b
EndMacro

test(0.50)
test(0.51)
...gives that...

Code: Select all

1
0
1
1
My question is "why ?"

Why the non-explicit double-to-integer converter has a light different behaviour, compared to the Round() native function ?

Where is the bug, by the way ? My suggest tends to the non-explicit converter...
TassyJim
Enthusiast
Enthusiast
Posts: 183
Joined: Sun Jun 16, 2013 6:27 am
Location: Tasmania (Australia)

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by TassyJim »

Olli wrote: Sun May 18, 2025 10:44 pm Why the non-explicit double-to-integer converter has a light different behaviour, compared to the Round() native function ?
It is the same as Int(), not Round()
This function returns an integer value. To get a quad value, use the IntQ() function.

The integer part is created by cutting off the fractional part of the value. There is no rounding. To perform rounding, use the Round() function.
AZJIO
Addict
Addict
Posts: 2141
Joined: Sun May 14, 2017 1:48 am

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by AZJIO »

TassyJim wrote: Sun May 18, 2025 11:42 pm It is the same as Int(), not Round()

Code: Select all

b.i = x
the conversion is not Int() or Round()
The Int() function gives 0 in both cases.
The Round() function gives 1 in both cases.

Code: Select all

; Step 1
x.d = 0.51
b.i = x
Debug Int(x)
Debug Round(x, #PB_Round_Nearest)
Debug b

; Step 2
x.d = 0.50
b.i = x
Debug Int(x)
Debug Round(x, #PB_Round_Nearest)
Debug b
Apparently b.i = x has its own simplified rounding method.
User avatar
Demivec
Addict
Addict
Posts: 4257
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Demivec »

@Michael Vogel: Apparently the default conversion method from float to integer is not truncation but "banker's rounding". I had assumed this as being my past experience but I was in error. I didn't conduct a test in past PureBasic versions but historical forum posts seem to indicate this was present in past versions though in versions prior to v6.0 there was a bug that created inconsistencies between doing 'b.i = x.d' and 'b.i = 1.5 + 3.0' because of the literals were being combined during compilations using the rounding method 'Nearest' instead of the usual 'banker's rounding' that was used for type conversion other values (from variables or expressions) to integer.

Here's a comparison of PureBasic's rounding methods:

Code: Select all

;Test code to show PureBasic's included rounding methods.
;  Other methods are available through custom routines.
;
;PureBasic version 6.20
;Date of last change: 5/18/2025

EnableExplicit

Macro mcr_center(_string_, _w_, _padChar_=" ")
  ReplaceString(Space(_w_ - (Len(_string_) + (_w_ - Len(_string_)) / 2)), " ", _padChar_) +
_string_ +
ReplaceString(Space((_w_ - Len(_string_)) / 2), " ", _padChar_)
EndMacro

Macro mcr_padRSet(_string_, _w_, _p_, _padChar_=" ")
  ReplaceString(Space(_p_), " ", _padChar_) + RSet(_string_, _w_, _padChar_)
EndMacro

Define x.d, b.i, o$, i

Debug "Rounding Methods" + #LF$ + RSet("", 16, "=")    
o$ = mcr_center("", 12) +
     mcr_center("Type d->i", 12) +
     mcr_center("Int()", 12) +
     mcr_center("Up", 12) +
     mcr_center("Down", 12) +
     mcr_center("Nearest", 12)    
Debug o$
o$ = mcr_center("x.d", 12) +
     mcr_center("(banker's)", 12) +
     mcr_center("(truncate)", 12) +
     mcr_center("(ceiling)", 12) +
     mcr_center("(floor)", 12) +
     mcr_center("(half up)", 12)    ;away from zero, to be more negative or more positive
Debug o$
o$ = ReplaceString("xxxxxx", "x", ReplaceString(" - ", "-", RSet("", 10, "-"))) 
Debug o$

x.d = -5.5
For i = 1 To 21
  x.d  +  0.5
  b.i  =  x.d ;invokes the default rounding from float to integer used by IE 754 ('banker's rounding')
  
  o$ = mcr_padRSet(StrD(x, 4), 8, 2) + 
       mcr_padRSet("" + Str(b), 10, 2) +
       mcr_padRSet("" + Int(x), 10, 2) +
       mcr_padRSet("" + Round(x, #PB_Round_Up), 10, 2) +
       mcr_padRSet("" + Round(x, #PB_Round_Down), 10, 2) +
       mcr_padRSet("" + Round(x, #PB_Round_Nearest), 10, 2)
  Debug o$
Next
Outputs:

Code: Select all

Rounding Methods
================
              Type d->i     Int()        Up         Down       Nearest  
     x.d     (banker's)  (truncate)   (ceiling)    (floor)    (half up) 
 ----------  ----------  ----------  ----------  ----------  ---------- 
   -5.0000          -5          -5          -5          -5          -5
   -4.5000          -4          -4          -4          -5          -5
   -4.0000          -4          -4          -4          -4          -4
   -3.5000          -4          -3          -3          -4          -4
   -3.0000          -3          -3          -3          -3          -3
   -2.5000          -2          -2          -2          -3          -3
   -2.0000          -2          -2          -2          -2          -2
   -1.5000          -2          -1          -1          -2          -2
   -1.0000          -1          -1          -1          -1          -1
   -0.5000           0           0           0          -1          -1
    0.0000           0           0           0           0           0
    0.5000           0           0           1           0           1
    1.0000           1           1           1           1           1
    1.5000           2           1           2           1           2
    2.0000           2           2           2           2           2
    2.5000           2           2           3           2           3
    3.0000           3           3           3           3           3
    3.5000           4           3           4           3           4
    4.0000           4           4           4           4           4
    4.5000           4           4           5           4           5
    5.0000           5           5           5           5           5
For those not familiar with it, Banker's Rounding will round fractions of one-half to the nearest EVEN integer. So this will transform each of the following list of values to the value following following the '->': -3.5 -> -4 , -2.5 -> -2, -1.5 -> -2, -0.5 -> 0, 0.5 -> 0, 1.5 -> 2, 2.5 -> 2, 3.5 -> 4.
Last edited by Demivec on Mon May 19, 2025 9:22 am, edited 2 times in total.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Michael Vogel »

Hi, this happens when the RC field (Rounding Control) of the FPU is set to "00" which means "Round to nearest, or to even if equidistant". You can also set the flag to "01" (Round down), "10" (Round up) or the expected "11" (toward 0).

Would be nice to have an compiler option to modify the state :wink:

Code: Select all


FPU.w

!fstcw	word[v_FPU]
!mov	ax,	[v_FPU]
;!or 	[v_FPU],	0000010000000000b; down
;!or 	[v_FPU],	0000100000000000b; up
!or 	[v_FPU],	0000110000000000b; trunk
!fldcw	word[v_FPU]

For n=0 To 10
	f.f=n/10
	i.i=f
	b.i=f+0.5
	Debug StrF(f,3)+" -> "+StrF(i)+" -> "+StrF(b)
Next n
Last edited by Michael Vogel on Mon May 19, 2025 9:46 am, edited 1 time in total.
User avatar
Demivec
Addict
Addict
Posts: 4257
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Demivec »

Michael Vogel wrote: Mon May 19, 2025 9:18 am Hi, this happens when the RC field (Rounding Control) of the FPU is set to "00" which means "Round to nearest, or to even if equidistant". You can also set the flag to "01" (Round down), "10" (Round up) or the expected "11" (toward 0).

Would be nice to have an compiler option to modify the state :wink:
Well, each of those settings is currently available through various means. There's actually 5 methods available. The one you didn't mention is "Round to nearest if equidistant". Three are obtainable with the Round() function, one with Int(). The last is obtainable in one of three ways: as a type conversion from float to integer when assigning a value to a integer variable; using the Val() of either StrD() or StrF() with a decimal value that ends in exactly '.5' and that uses 0 for the digits parameter.
Olli
Addict
Addict
Posts: 1194
Joined: Wed May 27, 2020 12:26 pm

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Olli »

AZJIO wrote:Apparently b.i = x has its own simplified rounding method.
Yes, it does. I thank you to this observation.

Thanks to Demivec and Michael Vogel for the work you have done.
Michael Vogel wrote:Would be nice to have an compiler option to modify the state :wink:
I will see if there is not already a feature request about rounding method. If no, modifying the title, I ll demand to move this subject in the 'feature request' section.

Mathematically, round 0.49 is 0 and round 0.5 is 1. But I did not know the floats hardware gave so many operating modes...

I think show to you, where does the root of this subject come from.
User avatar
Demivec
Addict
Addict
Posts: 4257
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Demivec »

Olli wrote: Mathematically, round 0.49 is 0 and round 0.5 is 1. But I did not know the floats hardware gave so many operating modes...
There are many operating modes because mathematically 0.5 is the same distance to both 1 and 0. That means the decision on what to do when rounding it is not clear cut. This opens things up to more than a few choices, each with their own pluses and minuses.
Olli
Addict
Addict
Posts: 1194
Joined: Wed May 27, 2020 12:26 pm

Re: Round(0.51, #PB_Round_Nearest) <> {debug integer 0.51}

Post by Olli »

@Demivec

You are right, for the geometry. But for the algebra, with 10 digits from 0 to 9, if we separate the 10 digits in two sets, a low set, and a high set, we get 2 groups :
low group = 0, 1, 2, 3 and 4
high group = 5, 6, 7, 8 and 9

5 is so considered in the high half way in the results set characterized by x being between 0 included and 10 excluded.
(
x belongs to [0 ; 10[
or also
0 =< x < 10
)

Adding that the sign swaps the borns : x belongs to [-10; 0[
-10 =< x < 0. This gives -5 rounded to 10 is zero (0, not -10).

So, you are right : I am wrong, excluding the geometry and the equal distances. But in an equation, it is ruled.
5 is 10 (+/- 10)
-5 is 0 (+/- 10)
Post Reply