Author Topic: CINT Oddity (and Slightly Incorrect _ROUND Wiki?)  (Read 2741 times)

0 Members and 1 Guest are viewing this topic.

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
CINT Oddity (and Slightly Incorrect _ROUND Wiki?)
« on: February 15, 2020, 07:48:10 am »
The definition of _ROUND in the Wiki gives:

The _ROUND function rounds to the closest even INTEGER, LONG or _INTEGER64 numerical value.


Thinking to myself "Why would anybody want to round to the nearest even integer (that is to say one that is divisible by 2)?", I think that this sentence is wrong and should not contain the word "even".  _ROUND rounds to the nearest integer:
Code: QB64: [Select]
  1. PRINT _ROUND(5.501)
  2. PRINT _ROUND(5.499)
gives 6, 6, 5 as expected (not 6, 6, 6!).

Meanwhile I thought that I'd demonstrate to myself that CINT always behaves properly, but:
Code: QB64: [Select]
  1. PRINT CINT(-6.5)
  2. PRINT CINT(-5.5)
  3. PRINT CINT(-4.5)
  4. PRINT CINT(-3.5)
  5. PRINT CINT(-2.5)
  6. PRINT CINT(-1.5)
  7. PRINT CINT(-0.5)
  8. PRINT CINT(0.5)
  9. PRINT CINT(1.5)
  10. PRINT CINT(2.5)
  11. PRINT CINT(3.5)
  12. PRINT CINT(4.5)
  13. PRINT CINT(5.5)
  14. PRINT CINT(6.5)
gives:                   -6, -6, -4, -4, -2, -2,  0, 0, 2, 2, 4, 4, 6, 6
I'd have expected: -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7

Actually, I'm not quite sure what I'd have expected for the negative numbers (!), so I'll stick with the positive numbers.

We normally round 6.5 to 7.  This is because the numbers 0 to 9 split in half as:

0,1,2,3,4
5,6,7,8,9

so any decimal number 6.0..., 6.1..., 6.2..., 6.3..., 6.4... rounds down to 6

and any decimal number 6.5..., 6.6..., 6.7..., 6.8..., 6.9... rounds up to 7

6.5 exactly may be a moot point as it is, of course, exactly half way between 6 and 7, but by convention (I thought) 6.5 rounds up to 7.

I would not expect 5.5 (rounds UP) to behave differently to 6.5 (rounds DOWN).

I suppose that this oddity is because of the conversions to binary.  By the way my few brain cells can never actually envisage a decimal in binary but that's just one of my problems.

Any comments on CINT(n.5) always rounding to an even integer??  I suspect that any oddity is in the person writing this!

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: CINT Oddity (and Slightly Incorrect _ROUND Wiki?)
« Reply #1 on: February 15, 2020, 09:01:32 am »
I'll leave the wiki stuff for someone else to comment on, but I'd like to explain the rounding behaviour you're seeing.

It's perhaps clearer to think about this when you have a series of numbers that have been rounded and you are adding them together:

When you round a number, you introduce an error. That error depends on what the first decimal place number was. If it were a 1, you have an error of -0.1; if it were a 9 your error is +0.1, and so on through to 4 giving error -0.4 and 6 giving +0.4.

If we assume that all values are equally probable to appear in the first decimal place, then adding a very large list should not have a very large error because, informally, every 1 can be paired with a 9, every 2 with an 8 and so on, with each pair resulting in a net error of 0.

But what about 5? It has no partner, so if we always rounded it up we would accumulate a positive error, and similarly accumulate a negative error if we rounded down. The solution is to round up half the time and down the other half. To achieve this split we assume numbers are odd and even with equal probability, and use that to decide the rounding direction. Hence the rule that 5 rounds up if it's an odd number, and down if it's even (this is equivalent to saying 5 rounds to the nearest even number, if you think about it a bit).

Thus half of our summed numbers with 5 will have an error of +0.5, and will cancel out with their pairs who have an error of -0.5.

This kind of rounding is sometimes referred to as Banker's Rounding.

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: CINT Oddity (and Slightly Incorrect _ROUND Wiki?)
« Reply #2 on: February 16, 2020, 06:41:39 am »
Luke, thanks for the insight.  I was not aware of the concept of Banker's Rounding.

But I still feel in our computing (and mathematical) sense the rounding of 0.5 up half the time and down half the time is not required (and false??).  In my discussion above I mentioned the pairing of 0,1,2,3,4 (all round down) and 5,6,7,8,9 (all round up) when these are the figures of the first decimal place.  I would say that the "class" of numbers in the range n.0 to n.49(9 recurring infinitely) just exactly balances the "class" of numbers in the range n.5 to n.99(9 recurring infinitely), and therefore 0.5 belongs in the rounding up class, and not 50% down / 50% up.

Consider the following code:
Code: QB64: [Select]
  1. FOR N& = 1 TO 100000000
  2.     R% = INT(RND * 10)
  3.     S! = 6 + R% / 10
  4.     IF S! >= 6.5 THEN
  5.         Tot& = Tot& + 7
  6.     ELSE
  7.         Tot& = Tot& + 6
  8.     END IF
  9.     IF N& / 1000000 = N& \ 1000000 THEN RANDOMIZE (TIMER)
  10. NEXT N&
  11. N& = N& - 1
  12. PRINT N&, Tot&, Tot& / N&

Here numbers 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8 and 6.9 are generated (quasi-)randomly with equal probability for 100 million times.  6.0, 6.1, 6.2, 6.3 and 6.4 are rounded down to 6 while 6.5, 6.6, 6.7, 6.8 and 6.9 are rounded up to 7.  The rounded number is totalled and the total is divided by 100 million.  The answer (as I would have expected) is very very close to 6.5.

But consider the following code where 6.5 is treated specially and rounded down 50% of the time and rounded up 50% of the time.  The average is the 6.45 and NOT 6.5 (again a result I would have expected and false).
Code: QB64: [Select]
  1. FOR N& = 1 TO 100000000
  2.     R% = INT(RND * 10)
  3.     S! = 6 + R% / 10
  4.     IF S! = 6.5 THEN
  5.         IF RND < 0.5 THEN
  6.             Tot& = Tot& + 6
  7.         ELSE
  8.             Tot& = Tot& + 7
  9.         END IF
  10.     ELSEIF S! >= 6.5 THEN
  11.         Tot& = Tot& + 7
  12.     ELSE
  13.         Tot& = Tot& + 6
  14.     END IF
  15.     IF N& / 1000000 = N& \ 1000000 THEN RANDOMIZE (TIMER)
  16. NEXT N&
  17. N& = N& - 1
  18. PRINT N&, Tot&, Tot& / N&

STxAxTIC will testify that I'm definitely not a mathematician of any sort, and I'm doubtless talking nonsense - does it just become philosophy in the end?  But in order for the function CINT to give the answers that it does in QB64, presumably somebody had to code the function (in the c- equivalent) as:
Code: QB64: [Select]
  1. IF X - INT(X) < 0.5 THEN
  2.         CINT = INT(X)
  3.         ELSE
  4.         CINT = INT(X) + 1
  5.         IF X - INT(X) = 0.5 AND INT(X) \ 2 = INT(X) / 2 THEN CINT = CINT - 1

The penultimate line where you have to see if the integer number is even or not to round up/down is, I should say, most peculiar.  Who are these merchant bankers anyway?!!!
« Last Edit: February 16, 2020, 09:42:38 am by Qwerkey »