faqts : Computers : Programming : Languages : Bbcbasic

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

0 of 4 people (0%) answered Yes
Recently 0 of 4 people (0%) answered Yes

Entry

BBCBASIC: Windows: Math: Number: Roundoff: How to round off a number to certain number of digits?

Aug 6th, 2006 17:39
Knud van Eeden,


----------------------------------------------------------------------
--- Knud van Eeden --- 06 August 2006 - 01:47 pm ---------------------

BBCBASIC: Windows: Math: Number: Roundoff: How to round off a number 
to certain number of digits?

---

Method: Use the special variable @%

---

It is a global solution, as it has influence on the print output of all
variables.

===

Works for BBCBASIC only.

---

You can search in the help of BBCBASIC for Windows
for the word

 PRINT

and then on that page press the keys

 <CTRL><F>

to search further for

 @%

---

After setting this variable @% all calculations will be printed
with that amount of roundoff digits after the decimal point.
So it is a global solution.

---

Setting this variable @% only influences the printed output
(that is the amount of printed decimals after the decimal point),
it will not influence the internal calculations of BBCBASIC.

===

The default value for this (hexadecimal) variable is

 @% = &090A

===

You can use the first 2 digits to set the number of roundoff decimals.

e.g.

--- cut here: begin --------------------------------------------------

*FLOAT64
:
myOldValue% = @% : REM store old value to put it back later
:
@% = &090A : REM this is the default value for @%
PRINT 1/3 : REM gives 0.333333333
:
@% = &010A : REM 1 digit roundoff
PRINT 1/3 : REM gives 0.3
:
@% = &020A : REM 2 digits roundoff
PRINT 1/3 : REM gives 0.33
:
@% = &030A : REM 3 digits roundoff
PRINT 1/3 : REM gives 0.333
:
@% = &040A : REM 4 digits roundoff
PRINT 1/3 : REM gives 0.3333
:
@% = &050A : REM 5 digits roundoff
PRINT 1/3 : REM gives 0.33333
:
@% = &060A : REM 6 digits roundoff
PRINT 1/3 : REM gives 0.333333
:
@% = &070A : REM 7 digits roundoff
PRINT 1/3 : REM gives 0.3333333
:
@% = &080A : REM 8 digits roundoff
PRINT 1/3 : REM gives 0.33333333
:
@% = &090A : REM 9 digits roundoff
PRINT 1/3 : REM gives 0.333333333
:
@% = &0A0A : REM 10 digits roundoff
PRINT 1/3 : REM gives 0.3333333333
:
@% = &0B0A : REM 11 digits roundoff
PRINT 1/3 : REM gives 0.33333333333
:
@% = &0C0A : REM 12 digits roundoff
PRINT 1/3 : REM gives 0.333333333333
:
@% = &0D0A : REM 13 digits roundoff
PRINT 1/3 : REM gives 0.3333333333333
:
@% = &0E0A : REM 14 digits roundoff
PRINT 1/3 : REM gives 0.33333333333333
:
@% = &0F0A : REM 15 digits roundoff
PRINT 1/3 : REM gives 0.333333333333333
:
@% = &100A : REM 16 digits roundoff
PRINT 1/3 : REM gives 0.3333333333333333
:
@% = &110A
PRINT 1/3 : REM gives 0.3333333333333333
:
@% = &120A
PRINT 1/3 : REM gives 0.3333333333333333
:
@% = myOldValue% : REM restore the old value
:
END

--- cut here: end ----------------------------------------------------

The maximum number of digits, when using 64 bits floats, is 16 digits
after the decimal point (as shown by the last 3 examples above, where
you ask for respectively 16, 17 or 18 digits after the decimal point,
but only 16 are shown all the time).

===

If you look in the original Acorn BBCBASIC user guide p. 70,
it shows:

@% = &20209

is made up of parts:

& = means hexadecimal numbers follow

2 = format number 2, i.e. fixed number of decimal places
      (it can be 0, 1 or 2)

02 = 2 decimal places

09 = field width of 9 characters

e.g.

@% = &20309 would give format 2, 3 decimal places and field width of 9 
characters.

It is thus clear that the basic 'atom' in this number is the
hexadecimal number. Therefor this conversion STR$ ~( decimalI% ) is a
very natural one I believe, and should be part of any simple conversion
routine

E.g.

--- cut here: begin --------------------------------------------------

 PRINT FNVariableSetPrintFormat( 2, 3, 9 )
 PRINT &20309
 END
 :
 :
 :
 DEF FNVariableSetPrintFormat( formatI%, decimalPlacesI%,fieldWidthI% )
 LOCAL format$
 LOCAL decimal$
 LOCAL fieldWidth$
 format$ = STR$ ~(  formatI% )
 decimal$ = STR$ ~( decimalPlacesI% )
 fieldWidth$ = STR$ ~( fieldWidthI%  )
 IF LEN decimal$ = 1 THEN decimal$ = "0" + decimal$
 IF LEN fieldWidth$ = 1 THEN fieldWidth$ = "0" + fieldWidth$
 = EVAL( "&" + format$ + decimal$ + fieldWidth$ )
 :

--- cut here: end ----------------------------------------------------

You see that this is a very clean, general and straightforward 
solution,
by staying in the hexadecimal field.

E.g.

Store the old value of @%, set the new value,
do something (e.g. print some rounded off numbers),
then put back the old value of @%.

--- cut here: begin --------------------------------------------------

oldvalueI% = @%
@% = FNVariableSetPrintFormat( 2, 3, 9 )
PRINT 1/3
@% = oldvalueI%
END

--- cut here: end ----------------------------------------------------

===

Method: use a function which performs the roundoff

Advantage: general approach, a similar function should work in almost
           any computer language

It is a local solution, as it has only influence on the variable
on which it is used.

As a 'one' liner

digitstotalI% = 3 : REM choose here the total amount of roundoff digits

x = 2.12345678

xroundoff = INT( ( x + 0.5 / ( 10^digitstotalI% ) ) * ( 
10^digitstotalI% ) ) / ( 10^digitstotalI% )

PRINT x, xroundoff : REM gives 2.12345678 2.123

END

:

===

What this general formula does is

1. -Rounding off the last round off digit to above
    (this by adding 0.5 at the digit one position after your last round
     off digit)

    E.g. given showing this for a round off of 3 digits,
    with a given number

     x = 2.12345678
             ^^
             ||
              |

    You want to round off at the 3th position.
    Thus you add 0.5 / 1000, which is

     0.0005

    That gives

     2.12345678 +
     0.0005
     ----------
         ^^
         ||
          |

    which is

     2.12395678
         ^^
         ||
          |

    Now adding this 0.5 / 1000 will add actually at the digit
    one after the last round off digit position
    (because dividing 5 by 1000 puts by definition 3 zeroes
     after the decimal point, so shifting the 5 to the third plus
     one position, or thus 3+1 = 4th position after the decimal point)

    If this digit is between 0 and 4 it will thus become now between
    0+5 and 4+5, or thus between 5 and 9, and it will have no influence
    on the digit (=your last roundoff digit) before.

    But is this digit between 5 and 9, it will thus give
    between 5+5 and 9+5, thus between 10 and 14, and this
    will influence the digit before it, by adding
    1 to that (=your last roundoff digit).

    Note:

     E.g. should you have chosen instead such a value for x, e.g.

      x = 2.12355678
              ^^
              ||
               |

     then by adding 0.0005 to it, it would have become

     2.12355678 +
     0.0005
     ----------
         ^^
         ||
          |

     which is

     2.12405678
         ^^
         ||
          |

     you see thus that there has been added 1 to that digit at the 3th
     position after the decimal point (3 + 1 = 4, instead of 3)

    Thus by this adding you basically round off the last roundoff digit
    (in this example the 3th digit after the decimal point) to above.

2. -It then moves the decimal point as much to the right as you have
    round off decimals

    E.g. in this example you move your decimal point
    3 positions to the right by multiplying with 1000

     2.12395678 * 1000 = 2123.95678
      ^                      ^
      |                      |

3. -You then remove any of the decimals after the
    decimal point, by taking the integer part of it

     INT( 2123.95678 ) = 2123.
               ^^^^^
               |||||
               take this away

4. -You finally move the decimal point back 3 positions
    by dividing by 1000

     2123. / 1000 = 2.123
         ^           ^
         |           |

===

Note

If you want to use 2 digits after the decimal point you
can replace in the above formula the constant part 10^digitstotalI% by
10^2, which is 100, and replace this in the general formula.

--- cut here: begin --------------------------------------------------

x = 2.12345678

xroundoff = INT( ( x + 0.5 / 100 ) * 100 ) / 100

PRINT x, xroundoff : REM gives 2.12345678 2.12

END

--- cut here: end ----------------------------------------------------

If you want to use 3 digits after the decimal point you
can replace in the above formula the constant part 10^digitstotalI% by
10^3, which is 1000, and replace this in the general formula.

--- cut here: begin --------------------------------------------------

x = 2.12345678

xroundoff = INT( ( x + 0.5 / 1000 ) * 1000 ) / 1000

PRINT x, xroundoff : REM gives 2.12345678 2.123

END

--- cut here: end ----------------------------------------------------

If you want to use 4 digits after the decimal point you
can replace in the above formula the constant part 10^digitstotalI% by
10^4, which is 10000, and replace this in the general formula.

--- cut here: begin --------------------------------------------------

x = 2.12345678

xroundoff = INT( ( x + 0.5 / 10000 ) * 10000 ) / 10000

PRINT x, xroundoff : REM gives 2.12345678 2.1235

END

--- cut here: end ----------------------------------------------------

More elaborate:

--- cut here: begin --------------------------------------------------

PRINT; FNNumberDecimalRoundR( 0.123456, 3 ) : REM gives 0.123

PRINT; FNNumberDecimalRoundR( 0.123456, 5 ) : REM gives 0.12346

:

:

:

REM library: math: number: ROUND: to digits after decimal

DEF FNNumberDecimalRoundR( numberR, digitstotalI% )

LOCAL powerI%

powerI% = FNMathGetPowerR( 10, digitstotalI% )

= FNMathGetNumberIntegerPartI( ( numberR + 0.5 / powerI% ) * 
powerI% ) / powerI%

:

REM library: math: power (with test for 0^power): real number as result

DEF FNMathGetPowerR( xR, powerR )

= FNMathGetPowerF( xR, powerR )

:
REM library: math: number: get whole part of a fraction

DEF FNMathGetNumberIntegerPartI( xR )

= INT( xR )

:

REM library: math: power (with test for 0^power): float

DEF FNMathGetPowerF( xR, powerR )

IF xR = 0 THEN = 0

= xR ^ powerR


--- cut here: end ----------------------------------------------------

===

Method: Using the function FNusing

You can find this function in the directory

 LIB

of your BBCBASIC for Windows installation

Documentation you can find here

http://www.bbcbasic.co.uk/products/bbcwin/manual/bbcwing.html#fnusing

===

Internet: see also:

---



----------------------------------------------------------------------