Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Extended KotD #25, #26, and #27: INTEGER DIVISION, CINT, and CLNG
#1
So these aren't new commands like most of the Extended KotD are, but these are some old commands that I *thought* I understood, only to realize I was 99% clueless about their behavior.

First, let me simply share links to their respect Wiki pages.  Note that currently I find these pages inadequate, (which is one reason why I feel a new KOTD entry should be used to highlight everyone to the quirks associated with them), and since I have wiki editing mojo, these pages will probably change somewhat soon(tm).  (I just want to give folks time to read this, see if I missed something, and if so, then I'll add in other's suggestions on these pages as well.)

WIKI LINKS:
https://qb64phoenix.com/qb64wiki/index.php/CINT
https://qb64phoenix.com/qb64wiki/index.php/CLNG
https://qb64phoenix.com/qb64wiki/index.php/%5C  (This is the page on INTEGER DIVISION.. the \ operator)

Now, with the links out of the way, let me go over what the existing pages on CINT and CLNG tell us.  Both say:

Quote:The ... function rounds decimal point numbers up or down to the nearest INTEGER/LONG value.
Values greater than .5 are rounded up. Values lower than .5 are rounded down.


Sounds simple enough.  Right?  Less than 0.5 and we round down.  More than 0.5 and we round up.

So here's my question for everyone -- What happens at EXACTLY 0.5?  This tidbit of information isn't really listed anywhere, and if you go back and scour the web for old QB45 documents and help files, I doubt you'll find it either.  I certainly didn't' anywhere that I looked on the net or all the resources I could find.

If your impressions are like mind, you would think, "rounds up or down to the nearest integer/long value", and you'd do rounding like always -- 0.5 and above rounds up.

And, you'd be *WRONG*!!  (Just like me!)  

CINT and CLNG work like this:
Values less than 0.5 are rounded down.
Values greater than 0.5 are rounded up.
Values exactly equal to 0.5 are rounded.... to the nearest even integer.

Now, study that last line there a few times and blink at it until it starts to sink in.  Honestly, it took a while to make sense to me.  Here's a little code snippet to run to see what I'm talking about yourself:

Code: (Select All)
For i = 1 To 10
    x = i + .5
    Print x, CInt(x)
Next

In this case, we see this:
1.5 rounds up to 2.  2 is the closest even integer.
2.5 rounds down to 2.   2 is the closest even integer.
3.5 rounds up.
4.5 rounds down.
5.5 rounds uo.
6.5 rounds down.

It's not a consistent 0.5 rounds up, or 0.5 rounds down.  It's always 0.5 rounds to the nearest even integer.

That's an odd style result and needs to be documented and folks aware of it, if they're ever going to make any real use out of it without running into errors.

Less than 0.5 and it always rounds down.
More than 0.5 and it always rounds up.
At exactly 0.5, it rounds up or down, but always rounds to the nearest even integer.

Folks definitely need to be aware of this little quirk in CINT and CLNG. 




And now that we have a basic understanding of these rounding tools, let's take a moment and talk about \ (INTEGER DIVISION).

The details here explain to us that: 
Quote:Rounding is done to the closest EVEN integer or long integer value.

Except that's not what we really see here.

Code: (Select All)
Print 5 \ 3

With the little code above, we know that 5 / 3 would be 1.6666.  The closest even integer to that value would be 2.

The result, however, is 1.

There's no rounding to the closest EVEN integer or long.  Not even close, anywhere or anyway that I can tell.  I've tested the heck out of this and here's what I've basically came up with on what's actually happening here:

x \ y  <--- Let's say this is our basic integer division formula.
INT(CINT(x) / CINT(y))   <--- this is how it's actually processed and gets the final results for us.

Does that sound a little complicated?  Try this little snippet and feel free to throw any other values you like at it to make certain the values match:

Code: (Select All)
Print 5 \ 3, Int_Div(5, 3)
Print 5.5 \ 3, Int_Div(5.5, 3)
Print 5 \ 2, Int_Div(5, 2)
Print 5.5 \ 2.5, Int_Div(5.5, 2.5)

Function Int_Div (x, y)
    Int_Div = Int(CInt(x) / CInt(y))
End Function

5 \ 3 .... this would yield 1.6666if we just divided 5 / 3, which we take the INT value of and make 1.  That's the result we see.
5.5 \ 3 .... that 5.5 would CINT up to 6.  6 / 3 = 2.  That's an integer already, so that's our result.
5 \ 2 ...  this would yield 2.5 if we just divided it.  We take the INT value of that and make 2.
5.5 \ 2.5 .... that 5,5 CINTs to become 6.  The 2.5 CINTs to become 2.  6 /2 = 3, and that's our answer.

So.. what that comment in the wiki is referring to... I honestly don't know.

INTEGER DIVISION simply returns the INT value of division, and if we pass it non-integers to work with, it converts them via CINT or CLNG before processing them.



So.... Everyone follow along with all that?  There's a lot of little nuance going on in the syntax and behavior here that folks should be aware of.

In conclusion:

CINT and CLNG round down if below 0.5, round up if above 0.5, and round to the nearest even integer if exactly 0.5.
When doing integer division (the \ math symbol), what we're actually doing is INT(CINT(x) / CINT(y)).

I'm not certain if other folks understood all these little quirks and what these commands are actually doing behind the scenes for us -- apparently, I didn't have a clue personally, and that's after programming in QBASIC for over 30 years!!  None of the documentation I found explains these little distinctions, but as far as I can tell, we perfectly mimic the old QB45 behavior.  This isn't a bug or a glitch... It's nothing new.  It's the way it's always been, apparently.

It's just that NOW I can understand what it's doing, while in the past I only *thought* I knew -- and I was wrong over it all.  LOL!!

Try it all out.  Kick it around.  See if you understand what I'm talking about here.  If you guys find someplace that I'm wrong or something that doesn't fit this logic, then kindly post about it, correct me, and let's all get to understanding these commands better.

If everyone agrees with these conclusions, and doesn't find any flaw anywhere in what I'm telling you guys, I'll go in and update the wiki to reflect this additional information in about a week or so.  (I want to give people time to respond first before making changes.)

Any and all comments, feedback, and cookies are welcome after banging my head on the desk for the past few days sorting into this mess.  Wink
Reply
#2
Code: (Select All)

For i = 1 To 10
    x = i + .5
    Print x, CInt(x)
Next
gives
Code: (Select All)

1.5          2
2.5          2
3.5          4
4.5          4
5.5          6
6.5          6
7.5          8
8.5          8
9.5          10
10.5          10
it follows a simple rule
if integer-part is odd then round-up
if integer-part is even then round-down
Reply
#3
(Yesterday, 12:26 PM)Jack Wrote:
Code: (Select All)

For i = 1 To 10
    x = i + .5
    Print x, CInt(x)
Next
gives
Code: (Select All)

1.5          2
2.5          2
3.5          4
4.5          4
5.5          6
6.5          6
7.5          8
8.5          8
9.5          10
10.5          10
it follows a simple rule
if integer-part is odd then round-up
if integer-part is even then round-down

Which is the nearest even integer.  Wink
Reply
#4
I must admit in most cases where I need this, I do a short test to see how it worked again  Blush

btw: The INT doc says it returns an Integer, but INT also works with LONG and _INTEGER64
45y and 2M lines of MBASIC>BASICA>QBASIC>QBX>QB64 experience
Reply
#5
@mdijkens
Int also does rounding, I think that it rounds toward zero, so as long as the argument is positive it will give the integer-part
here's the above snippet modded a bit
Code: (Select All)

For i = 1 To 10
    x = i + .5
    Print x, Int(-x)
Next
output
Code: (Select All)

1.5          -2
2.5          -3
3.5          -4
4.5          -5
5.5          -6
6.5          -7
7.5          -8
8.5          -9
9.5          -10
10.5        -11
if you are sure that you don't want any rounding then use Fix

but if your language doesn't have the Ceil function then you can fake it
Ceil(x) = -Int(-x)
Reply
#6
@Jack

Code: (Select All)
For i = -10 To 10
    x = i + .5
    Print x, Int(x), CInt(x), _Ceil(x)
Next

INT always rounds down.
_CEIL always rounds up.
CINT and CLNG ... rounds down if < 0.5, up if > 0.5, and to the nearest even integer if *exactly* 0.5.
Reply
#7
0.5 rounds to the nearest even integer

Have to admit the rounding rules of CINT and CLNG are exactly as I'd expect them, and I'm probably the only one, but that is exactly how I learned the rounding rules it in school.

That probably proves once more, that the former East Germany way back ages ago had one of the most sophisticated education systems in the world. And it also explains why I never got in trouble using these commands and it brings always a smile on my face to see people struggle with things they should have learned in 5th grade already.

Unfortunately with the German reunion in 1990 they were not smart enough to take over the education system for whole Germany. They simply didn't want it, because it was communistic influenced.
The result, today each of the 16 federal regions in Germany teaches different things on different pace. If a family moves from Bavaria to Saxony, then their Kids its education is screwed because they are either one year ahead or one year behind.
Reply
#8
Here, where I live in the states, we were always taught:  Less than 0.5, round down.  Otherwise round up.  (No special rule for 0.5.)

I honestly had never heard of rounding to the nearest even number until going to college and hearing of "banker's rounding".  It's still not something I ever see used around here for anything at all.  I guess we're just not sophisticated enough to sort out rounding to the nearest even number...

Not that that's surprising anymore.  At our local high school, they removed all the analog clocks because none of the students knew how to read them and were being stressed out during exams, not knowing how much time they had left.  If we can't read a clock, we certainly can't sort out how to round properly without several pages of instructions and preferably a dozen illustrated photos to use for reference.  /sigh
Reply
#9
Dang I did not know Integer division was a problem! (Giving results unexpected like CINT and CLNG which I never use anyway)

Round2 gives me results I'd prefer as expected, has this been proposed before? (expected round up on .5 or down if negative)
Code: (Select All)
For i = -5 To 5 Step .5
    Print i, round2&(i), _Round(i)
Next
Function round2& (x)
    If x < 0 Then round2& = -1 * Int(-x + .5) Else round2& = Int(x + .5)
End Function

I vaguely remember some conversation about rounding and Luke mentioning that rounding in QB64 was like bankers way of rounding .5's so that a balance of them doesn't accumulate a significant plus amount.
b = b + ...
Reply
#10
Note that after some testing, _ROUND also behaves exactly the same as CINT and CLNG.  It's simply for a larger range of areas.

value < 0.5 rounds down.
value = 0.5 rounds to the nearest even integer.
value > 0.5 rounds up.

Code: (Select All)
For x = -10.5 To 10.5
    Print x - .01; _Round(x - .01),
    Print x; _Round(x),
    Print x + .01; _Round(x + .01)
Next

Its wiki page needs a complete update and overhaul as well to indicate this behavior properly as it simply says:

Quote:Rounding is done to the closest even integer value. 


And, as you can see from the above, that statement only holds true when dealing with 0.5 values directly.  All others round up or down to the nearest integer, even if that's an odd number.
Reply




Users browsing this thread: 1 Guest(s)