Qbasicnews.com
April 05, 2020, 04:30:47 PM
 Pages: [1] 2 3
 Author Topic: ROUNDING NUMBERS: Which method do you use? SEE MANY METHODS  (Read 8739 times)
Moneo
Na_th_an

Posts: 1971

 « on: August 16, 2003, 09:12:01 PM »

In the last couple of days in the challenge regarding "useful programs", Oracle posted the following function to perform rounding to the nearest whole number:
Code:

DEF FNround# (m#) = SGN(m#) * INT(ABS(m#) + .5)

(I took the liberty of streamlining it for the general case.)

I recognized immediately that this was the most precise rounding logic that I had ever seen, because usng the ABS it rounds correctly without being burdened by the sign of the input number. Then, it restores the sign by using the SGN.

After testing Oracle's logic thoroughly, I realized that for at least the last 5 years, I have been rounding negative numbers incorrectly. :oops:

BTW: Ak00ma came up with the following enhancement of Oracle's logic, which handles numbers with implied decimal places:
Code:

DEF FNround# (m#, dec%) = SGN(m#) * INT(10^dec% * ABS(m#) + .5) / 10 ^ dec%

Without much ado, I'll list 3 erroneous rounding methods:
1) Using the CINT. This looks very simple when your numbers to be rounded are in the range of integer values. However, if you round .5 or -.5 you get zero for both. Who knows why, and what other inputs will also fail?

2) Testing the sign and rounding by +.5 or -.5 accordingly. This was my method, but testing shows that it doesn't always work for all negative inputs. The root of the problem is stated in the QB manual: "The INT returns the largest interger less than or equal to to the inputš. Less than/equal does not work the same for positive and negative numbers. Another Basic weirdness.

3) The cavalier method. Just do: INT(NUMB+.5) and hope for the best. Works great for positive numbers, but suffers from obvious problems on negative numbers, compounded by the above less than/equal weirdness.

SUMMARY: Don't take my word for it, do your own testing. You will certainly come to realize that Oracle's approach is the only correct way to round.
BTW: Tested the INT on a negative number in Visual Basic, and got the same error. INT(-123.4) gave -124. :rotfl:
*****
_________________
 Logged
pr0gger
I hold this place together

Posts: 775

 « Reply #1 on: August 17, 2003, 12:34:08 AM »

Code:
RoundedInteger% = UnroundedSingle!

Works most of the time for me, but it has obvious limits.
 Logged

size=9]"To announce that there must be no criticism of the president, or that we are to stand by the president, right or wrong, is not only unpatriotic and servile, but is morally treasonable to the American public." -- Theodore Roosevelt[/size]
Moneo
Na_th_an

Posts: 1971

 « Reply #2 on: August 17, 2003, 01:05:12 AM »

PROGGER:
You're right, it works *most* of the time, and even handles negative numbers. I just tested it and it has the same problem as a CINT; that is, an input of .5 or -.5 returns a zero. It probably uses the same internal "Convert to INTeger" logic.

BTW: Many years ago I had all kinds of problems with CINT. I don't recall what the other problems were, other than the .5 bug, but I decided back then NEVER to use the CINT again.

I don't know what other "obvious limits " you're referring to.
*****
 Logged
Neo
Na_th_an

Posts: 2150

 « Reply #3 on: August 18, 2003, 09:47:46 AM »

Don't know what the obvious limits are!?!?!?!?
You try this:

Code:
RoundedInteger% = UnroundedSingle!

When UnroundedSingle! is a very big or very small negative number, like >32767 and <-32768.
 Logged
pr0gger
I hold this place together

Posts: 775

 « Reply #4 on: August 18, 2003, 12:31:23 PM »

Quote from: "Neo"
Don't know what the obvious limits are!?!?!?!?
You try this:

Code:
RoundedInteger% = UnroundedSingle!

When UnroundedSingle! is a very big or very small negative number, like >32767 and <-32768.

^
|
|
What he said
 Logged

size=9]"To announce that there must be no criticism of the president, or that we are to stand by the president, right or wrong, is not only unpatriotic and servile, but is morally treasonable to the American public." -- Theodore Roosevelt[/size]
Moneo
Na_th_an

Posts: 1971

 « Reply #5 on: August 18, 2003, 04:11:00 PM »

You guys are right. Trying to stuff a number outside of an integer range into an integer variable --- this is pretty obvious. Just like an old saying: "You can't get 10 pounds of sh*t into a 5 pound bag".
*****
 Logged
Antoni Gual
Na_th_an

Posts: 1434

 « Reply #6 on: August 18, 2003, 05:38:20 PM »

MS says in its Knowledge base that INT(.5) giving a result of 0 is not a bug, it's just the correct result according to IEEE standard...
Edited:  Sorry, wrong quote...

Code:

DOCUMENT:Q23389  21-FEB-1991  [B_QUICKBAS]
TITLE   :Unexpected Result from CINT(.5) in QB87; IEEE Rounds to Even
PRODUCT :Microsoft QuickBASIC Compiler
PROD/VER:3.00
OPER/SYS:MS-DOS
KEYWORDS:B_BasicCom

Summary:

The coprocessor version of QuickBASIC version 3.00 (QB87.EXE) returns
0 (zero) in the following example (as do QuickBASIC versions 4.00,
4.00b, and 4.50; Microsoft BASIC Compiler versions 6.00 and 6.00b; and
Microsoft BASIC Professional Development System (PDS) versions 7.00
and 7.10, which all use the IEEE floating-point format):

PRINT CINT(1/2)
PRINT CINT(.5)
PRINT CINT(1.0/2.0)

Three zeros (0) are printed. This differs from the non-coprocessor
version of QuickBASIC version 3.00 (QB.EXE) and earlier versions,
which instead prints three ones (1).

Compare this with the fact that PRINT CINT(1.5) returns 2 in both the
coprocessor and non-coprocessor versions of QuickBASIC version 3.00
(and in the other more recent products listed above).

The observed rounding difference in the first case above is not caused
by any bug. The IEEE standard dictates that rounding of x.5 occurs to
the even integer nearest to x, for example, CINT(.5)=0, CINT(1.5)=2,
CINT(2.5)=2, CINT(3.5)=4, CINT(4.5)=4, etc.

The non-coprocessor version of QuickBASIC 3.00 (QB.EXE) supports a
different floating-point format, the Microsoft Binary format (MBF),
which rounds differently than the IEEE standard.

End of edit

Second edit:  I erased the wrong quote because it  was about QB for MacIntosh   But it said an interesting thing :
Code:

The purpose of this behavior is to prevent an average upward (or
downward) bias as various calculations are rounded. If the number was
always rounded up, there would be an upward bias in calculations.
Rounding to the nearest even number averages out; therefore, no
rounding bias occurs.

End of second edit
 Logged

Antoni
oracle
*/-\*

Posts: 3652

 « Reply #7 on: August 18, 2003, 05:41:19 PM »

Or we could just use the common maths standard, round >=.5 up and <.5 down.

Anyone up for making a swedish rounding function?
 Logged

Sterling Christensen
Na_th_an

Posts: 1328

 « Reply #8 on: August 18, 2003, 06:09:36 PM »

INT(number + .5) seems to be mathematically correct. At ±???.5 is always rounds up (toward positive infinity). In those cases, your formula always rounds away from zero. It just depends what you want.
 Logged
Moneo
Na_th_an

Posts: 1971

 « Reply #9 on: August 18, 2003, 06:56:15 PM »

ANTONI,
Very interesting and informative. However, no matter how much authority the IEEE has, these rounding principles are not a global industry standard, and would never be accepted by any CPA or accountant. In accounting practices, if you round 2.5 you should get 3 not 2.

However, in analysing the attached document that you provided, this IEEE rounding phenomenon only occurs in QB when using the CINT function or when assigning a value to a integer variable.

If you use the "normal" way of rounding, which is to add .5 to the positive floating point number and then take the INT of the result, then the above IEEE standard has no effect at all. That is, the values will always be rounded up.

On the other hand, if your program is working and rounding values that are to be used in an IEEE related application, then you should consider emulating the IEEE standard of rounding.

PS: Have a nice vacation, Antoni. Thanks for thinking of us. :wink:
*****
 Logged
Moneo
Na_th_an

Posts: 1971

 « Reply #10 on: August 18, 2003, 08:09:26 PM »

I'm using QuickBasic 4.5 which after testing I found that it does use the IEEE floating-point format.  Here's what I found:
Code:

'ASSIGNMENT A OF FLOATING POINT NUMBER TO AN INTEGER:
R% = .5           'The result is 0 .........Expected result is 1
R% = 2.5          'The result is 2..........Expected result is 3

'USE OF CINT FUNCTION:
R% = CINT(.5)     'The result is 0 .........Expected result is 1
R% = CINT(2.5)    'The result is 2 .........Expected result is 3

So, if you are using any of the mentioned versions of QuickBasic or QB, and you DO NOT want the IEEE floating-point format to be applied to your arithmetic:
1) Don't directly assign floating-point numbers to an integer variable if you expect normal, accounting procedure rounding. Perform a standard rounding procedure on the floating-point number, and then assign the result to your integer variable.
2) Don't use CINT.

"A word to the wise is sufficient".
*****
 Logged
Moneo
Na_th_an

Posts: 1971

 « Reply #11 on: August 27, 2003, 12:44:35 AM »

CONCLUSIONS ON THE ROUNDING ISSUE:

I spent about 16-20 hours looking on the web for stuff about rounding. I found 8 rounding modes. One of them is called "Round-half-up" and is also known as "conventional rounding". This is the one that Oracle and I use.

I discovered that countries, government agencies, and corporations all do rounding in different ways. The reason: they want to handle MONEY in the manner that best suits them.  Therefore, there is no "standard" accounting method of rounding.

The programming solution is:
Find out if the project has any special rounding or truncating requirements. If so, get specific, detailed specifications, and write special functions accordingly.
If not, obtain written authorization, and use Oracle's "conventional rounding" function, as follows:
Code:
DEF FNround# (m#) = SGN(m#) * INT(ABS(m#) + .5)

Never use CINT, FIX, or INT for the purpose of rounding. They use non-conventional rounding rules.
--- EDIT ---------------------------------------

BTW: Here's 8 rounding methods that I was able to find:

1] Round-half-even: AKA Bankers Rounding, AKA odd/even rule, AKA in the USA as round-to-nearest for certain applications. This rounds 2.5 to 2, and 3.5 to 4.

2] Round-half-up: AKA conventional rounding, AKA arithmetic rounding, AKA round-to-nearest in Europe, International Finance, and in the US for taxes, particularly the IRS. This rounds 2.5 to 3. We will also call this "The Oracle method".

3] Round-down: AKA truncate. Did find some obscure usages. This rounds 1.9 to 1.

***For the following 5 types, found no usages:
4] Round-ceiling: towards + infinity
5] Round-floor: towards - infinity
6] Round-up: 1.1 goes to 2.
7] Round-half-down: Similar to Round-half-up by .5 goes down.
*****
 Logged
Blitz
I hold this place together

Posts: 853

 « Reply #12 on: September 02, 2003, 04:30:37 AM »

int() cuts the fraction off, cint() rounds. And when you cast a float to integer or long in qb, qb uses cint
 Logged

oship me and i will give you lots of guurrls and beeea
Antoni Gual
Na_th_an

Posts: 1434

 « Reply #13 on: September 02, 2003, 06:31:25 AM »

I think  the way CINT rounds is perfectly valid. Rounding .5 to the nearest even integer will no make any difference in graphics or whatever. The  strange behavior of CINT has a logical reason to be . You only need to worry if  you are doing an accounting program, in this case the rules are different from QB's
 Logged

Antoni
Moneo
Na_th_an

Posts: 1971

 « Reply #14 on: September 02, 2003, 03:17:59 PM »

BLITZ,

1) Yes, CINT rounds, but it uses the "round-half-even" method which is not what everyone considers as normal rounding.
Example: 2.5 goes to 2, and 3.5 goes to 4.

2) When CINT rounds negative numbers, it uses the "round-half-even" method on the absolute value, and then changes the sign to negative. This is a variation all its own.

3) Using an INT or assigning a float to an integer give the same results.

4) An INT doesn't always "cut the fraction off". That's true for positive numbers, but for negatives, the absolute value is rounded up to the next whole value.
------------------------------------

ANTONI,

The way CINT rounds may be perfectly valid for some applications, but obviously not for all. That's why I recommend not using it and instead implementing the exact rounding method required by the application.

Please take a look at this Microsoft page for some interesting rounding variations even within their own languages.
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q196652
*****
 Logged
 Pages: [1] 2 3