Announcement

Collapse
No announcement yet.

Common Rounding

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Michael Mattias
    replied
    If you write
    x = 3.045##
    STR$(x, 18) return exactly 3.045
    You or I could do the math but I would not be surprised if STR$(X, 23) - were that supported - were to return some non-zero digits in those extra five positions.

    Floats are often approximations.

    This particular scenario is just begging for:
    Code:
     LOCAL X AS CUR 
     X = [email protected]
    CUR and CUX are scaled binary rather than float, so they are not approximations.

    MCM

    Leave a comment:


  • Marc Chauviere
    replied
    Originally posted by Charles Dietz View Post
    This is what I did...
    LOCAL x AS EXT
    x = 3.045
    ? STR$(x, 18) --> 3.04500007629394531
    which of course is why it rounds up.
    If you write
    x = 3.045##
    STR$(x, 18) return exactly 3.045
    I think when we fill a variable with a constant, we should always define the constant with same data type as the variable.

    Leave a comment:


  • Charles Dietz
    replied
    Interesting... Thanks, John

    Leave a comment:


  • John Gleason
    replied
    One reason is accountants use common rounding. Not too long ago I was going to post the above algo in a new thread called "Accountants, not Bankers," a line from a rounding question asked a few years ago. But someone--actually I think MCM --said it's probably best to have a different title not so confusing. Anyway, I never did start that thread because I couldn't bring myself to use the word "rounding" after that record huge rounding thread.

    But it's true, I've seen posts asking for, and have been asked myself to write code to round up on 5 for accountants.

    Leave a comment:


  • Charles Dietz
    replied
    John, You're absolutely right. This is what I did...

    LOCAL x AS EXT
    x = 3.045
    ? STR$(x, 18) --> 3.04500007629394531

    which of course is why it rounds up.

    Sorry I'm a little slow to catch on to this thread... but why would anyone want a common rounding technique when banker's rounding is less biased and preserves a sense of symmetry, and is what the ROUND function already delivers?

    Leave a comment:


  • Michael Mattias
    replied
    >Maybe the problem is that you used a double or single constant for 3.045.

    3.045 mostly likely was some kind of FLOAT (SINGLE, DOUBLE and EXT are floats), meaning by the time you see it via PRINT or STR$ or FORMAT$ it could have already been rounded.

    You can avoid floats by using CUR or CUX data types if they hold your range of values.

    MCM

    Leave a comment:


  • John Gleason
    replied
    Charles, I show ROUND() giving 3.04 for the below. Maybe the problem is that you used a double or single constant for 3.045. To be sure you get the exact EXT precision representation, use either VAL("3.045") or as Paul Purvis showed, x = 3045 / 1000.
    Code:
    #COMPILE EXE
    #DIM ALL
    
    FUNCTION PBMAIN () AS LONG
    
        LOCAL x AS EXT
        x = VAL("3.045")
        x = ROUND(x, 2)
        ? STR$(x, 18)
    
    END FUNCTION

    Leave a comment:


  • Charles Dietz
    replied
    John, with the algorithm that I posted from Wikipedia, 3.045 should not round up, but yours does... you're rounding all ending 5's up. And so does the PowerBasic ROUND function.
    Last edited by Charles Dietz; 30 Sep 2008, 04:53 PM.

    Leave a comment:


  • John Gleason
    replied
    Originally posted by Charles Dietz View Post
    I believe I would base a rounding algorithm to the Round-to-even method...
    And indeed, that is just what the PB ROUND() function does. Good ol' banker's rounding.

    (However, given the financial condition of some of these banks the past few weeks, maybe we should scrutinize this rounding philosophy. )
    Last edited by John Gleason; 30 Sep 2008, 07:27 PM. Reason: too many "should"'s were in there :)

    Leave a comment:


  • Charles Dietz
    replied
    I believe I would base a rounding algorithm to the Round-to-even method...
    (see http://en.wikipedia.org/wiki/Rounding)


    Round-to-even method

    This method is also known as unbiased rounding, convergent rounding, statistician's rounding, Dutch rounding, Gaussian rounding, or bankers' rounding. It is identical to the common method of rounding except when the digit(s) following the rounding digit starts with a five and has no non-zero digits after it. The new algorithm is:

    * Decide which is the last digit to keep.
    * Increase it by 1 if the next digit is 6 or more, or a 5 followed by one or more non-zero digits.
    * Leave it the same if the next digit is 4 or less
    * Otherwise, if all that follows the last digit is a 5 and possibly trailing zeroes; then change the last digit to the nearest even digit. That is, increase the rounded digit if it is currently odd; leave it if it is already even.

    With all rounding schemes there are two possible outcomes: increasing the rounding digit by one or leaving it alone. With traditional rounding, if the number has a value less than the half-way mark between the possible outcomes, it is rounded down; if the number has a value exactly half-way or greater than half-way between the possible outcomes, it is rounded up. The round-to-even method is the same except that numbers exactly half-way between the possible outcomes are sometimes rounded up—sometimes down.

    Although it is customary to round the number 4.5 up to 5, in fact 4.5 is no nearer to 5 than it is to 4 (it is 0.5 away from both). When dealing with large sets of scientific or statistical data, where trends are important, traditional rounding on average biases the data upwards slightly. Over a large set of data, or when many subsequent rounding operations are performed as in digital signal processing, the round-to-even rule tends to reduce the total rounding error, with (on average) an equal portion of numbers rounding up as rounding down. This generally reduces upwards skewing of the result.

    Round-to-even is used rather than round-to-odd as it reduces rounding to a final digit of 5, and so reduces the likelihood of error resulting from double rounding.

    Examples:

    * 3.016 rounded to hundredths is 3.02 (because the next digit (6) is 6 or more)
    * 3.013 rounded to hundredths is 3.01 (because the next digit (3) is 4 or less)
    * 3.015 rounded to hundredths is 3.02 (because the next digit is 5, and the hundredths digit (1) is odd)
    * 3.045 rounded to hundredths is 3.04 (because the next digit is 5, and the hundredths digit (4) is even)
    * 3.04501 rounded to hundredths is 3.05 (because the next digit is 5, but it is followed by non-zero digits)

    [edit]

    Leave a comment:


  • Heinz Salomon
    replied
    Thank you very much for your help by testing my code and publishing your tried and tested common rounding routine.

    Again, the PB forums have helped me in getting a better programmer.

    Heinz Salomon

    Leave a comment:


  • John Gleason
    replied
    Heinz, I ran some tests and your function returned occasional errors. This was the problem we experienced several months ago--an algorithm would seem good, then errors would show up. We'd post a fix, and other errors would appear, and on and on until Guinness recorded the thread as the longest in the history of the internet. So, you may want to consider roundUpV3. It is moderately fast, handles 18 digits, and has had a ton of testing by several PB'ers.

    Here are a few of the errors in roundCommon():
    Code:
    ext val     = 305836372081947.55 round digits = 1
    roundCommon = 305836372081947.5
    roundUpV3   = 305836372081947.6
    ext val     = 5052478737236737.55 round digits = 1
    roundCommon = 5052478737236737.5
    roundUpV3   = 5052478737236737.6
    ext val     = 9830868917420.82595 round digits = 4
    roundCommon = 9830868917420.8259
    roundUpV3   = 9830868917420.826
    ext val     = 10937049177568.15 round digits = 1
    roundCommon = 10937049177568.1
    roundUpV3   = 10937049177568.2
    ext val     = 5273843846123.20805 round digits = 4
    roundCommon = 5273843846123.208
    roundUpV3   = 5273843846123.2081
    ext val     = 89655156198343.4155 round digits = 3
    roundCommon = 89655156198343.415
    roundUpV3   = 89655156198343.416
    ext val     = 87225388707924.7075 round digits = 3
    roundCommon = 87225388707924.707
    roundUpV3   = 87225388707924.708
    ext val     = 4419172709427.19405 round digits = 4
    roundCommon = 4419172709427.194
    roundUpV3   = 4419172709427.1941
    ext val     = 2647177321133404.15 round digits = 1
    roundCommon = 2647177321133404.1
    roundUpV3   = 2647177321133404.2
    ext val     = 49036207327374.0515 round digits = 3
    roundCommon = 49036207327374.051
    roundUpV3   = 49036207327374.052
    ext val     = 4933067151369.03745 round digits = 4
    roundCommon = 4933067151369.0374
    roundUpV3   = 4933067151369.0375
    ext val     = 6186623783298.8815 round digits = 3
    roundCommon = 6186623783298.881
    roundUpV3   = 6186623783298.882
    ext val     = 1087885000.888785 round digits = 5
    roundCommon = 1087885000.88878
    roundUpV3   = 1087885000.88879
    ext val     = 5656671723178.33375 round digits = 4
    roundCommon = 5656671723178.3337

    Leave a comment:


  • John Gleason
    replied
    Hubert and Heinz, this is no exaggeration, several of us spent the better part of 2 months writing a common rounding routine, and again no exaggeration I've thought about it on and off for another three or so months. Here's my conclusion: This function works 100% as long as you know what your input values are, that is, they are known values or expressions, not unknown floating point expressions, and it is good to 18 digits. And by the way, 5.0000000... rounds up. Heinz, feel free to check your algo against ours for accuracy. I'll try yours too when I can. There is a version 4 too, but it is mostly in asm and a couple beta versions I didn't test fully.

    Code:
    FUNCTION roundUpV3(a AS EXT, b AS LONG) AS EXT
    '---------------------------------------------------------------------------------------
    'rounds "a" to "b" digits past decimal point, but up on 5 instead of using
    'banker's rounding. It rounds as you would expect by just looking at the decimal numbers,
    'as if the computer were a decimal computer. eg. roundUpV3(123.4545, 3) becomes 123.455.
    '---------------------------------------------------------------------------------------
       STATIC x AS EXT
       STATIC qa AS QUAD
       STATIC fillOnce, sizeA AS LONG
       DIM ten(18) AS STATIC QUAD
       IF a < 0 THEN         'handle negatives
          x = ROUND(a, b)    'in 8.04 (and probably most recent CC ver.) round to even, or banker's rounding
          IF x <= a THEN     'rounded up, so no need to go thru hoops.
             FUNCTION = x
             EXIT FUNCTION
          END IF
       ELSE
          x = ROUND(a, b)    'in 8.04 (and probably most recent CC ver.) round to even, or banker's rounding
          IF x >= a THEN     'rounded up
             FUNCTION = x
             EXIT FUNCTION
          END IF
       END IF
                             'make powers of 10 to avoid exponent calcs
       IF fillOnce = 0 THEN
       ten(00) = 1
       ten(01) = 10
       ten(02) = 100
       ten(03) = 1000
       ten(04) = 10000
       ten(05) = 100000
       ten(06) = 1000000
       ten(07) = 10000000
       ten(08) = 100000000
       ten(09) = 1000000000
       ten(10) = 10000000000
       ten(11) = 100000000000
       ten(12) = 1000000000000
       ten(13) = 10000000000000
       ten(14) = 100000000000000
       ten(15) = 1000000000000000
       ten(16) = 10000000000000000
       ten(17) = 100000000000000000
       ten(18) = 1000000000000000000
       fillOnce = 1
       END IF
                            'how big is a?
       SELECT CASE ABS(a)
          CASE < ten(00): sizeA = 01: qa = a * ten(18)
          CASE < ten(01): sizeA = 02: qa = a * ten(17)
          CASE < ten(02): sizeA = 03: qa = a * ten(16)
          CASE < ten(03): sizeA = 04: qa = a * ten(15)
          CASE < ten(04): sizeA = 05: qa = a * ten(14)
          CASE < ten(05): sizeA = 06: qa = a * ten(13)
          CASE < ten(06): sizeA = 07: qa = a * ten(12)
          CASE < ten(07): sizeA = 08: qa = a * ten(11)
          CASE < ten(08): sizeA = 09: qa = a * ten(10)
          CASE < ten(09): sizeA = 10: qa = a * ten(09)
          CASE < ten(10): sizeA = 11: qa = a * ten(08)
          CASE < ten(11): sizeA = 12: qa = a * ten(07)
          CASE < ten(12): sizeA = 13: qa = a * ten(06)
          CASE < ten(13): sizeA = 14: qa = a * ten(05)
          CASE < ten(14): sizeA = 15: qa = a * ten(04)
          CASE < ten(15): sizeA = 16: qa = a * ten(03)
          CASE < ten(16): sizeA = 17: qa = a * ten(02)
          CASE < ten(17): sizeA = 18: qa = a * ten(01)
          CASE ELSE
             FUNCTION = a
             EXIT FUNCTION             'exit because a is too big to round
       END SELECT
       IF sizeA + b > 18 THEN
          FUNCTION = a
          EXIT FUNCTION                'exit because round digits too big
       END IF
    
       qa = qa \ ten(18 - (sizeA + b)) 'make whole EXT effectively an integer
       sizeA = qa MOD 10
       IF sizeA = 5 THEN               'is last digit a 5?
          FUNCTION = ((qa + 10) \ 10) / ten(b)'if so round up prev. digits and replace decimal point in correct position
       ELSEIF sizeA = -5 THEN          'or -5
          FUNCTION = ((qa - 10) \ 10) / ten(b)'if so round up prev. digits and replace decimal point in correct position
       ELSE
          FUNCTION = x
       END IF
    
    END FUNCTION

    Leave a comment:


  • Hubert Brandel
    replied
    Hi,

    now I am confused ... the link say

    common rounding
    Code:
    if  digit >= 5 then 
       UP
    else 
       DOWN
    endif
    The

    Round-to-even method
    Code:
    if  digit > 5 then 
       UP
    elseif digit < 5 then 
       DOWN
    else
       chouse the next evens ... 
       5.0000000... DOWN
       5.0000001... UP
    endif
    did you mean >= or realy >5 ?
    Last edited by Hubert Brandel; 29 Sep 2008, 11:01 AM.

    Leave a comment:


  • George Bleck
    replied
    I live in the US and I always learned it as >5 is round up.

    Leave a comment:


  • Hubert Brandel
    replied
    Hi,

    I do not have a better function, but a question on rounding in USA !

    here in germany wie round down (like int(x) when x>0)
    from 0 to 4 down, from 5 to 9 up

    -> 2 digits for money:

    456.3649 -> 456.36
    456.3650 -> 456.37

    -56.3649 -> -56.36
    -56.3650 -> -56.37

    a few days ago someone told me, that in the USA rounding would be different:

    0 to 5 down, 6 to 9 up - is this true ?

    Bye
    Hubert
    Last edited by Hubert Brandel; 29 Sep 2008, 10:49 AM. Reason: writing errors

    Leave a comment:


  • Heinz Salomon
    started a topic Common Rounding

    Common Rounding

    I wrote a function which performs "common rounding" on an extended precision value:

    Code:
    FUNCTION RoundCommon##(rdVal##, BYVAL rlDec&)
      LOCAL lShift&, lSign&, qCalc&&
      lShift& =  CHOOSE&(rlDec&, 100, 1000, 10000, 100000, 1000000)
      lSign&  =  SGN(rdVal##)
      qCalc&& =  CQUD(FIX(rdVal## * lShift&))
      qCalc&& =  ABS(qCalc&&)
      qCalc&& =  qCalc&& + 5
      qCalc&& =  qCalc&& * lSign&
      qCalc&& =  qCalc&& \ 10
      lShift& =  CHOOSE&(rlDec&, 10, 100, 1000, 10000, 100000)
      FUNCTION = CEXT(qCalc&& / lShift&)
    END FUNCTION
    Do you see any obvious optimization potential (apart from writing this code in assembly language)?

    Thank you for any suggestions!

    Heinz Salomon
    Last edited by Heinz Salomon; 29 Sep 2008, 10:31 AM. Reason: signature
Working...
X