ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 12.06.2025

Просмотров: 8219

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

© MCS Electronics, 1995-2007

Floating-Point Complications

Every decimal integer can be exactly represented by a binary integer; however, this is not true for fractional numbers. In fact, every number that is irrational in base 10 willalso be irrational in any system with a base smaller than 10.

For binary, in particular, only fractional numbers that can be represented in the formp/q, where q is an integer power of 2, can be expressed exactly, with a finite number of bits.

Even common decimal fractions, such as decimal 0.0001, cannot be represented exactly in binary. (0.0001 is a repeating binary fraction with a period of 104 bits!)

This explains why a simple example, such as the following

SUM = 0

FOR I% = 1 TO 10000

SUM = SUM + 0.0001

NEXT I%

PRINT SUM ' Theoretically = 1.0.

will PRINT 1.000054 as output. The small error in representing 0.0001 in binary propagates to the sum.

For the same reason, you should always be very cautious when making comparisons on real numbers. The following example illustrates a common programming error:

item1# = 69.82#

item2# = 69.20# + 0.62#

IF item1# = item2# then print "Equality!"

This will NOT PRINT "Equality!" because 69.82 cannot be represented exactly in binary, which causes the value that results from the assignment to be SLIGHTLY different (in binary) than the value that is generated from the expression. In practice, you should always code such comparisons in such a way as to allow for some tolerance.

General Floating-Point Concepts

It is very important to realize that any binary floating-point system can represent only a finite number of floating-point values in exact form. All other values must be approximated by the closest represent able value. The IEEE standard specifies the method for rounding values to the "closest" represent able value. BASCOM supports the standard and rounds according to the IEEE rules.

Also, keep in mind that the numbers that can be represented in IEEE are spread out over a very wide range. You can imagine them on a number line. There is a high density of represent able numbers near 1.0 and -1.0 but fewer and fewer as you go towards 0 or infinity.

The goal of the IEEE standard, which is designed for engineering calculations, is to maximize accuracy (to get as close as possible to the actual number). Precision refers to the number of digits that you can represent. The IEEE standard attempts to balance the number of bits dedicated to the exponent with the number of bits used for the fractional part of the number, to keep both accuracy and precision within acceptable limits.

page -177-

© MCS Electronics, 1995-2007

IEEE Details

Floating-point numbers are represented in the following form, where [exponent] is the binary exponent:

X = Fraction * 2^(exponent - bias)

[Fraction] is the normalized fractional part of the number, normalized because the exponent is adjusted so that the leading bit is always a 1. This way, it does not have to be stored, and you get one more bit of precision. This is why there is an implied bit. You can think of this like scientific notation, where you manipulate the exponent to have one digit to the left of the decimal point, except in binary, you can always manipulate the exponent so that the first bit is a 1, since there are only 1s and 0s.

[bias] is the bias value used to avoid having to store negative exponents.

The bias for single-precision numbers is 127 and 1023 (decimal) for double-precision numbers.

The values equal to all 0's and all 1's (binary) are reserved for representing special cases. There are other special cases as well, that indicate various error conditions.

Single-Precision Examples

2 = 1 * 2^1 = 0100 0000 0000 0000 ... 0000 0000 = 4000 0000 hex

Note the sign bit is zero, and the stored exponent is 128, or

100 0000 0 in binary, which is 127 plus 1. The stored mantissa is

(1.) 000 0000 ... 0000 0000, which has an implied leading 1 and

binary point, so the actual mantissa is 1.

-2 = -1 * 2^1 = 1100 0000 0000 0000 ... 0000 0000 = C000 0000 hex

Same as +2 except that the sign bit is set. This is true for all

IEEE format floating-point numbers.

4 = 1 * 2^2 = 0100 0000 1000 0000 ... 0000 0000 = 4080 0000 hex

Same mantissa, exponent increases by one (biased value is 129, or

100 0000 1 in binary.

6 = 1.5 * 2^2 = 0100 0000 1100 0000 ... 0000 0000 = 40C0 0000 hex

Same exponent, mantissa is larger by half -- it's

(1.) 100 0000 ... 0000 0000, which, since this is a binary fraction, is 1-1/2 (the values of the fractional digits are 1/2, 1/4, 1/8, etc.).

1 = 1 * 2^0 = 0011 1111 1000 0000 ... 0000 0000 = 3F80 0000 hex

page -178-


© MCS Electronics, 1995-2007

Same exponent as other powers of 2, mantissa is one less than

2 at 127, or 011 1111 1 in binary.

.75 = 1.5 * 2^-1 = 0011 1111 0100 0000 ... 0000 0000 = 3F40 0000 hex

The biased exponent is 126, 011 1111 0 in binary, and the mantissa

is (1.) 100 0000 ... 0000 0000, which is 1-1/2.

2.5 = 1.25 * 2^1 = 0100 0000 0010 0000 ... 0000 0000 = 4020 0000 hex

Exactly the same as 2 except that the bit which represents 1/4 is

set in the mantissa.

0.1 = 1.6 * 2^-4 = 0011 1101 1100 1100 ... 1100 1101 = 3DCC CCCD hex

1/10 is a repeating fraction in binary. The mantissa is just shy of 1.6, and the biased exponent says that 1.6 is to be divided by 16 (it is 011 1101 1 in binary, which is 123 n decimal). The true exponent is 123 - 127 = -4, which means that the factor by which to multiply is 2**-4 = 1/16. Note that the stored mantissa is rounded up in the last bit. This is an attempt to represent the unrepresentable number as accurately as possible. (The reason that 1/10 and 1/100 are not exactly representable in binary is similar to the way that 1/3 is not exactly representable in decimal.)

0 = 1.0 * 2^-128 = all zeros -- a special case.

Other Common Floating-Point Errors

The following are common floating-point errors:

1. Round-off error

This error results when all of the bits in a binary number cannot be used in a calculation.

Example: Adding 0.0001 to 0.9900 (Single Precision)

Decimal 0.0001 will be represented as:

(1.)10100011011011100010111 * 2^(-14+Bias) (13 Leading 0s in Binary!)

0.9900 will be represented as:

(1.)11111010111000010100011 * 2^(-1+Bias)

Now to actually add these numbers, the decimal (binary) points must be aligned. For this they must be Unnormalized. Here is the resulting addition:

.000000000000011010001101 * 2^0 <- Only 11 of 23 Bits retained

page -179-

© MCS Electronics, 1995-2007

+.111111010111000010100011 * 2^0

________________________________

.111111010111011100110000 * 2^0

This is called a round-off error because some computers round when shifting for addition. Others simply truncate. Round-off errors are important to consider whenever you are adding or multiplying two very different values.

2. Subtracting two almost equal values

.1235 -.1234

_____

.0001

This will be normalized. Note that although the original numbers each had four significant digits, the result has only one significant digit.

3. Overflow and underflow

This occurs when the result is too large or too small to be represented by the data type.

4. Quantizing error

This occurs with those numbers that cannot be represented in exact form by the floating-point standard.

Rounding

When a Long is assigned to a single, the number is rounded according to the rules of the IEEE committee.

For explanation: 1.500000 is exact the middle between 1.00000 and 2.000000. If x.500000 is always rounded up, than there is trend for higher values than the average of all numbers. So their rule says, half time to round up and half time to round down, if value behind LSBis exact ..500000000.

The rule is, round this .500000000000 to next even number, that means if LSB is 1 (half time) to round up, so the LSB is going to 0 (=even), if LSB is 0 (other half time) to round down, that means no rounding.

This rounding method is best since the absolute error is 0.

You can override the default IEEE rounding method by specifying the $LIBLONG2FLOAT.LBX library which rounds up to the next number. This is the method used up to 1.11.7.4 of the compiler.

Double

page -180-


© MCS Electronics, 1995-2007

The double is essential the same as a single. Except the double consist of 8 bytes instead of 4. The exponent is 11 bits leaving 52 bits for the mantissa.

Arrays

An array is a set of sequentially indexed elements having the same type. Each element of an array has a unique index number that identifies it. Changes made to an element of an array do not affect the other elements.

The index must be a numeric constant, a byte, an integer, word or long. The maximum number of elements is 65535.

The first element of an array is always one. This means that elements are 1-based.

Arrays can be used on each place where a 'normal' variable is expected.

Example:

'create an array named a, with 10 elements (1 to 10) Dim A(10) As Byte

'create an integer Dim C As Integer 'now fill the array For C = 1 To 10 'assign array element A(c)= C

' print it Print A(c) Next

'you can add an offset to the index too C = 0

A(c + 1)= 100

Print A(c + 1) End

Strings

A string is used to store text. A string must be dimensioned with the length specified.

DIM S as STRING * 5

Will create a string that can store a text with a maximum length of 5 bytes. The space used is 6 bytes because a string is terminated with a null byte.

To assign the string: s = "abcd"

To insert special characters into the string : s= "AB{027}cd"

The {ascii} will insert the ASCII value into the string.

The number of digits must be 3. s = "{27} will assign "{27}" to the string instead of escape character 27!

page -181-


© MCS Electronics, 1995-2007

Casting

In BASCOM-AVR when you perform operations on variables they all must be of the same data type.

long = long1 * long2 ' for example

The assigned variables data type determines what kind of math is performed. For example when you assign a long, long math will be used.

If you try to store the result of a LONG into a byte, only the LSB of the LONG will be stored into the BYTE.

Byte = LONG

When LONG = 256 , it will not fit into a BYTE. The result will be 256 AND 255 = 0.

Of course you are free to use different data types. The correct result is only guaranteed when you are using data types of the same kind or that result always can fit into the target data type.

When you use strings, the same rules apply. But there is one exception:

Dim b as Byte

b = 123 ' ok this is normal

b = "A" ' b = 65

When the target is a byte and the source variable is a string constant denoted by "", the ASCII value will be stored in the byte. This works also for tests :

IF b = "A" then ' when b = 65

END IF

This is different compared to QB/VB where you can not assign a string to a byte variable.

SINGLE CONVERSION

When you want to convert a SINGLE into a byte, word, integer or long the compiler will automatic convert the values when the source string is of the SINGLE data type.

integer = single

You can also convert a byte, word, integer or long into a SINGLE by assigning this variable to a SINGLE.

single = long

Mixing ASM and BASIC

BASCOM allows you to mix BASIC with assembly.

This can be very useful in some situations when you need full control of the generated code.

Almost all assembly mnemonics are recognized by the compiler. The exceptions are : SUB, SWAP, CALL and OUT. These are BASIC reserved words and have priority over the ASM

page -182-

© MCS Electronics, 1995-2007

mnemonics. To use these mnemonics precede them with the ! - sign.

For example :

Dim a As Byte At &H60 'A is stored at location &H60 Ldi R27 , $00 'Load R27 with MSB of address

Ldi R26 , $60 'Load R26 with LSB of address Ld R1, X 'load memory location $60 into R1 !SWAP R1 'swap nibbles

As you can see the SWAP mnemonic is preceded by a ! sign.

Another option is to use the assembler block directives:

$ASM

Ldi R27 , $00 'Load R27 with MSB of address Ldi R26 , $60 'Load R26 with LSB of address Ld R1, X 'load memory location $60 into R1 SWAP R1 'swap nibbles

$END ASM

A special assembler helper function is provided to load the address into the register X or Z. Y can may not be used because it is used as the soft stack pointer.

Dim A As Byte 'reserve space

LOADADR a, X 'load address of variable named A into register pair X

This has the same effect as :

Ldi R26 , $60 'for example !

Ldi R27, $00 'for example !

Some registers are used by BASCOM

R4 and R5 are used to point to the stack frame or the temp data storage R6 is used to store some bit variables:

R6 bit 0 = flag for integer/word conversion

R6 bit 1 = temp bit space used for swapping bits R6 bit 2 = error bit (ERR variable)

R6 bit 3 = show/noshow flag when using INPUT statement R8 and R9 are used as a data pointer for the READ statement.

All other registers are used depending on the used statements.

To Load the address of a variable you must enclose them in brackets.

Dim B As Bit

Lds R16, {B} 'will replace {B} with the address of variable B

To refer to the bitnumber you must precede the variable name by BIT.

Sbrs R16 , BIT.B 'notice the point!

Since this was the first dimensioned bit the bit number is 7. Bits are stored in bytes and the first dimensioned bit goes in the LS bit.

To load an address of a label you must use :

LDI ZL, Low(lbl * 1)

page -183-


© MCS Electronics, 1995-2007

LDI ZH , High(lbl * 1)

Where ZL = R30 and may be R24, R26, R28 or R30

And ZH = R31 and may be R25, R27, R29 or R31.

These are so called register pairs that form a pointer.

When you want to use the LPM instruction to retrieve data you must multiply the address with 2 since the AVR object code consist of words.

LDI ZL, Low(lbl * 2)

LDI ZH , High(lbl * 2) LPM ; get data into R0 Lbl:

Atmel mnemonics must be used to program in assembly.

You can download the pdf from www.atmel.com that shows how the different mnemonics are used.

Some points of attention :

*All instructions that use a constant as a parameter only work on the upper 16 registers (r16-r31)

So LDI R15,12 WILL NOT WORK

*The instruction SBR register, K

will work with K from 0-255. So you can set multiple bits!

The instruction SBI port, K will work with K from 0-7 and will set only ONE bit in a IO-port register.

The same applies to the CBR and CBI instructions.

You can use constants too:

.equ myval = (10+2)/4 ldi r24,myval+2 '5

ldi r24,asc("A")+1 ; load with 66

Or in BASIC with CONST :

CONST Myval = (10+2) / 4

Ldi r24,myval

How to make your own libraries and call them from BASIC?

The files for this sample can be found as libdemo.bas in the SAMPLES dir and as mylib.lib in the LIB dir.

First determine the used parameters and their type.

Also consider if they are passed by reference or by value

For example the sub test has two parameters:

page -184-

©MCS Electronics, 1995-2007

xwhich is passed by value (copy of the variable)

ywhich is passed by reference(address of the variable)

In both cases the address of the variable is put on the soft stack which is indexed by the Y pointer.

The first parameter (or a copy) is put on the soft stack first

To refer to the address you must use:

ldd r26 , y + 0 ldd r27 , y + 1

This loads the address into pointer X

The second parameter will also be put on the soft stack so :

The reference for the x variable will be changed :

To refer to the address of x you must use: ldd r26 , y + 2

ldd r27 , y + 3

To refer to the last parameter y you must use ldd r26 , y + 0

ldd r27 , y + 1

Write the sub routine as you are used too but include the name within brackets []

[test]

test:

ldd r26,y+2 ; load address of x ldd r27,y+3

ld r24,x ; get value into r24 inc r24 ; value + 1

st x,r24 ; put back

ldd r26,y+0 ; address of y ldd r27,y+1

st x,r24 ; store ret ; ready [end]

To write a function goes the same way.

A function returns a result so a function has one additional parameter. It is generated automatic and it has the name of the function.

This way you can assign the result to the function name

For example:

Declare Function Test(byval x as byte , y as byte) as byte

A virtual variable will be created with the name of the function in this case test. It will be pushed on the softstack with the Y-pointer.

To reference to the result or name of the function (test) the address willbe:

page -185-