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 2020 - 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:
---
----------------------------------------------------------------------