Path: Home => AVR-EN => assembler introduction => Floating point numbers => Conversion of decimals    (Diese Seite in Deutsch: Flag DE) Logo

Beginner's introduction to AVR assembler language

Floating point arithmetic in assembly language

Converting decimals to binary floating point numbers in assembler language

Following the introduction to binary floating point numbers here and the conversion of binary floats to decimal format here we need the opposite of the last: the conversion of decimals to float. And that goes like this.

Decimal number formats

There are lots of different decimal number formats: To convert all these formats of decimals to binary floating point numbers, the software has to:
  1. check whether the decimal is negative (the string starts with "-"),
  2. convert the unsigned decimal mantissa to a binary number format,
  3. get the decimal exponent (number of decimal digits before the decimal dot plus the number Anzahl der Ziffern vor dem Dezimalkomma plus the negative or positive number following "", if any) and to multiply (positive exponent) or divide (negative exponent) the binary mantissa, including any changes to the binary exponent, if necessary,
  4. normalize the binary mantissa (highest mantissa bit = 1), and to
  5. invert the mantissa's sign bit and it's content if the input number is negative.

The assembler software for the conversion

The assembler source code here has the decimal number to be converted to a binary float as a string with ASCII-formatted characters in its flash memory, together with the conversion code. This string is copied to a location in SRAM first. This step is necessary to avoid multiple mixed accesses to the flash later on. If your decimal is already located in the SRAM (e.g. because you received it via serial communication) you can skip this step. Only ensure that the decimal ends with a null byte at the end (null-terminated string).

The software is written for binary mantissas of up to 40 bit length and uses an 8-bit binary exponent. That corresponds to a 48-bit binary float. Those who need less accuracy can remove the last or the two last bytes and save some execution time with that.
Detecting the negative sign
By default a positive sign is assumed. The pointer X (XH:XL = R27:R26) points to the beginning of the string and reads the first ASCII character. If that is a minus character, the flag bMneg is set. The procedure later on uses this flag to format the mantissa as negative.
Read the decimal mantissa and convert it to a binary integer
The software then starts reading the decimal mantissa. This is written for english format (decimal dot), but this is ignored in this stage. The null terminator 0x00 or a "E" character ends reading the decimal mantissa. Other characters than ASCII-Zero to ASCII-Nine lead to a jump to the error loop routine. In that case register R16 holds an ASCII character that characterizes the reason for the failure: The digits read are, starting with 10,000,000,000 (0x02540BE400), multiplied (by repeated addition to the result) and added. The next digit reads the next lower decimal as hex. This is repeated until either the string ends or an "E" ocuurs or the decimal reaches zero (all other characters following are ignored). To read the decimals from a table in flash memory avoids to divide the number by 10 and accelerates execution.
Calculate the binary mantissa
To convert the binary integer that was read in to a binary mantissa, all mantissa bits are first cleared. Only the least significant bit in thje mantissa is set, which signals that all 40 bits have been converted.

Starting with the decimal 1,000,000,000,000 (0xE8D4A51000) this decimal is repeatedly divided by two. The integer is then compared with this divided number. If the integer is equal or larger than the divided decimal, the decimal is subtracted and a one is shifted into the result registers. If not, a zero is shifted into the result registers. If, after shifting, a zero is shifted out the division by two and the comparison is repeated. If a one is shifted out, the conversion of 40 bits is complete.
Determine the decimal exponent and convert it
Now the decimal exponent is determined. First the position of the decimal dot is searched for in the string: any digit left to the dot increases the decimal exponent. Then the "E" is searched for. If the string ends without this character, the decimal exponent is already correct. If not, the maximal two decimal digits are read, converted to a binary byte and this is either added (if the exponent sign is missing or "+" or is subtracted, if negative (flag bEneg is set).

Now the decimal exponent is checked if it is larger than 40 or smaller than -40. If so, the error loop is executed.

If the decimal exponent is positive (bit 7 of rDExp is clear), the mantissa is multiplied by 10. This is performed in a subroutine named Mult10:. To do this it is first checked if the most significant byte of the mantissa (rR4) reaches or exceeds 25. If that is the case, the mantissa is shifted right and the binary exponent is increased. The mantissa is then copied, rotated to the left two times, then the copy is added and another shift left is performed. This multiplication is repeated as often as the decimal exponent says.

If the decimal exponent is negative, the mantissa is divided by 10, as often as the decimal exponent says. Division by 10 can be done in two ways, both are included in the source code following the label Div10:. Just change the respective switch either to zero or one. The first type of division by 10 is to shift out the 40 bits one by one and to subtract 10 from the shifted-out bits. If no carry occurs, a one is shifted into the result registers. If a carry occurred, the subtraction of the 10 is undone and a zero is shifted in.

The second version of dividing by 10 needs a little bit more source lines, but performs faster. The accelerated mode copies the previous mantissa, adds five to the mantissa, then the copy is repeatedly divided by two. The first, the second and the third divided copies are subtracted from the uprounded mantissa, the fourth and fifth are not subtracted. Then the following two divided copies are subtracted and the next following two are not subtracted. The dividing and subtracting ends when the divided copy is empty.

To switch to the accelerated div10 version is useful if many divisions by 10 have to be performed (in case of a negative decimal exponent). In case of 1E-30 the classical div10 method needs 24.55 ms, the accelerated method only 14 ms, and so is nearly double as fast.

The accelerated method is further described here for divisions by N and here for 10 only.
Normalization and sign processing
Finally the binary mantissa is normalized. It is either shifted to the right (if bit 39 is set) or is shifted left as long as bit 38 of the mantissa is not one. Of course shifting decreases or increases the binary exponent accordingly.

Those who need normalization with an extra mantissa bit, shift the mantissa left until bit 39 is one and then clear bit 39. This shifts the most significant bit out and adds one bit to the mantissa.

Finally: if the flag rDneg is set, the complete mantissa, including the sign bit 39, is inverted.

After all these operations the result binary mantissa is in rR4:rR3:rR2:rR1:rR0, the binary exponent in rBExp and all is completed.
Results
Conversion results and times for dezimals to float The table to the right shows results of such conversions from decimal to binary for selected cases. Displayes is the decimal number, its binary mantissa and exponent, the result of the conversion of the back to decimal format, as well as the execution times needed. In all cases the accelerated DIV10 method has been switched on.

As can be seen, the re-converted numbers differ in the fifth or sixth decimal digit. So is 0.12345651 incorrect for the second "5" in the seventh digit, which would round up falsely to the sixth digit. This is as expected because with 40 bits LOG2(40) is little more than 5. If you need it more accurate, use a 56 or 64 bit mantissa instead. The method is the same, the extensions are pretty small.

Conclusion

Those who want to send the controller into deep bit shifting and away from the relevant things that also can happen in the controller's life, use floating point conversion. Conversion of a decimal to a 40-bit float can replace delay routines of around 10 ms. If you need maximum delay, then let your controller convert 1E-36 and switch the accelerated method off.

Have much fun with playing with this software.

To the page top

©2021 by http://www.avr-asm-tutorial.net