Announcement

Collapse
No announcement yet.

How to change rounding?

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

  • How to change rounding?

    I would like to test some numeric code using different rounding.

    rounding:
    round ties to even
    round to nearest
    round toward 0
    round toward +inf
    round toward -inf

    What commands do I need to use to change rounding?

  • #2
    The only rounding function provided by the compiler is ROUND(). If you don't like the way that rounds, there are no other commands to use: you'll have to write your own procedure(s) to round the way you want to round.

    I suppose you could say CEIL() does a form of rounding. INT() and FIX() also do 'sort of' rounding. FORMAT$/USING$ round under some conditions, but I would not try using these DISPLAY functions to perform mathematical rounding.
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Unfortunately Math is not my strong suit (unless I know what the math is [b]REALLY[/bold] doing) and this seems to be one area that I TOTALLY mis-read the documentation (vs what I was taught in school)

      According to the docs:
      Rounding is done according to the "banker's rounding" principle: if the fractional digit being rounded off is exactly five, with no trailing digits, the number is rounded to the nearest even number. This provides better results, on average, than the simple "round up at five" approach.
      Odd...I was always taught, round up if even or over .5 and down if under

      On that point I was also taught that an integer was a WHOLE NUMBER not a fraction, not a decimal but a WHOLE NUMBER!!!!!!

      Because my weakness in Math, I have always followed the rule of
      "Thou Shalt not Round till presenting the answer"
      meaning...you multiply all the decimal points until you are ready to present a shorter version. For example: If google describes PI as pi = 3.14159265 and I use that as my base number of digits, then I should NEVER perform a change by assuming 3.14

      Anyways...the word [b]Integer[/b} is what always threw me off till tonight (MCM will love this because my example TAUGHT me something I never knew)

      Just because my code I wanted a whole number (INTEGER), I would add 0.5 to the value and then round (not realizing a 2.7 + 0.5 and round would equal 3, but because I went 0 digits, came out correct because I thought PB meant I would get 4 according to bankers rules)

      Here is what I came up with as my test
      Code:
      #COMPILE EXE
      #DIM ALL
      '**********************************************************************************************************
      '*** CNDS (Cliff Nichols "Doh" Syndrome)
      '*** Rounding explaination made clear
      '**********************************************************************************************************
      '*** PB Help File
      '***  Rounding is done according to the "banker's rounding" principle:
      '***       If the fractional digit being rounded off is exactly five,
      '***       with no trailing digits, the number is rounded to the nearest even number.
      '***       This provides better results, on average, than the simple "round up at five" approach.
      '**********************************************************************************************************
      '??? I had no CLUE what a banker does
      '??? but it would explain our taxes *LOL*, or how a bank accrues your interest
      '**********************************************************************************************************
      ' Rounding by five is what we do by nature to the next nearest digit, but I thought PB meant something like
      ' 3.15 would round to 4
      ' 2.95 would round to 2
      ' and BOOOOY by this example I was wrong, by TOTALLY mis-reading the documentation.
      ' The below should show my knowledge of the situation as it is.... CCN 06-05-2008
      FUNCTION PBMAIN () AS LONG
      '*** PB EXAMPLE
           MSGBOX "ROUND(0.5, 0) = " + TRIM$(STR$(ROUND(0.5, 0)))                 ' 0  'Ok this is as I would see it (round .5 up)
           MSGBOX "ROUND(1.5, 0) = " + TRIM$(STR$(ROUND(1.5, 0)))                 ' 2  'Ok this is as I would see it (round .5 up)
           MSGBOX "ROUND(2.5, 0) = " + TRIM$(STR$(ROUND(2.5, 0)))                 ' 2  'Bankers round down to 2??? OMG!!!...(later note see my turn)
           MSGBOX "ROUND(2.51, 0) = " + TRIM$(STR$(ROUND(2.51, 0)))               ' 3  'Ok this is as I would see it (round .5 up)
      '*** What I misunderstood was the decimal point are what comes into play rather than "Integer" values (which to me are "Whole Numbers")
      '*** My Example
      MSGBOX "Now its my turn"
           MSGBOX "ROUND(3.2, 0) = " + TRIM$(STR$(ROUND(3.2, 0)))                 ' 3 - due to round to nearest even, zero decimal points
           MSGBOX "ROUND(3.5, 0) = " + TRIM$(STR$(ROUND(3.5, 0)))                 ' 3 - due to round to nearest even, zero decimal points
           MSGBOX "ROUND(3.11, 1) = " + TRIM$(STR$(ROUND(3.11, 1)))               ' 3 - due to round to nearest even, one decimal points
           MSGBOX "ROUND(3.15, 1) = " + TRIM$(STR$(ROUND(3.15, 1)))               ' 3 - due to round to nearest even, one decimal points
           MSGBOX "ROUND(3.17, 1) = " + TRIM$(STR$(ROUND(3.17, 1)))               ' 3 - due to round to nearest even, one decimal points
      END FUNCTION
      All in all I think its confusion of language vs what it really means
      Engineer's Motto: If it aint broke take it apart and fix it

      "If at 1st you don't succeed... call it version 1.0"

      "Half of Programming is coding"....."The other 90% is DEBUGGING"

      "Document my code????" .... "WHYYY??? do you think they call it CODE? "

      Comment


      • #4
        Using the 2-bit rounding control field built in to the processor (which is accessible by the asm commands !FSTCW and !FNSTCW that PB so kindly includes ), you can:
        1) round to nearest (ties go to even) < default
        2) round down -infin
        3) round up +infin
        4) truncate (chop) < the FIX command can do this

        To round toward 0 this look like it works:
        Code:
        #COMPILE EXE
        #DIM ALL
        
        FUNCTION PBMAIN () AS LONG
            LOCAL x, y AS EXT
            x = -1.5
            y =  1.5
            x = CEIL(x)         '< really a chop rather then a round
            y = -CEIL(-y)
            ? STR$(x) & $CRLF & STR$(y)
        
            x = -1.4
            y =  1.4
            x = ROUND(x, 0)     '< this is a round
            y = ROUND(y, 0)     'addendum2: removed unnecessary minus signs
            ? STR$(x) & $CRLF & STR$(y)
        
        END FUNCTION
        Last edited by John Gleason; 5 Jun 2008, 07:41 PM. Reason: Was showing only a chop, so added a round example

        Comment


        • #5
          I was looking at some strange results of floating-point math. I am thinking that double rounding has taken place. Round to zero (truncation) would work better for what I am doing.

          I saw a recomendation by a prof at Berkley that suggested trying the program with different rounding. This is a test of rounding sensitivity.

          I could not find anything in the help files to invoke a rounding change for floating-point math.

          I think it will require assembly code to change.

          Comment


          • #6
            Converting from one number form (i.e., Single) to another (i.e. Integer) can cause rounding. Divide operations on integers can result in rounding. Banker's rounding methods are not based on sound engineering principles, but are a means of trying to cancel out interest errors when computing interest on electronically transferred funds. If one side rounds up, the other side must round down, and banker's rules decides which side does what and when. It's a trick to make sure that the books will balance.

            Rounding does not necessarily limit itself to integers, but to any numeric quantity that only allows a fixed number of digits for fractional representation. To represent 1/3rd, we might write 0.3333. On the other hand, to represent 2/3rds, we would instead write 0.6667. The last position represents a rounding error if the decimal quantity does not exactly match what it represents.

            Using the INT() command. you can forceably drop any fractional part. It only works with integers. But if n=1/3, with n being a floating point variable, and I want it to round off at the fourth decimal place, I can write n=INT(n*10000+.5)/10000, and the displayed results would be .3333, or if it were set to 2/3, the displayed results would be .6667. then I have defined for myself where the rounding is to take place and what rounding rule to employ.
            Last edited by Donald Darden; 5 Jun 2008, 08:29 PM.

            Comment


            • #7
              Tom,
              if your calculations are simple then just plug them into this program in place of my test code and it'll show the effect of the 4 different roundings.

              Paul
              Code:
              'PBCC4 program
              'show the effect of the 4 different FPU rounding schemes on calculations
              #COMPILE EXE
              #DIM ALL
              #REGISTER NONE
                                 
              FUNCTION PBMAIN () AS LONG
              
              LOCAL OriginalControlWord, NewControlWord, mask AS INTEGER    'control word is a 16 bit register
              LOCAL sum AS EXT
              LOCAL r,cnt AS LONG
              DIM mask(4) AS INTEGER
              
              'choose the mask to suit your rounding requirements
              mask(1)=&h0000   'round to nearest (even on tie)
              mask(2)=&h0400   'round toward -infinty
              mask(3)=&h0800   'round toward +infinity
              mask(4)=&h0c00   'round toward zero
              
              !fstcw OriginalControlWord  'save the original control word
              
              FOR cnt=1 TO 4
                  mask=mask(cnt)
                  !mov ax,OriginalControlWord 'get the control word
                  !and ax,&hf3ff              'mask off the 2 rounding bits
                  !or ax,mask                 'set the rounding bits as required
                  !mov NewControlWord,ax      'save the new control word
                  !fldcw NewControlWord       'set the FPU to use the new control word
              
                  'my test codegoes here
                  sum=0
                  FOR r = 1 TO 10000000
                      sum=sum+1/r
                  NEXT
                  PRINT "mask="RIGHT$("0000"+HEX$(mask(cnt)),4);" sum="STR$(sum,18)
              
              NEXT
              
              !fldcw OriginalControlWord  'restore the original control word
              
              WAITKEY$
              END FUNCTION
              Last edited by Paul Dixon; 5 Jun 2008, 08:37 PM.

              Comment


              • #8
                Thankyou Paul Dixon.

                Comment


                • #9
                  FIX truncates.

                  For "Round-Half-Up", here is something I put together.

                  Round-Half-Up
                  Last edited by Greg Lyon; 15 Jun 2008, 09:30 PM. Reason: Moved the code to the Source Code Forum

                  Comment


                  • #10
                    Round-Half-Up ASM version.
                    Last edited by Greg Lyon; 15 Jun 2008, 09:39 PM. Reason: Moved the code to the Source Code Forum

                    Comment


                    • #11
                      It can't hurt to be a bite more precise about what you want.

                      There is a difference between "rounding a numeric variable used for subsequent operations" and "displaying a rounded current value of a variable;" and 'when you round' is also a factor.

                      eg.. if rounding to 2 decimals for subsequent calculations, 100 units at a price of 1/3 dollar would extend to $33.00; versus an extended price of $33.33 if rounding were delayed until AFTER the mutiplication.


                      I actually had this come up once in a very real situation... my client was sending electronic invoices to his customer; the item was priced at something with pennies in the price per 1000 units. A non-integral number of 1000s was billed and several line items of similarly-priced items were on the invoice.

                      The customer did not round until the totalling; we rounded at the line item level. This resulted in a sixteen cent difference in the invoice total.

                      Sixteen cents is no big deal, right? Well, it was enough for the customer to refuse to pay a $100,000+ invoice for an extra week until the "error" was corrected.

                      MCM
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        After rereading Tom Ulrich's posts I imagine Paul Dixon's code is probably what he was after. I decided to move the code I posted to the Source Code Forum. Someone might find it useful.

                        Comment


                        • #13
                          NBRound - Not Banker's Round

                          Posted here in the Source Code Forum:
                          PowerBASIC and related source code. Please do not post questions or discussions, just source code.


                          This function can replace the built in PB function by handling the specified number of decimal places.

                          The idea to multiply by two, add the 0.5, and then divide by two is from this paper by Laurent de Soras:


                          Regards,
                          Colin Schmidt

                          Comment


                          • #14
                            The idea to multiply by two, add the 0.5, and then divide by two is from this paper by Laurent de Soras:
                            http://ldesoras.free.fr/doc/articles/rounding_en.pdf
                            It's pretty much the same method I used in the assembly code I posted for Round-Half-Up, and I did get the idea from the paper you referenced.

                            Comment

                            Working...
                            X