ArmAssemblyInt:FloatingPoint:A

Copyright 2019, Clinton A Staley

Concepts

  1. Binary FP formats
  2. Normalization

Overview

In this segment we'll look at floating point computation in assembly, ARM floating point opcodes and special registers, and the format and structure of floating point values.

Floating Point Formats

Let's start by showing how floating point values are represented in binary.

Fractional place values

First, let's do a reminder of how binary fractional values work, from earlier topics.

A decimal floating point number like 3.1416 uses fractional place values. As we move to the right, each decimal place has 1/10th the value of the one to its left. This pattern continues past the decimal point, with place values of 1/10th, 1/100th, etc. As you know, this allows arbitrarily precise representation of a fractional value as you add more fractional digits. And all the rules for counting, carrying, adding, etc. extend naturally to the fractional digits. .109 + .141 = .250 is as easy to compute, and follows the same rules, as 109 + 141 = 250.

Precisely the same thing can be done with binary values, but with the obvious difference that place values reduce by a factor of 2, not 10, as we move to the right. So, we add bits below the 1's place, after what we might call a "binary point" instead of a decimal point: e.g. 1.1101. Each place value to the right of the binary point has 1/2 the value of the one to its left, so we have the 1/2s place, the 1/4s place, the 1/8ths place, etc. .1, for instance represents the value 1/2, .01 represents 1/4, and .11 represents 3/4.

Question 1

Translate these binary values into their base-10 equivalents: .101 11.011, 11.111

Answer 1

5/8ths or .625, 3 3/8 or 3.375, 3 7/8 or 3.875

Scientific notation

Decimal floating point numbers are used to represent a huge range of values, from the mass of an electron to the mass of the universe. In practice, however, we know any value only to a limited accuracy, so a given floating point value has only a certain number of nonzero digits. If we write out very large or very small numbers entirely, there are a lot of zeros:

electron mass .0000000000000000000000000000091 kg

universe mass 310000000000000000000000000000000000000000000000000000 kg

Instead, we use scientific notation, typically written with a mantissa between 1 and 10 in value, and an exponent of the base 10, which implies a shift of the mantissa left or right by some number of digits. So, an electron's mass is 9.1 × 10^-31 kg and the universe's mass is 3.1 x 10^52 kg.

The mantissa (e.g. 9.1 or 3.1) determines the accuracy, and the exponent (e.g. -31 or 52) determines the size range. More mantissa digits provide greater accuracy. More exponent digits provide greater range. Typically the mantissa digits are more important, since 2 or perhaps 3 exponent digits are sufficient for all practical purposes.

Scientific notation also works in binary, with the obvious change that the mantissa is in binary, and the exponent is a power of 2, not 10. So we might write 13, or 1101 as 1.101 x 2^3. As in decimal scientific notation, the power of two has the effect of shifting the binary point to the right or left. And as in decimal scientific notation, the mantissa has exactly one digit to the left of the binary point. But, note, in base 2 that means the mantissa is less than 2: it's always 1.XXXX

Question 2

Translate these values into binary scientific notation: 28, 5.25, .0625 or 1/16th.

Answer 2

28 is 1.11 x 2^4, 5.25 is 1.0101 * 2^2, and .0625 is 1.0 x 2^-4

IEEE format

Any floating point variable, like a C double, is a binary scientific notation number, with some of the variable bits devoted to the mantissa, and some to the exponent -- both in base 2.

The organization of bits within the number is so important that there is a nearly universal standard for it: the IEEE floating point standard. This standard divides the bits of a floating point variable into three parts, with the number of bits per part varying by the number of bits in the entire variable. For a 64-bit variable, which is the most common (e.g. a C double) the parts are:

Each has an interesting minor point attached to it.

Sign bit

The sign bit indicates the sign of the entire number, and the interesting point is that it's not a twos complement sign. Floating point values use sign-magnitude, so the sign bit is simply a 0 if the number is positive and 1 if it is negative. The exponent and mantissa represent the absolute value of the number either way.

Exponent bits

The exponent gives the power of two, as an integer, and its interesting point is that it's not a twos complement value either, despite needing to represent negative exponents. 11 bits can represent unsigned values from 0 to 2047. But the exponent is an excess-1023 number, which means it is a positive unsigned value, but it's equal to the exponent value plus 1023. An exponent of 1, for instance, is represented by 1024 in the exponent bits, or 10000000000. An exponent of 2 is 10000000001, and -1 is 01111111110.

This is an odd way to represent the range from -1023 (00000000000) to 1024 (11111111111), but it is preferred for hardware design reasons beyond the scope of this discussion.

Question 3

What are the exponent bits for these exponent values: 0, -8, 42?

Answer 3

0 would be exactly 1023 or 01111111111. -8 is 1023-8 = 1015, or 01111110111. 42 is 1023+42 = 1065 or 10000101001

Mantissa bits

The mantissa occupies the majority of the bits, and its interesting point is that only the bits after the binary point are stored, because in a correctly normalized binary floating point value the mantissa always starts with 1. so the 1 to the left of the binary point is simply assumed. Thus a mantissa of 1.101 is stored as 101 followed by 49 zero bits.

Let's try a few examples, with the 64 bits of the floating point value broken into sign, exponent and mantissa fields.

5.25 is 101.01 or 1.0101 x 2^2 or

0 10000000001 010100000000000000000000000000000000000000000000000

-23.25 is -10111.01 or -1.011101 x 2^4 or

1 10000000011 01110100000000000000000000000000000000000000000000

.1875 is .00011 or 1.1 x 2^-4 or

0 01111111011 100000000000000000000000000000000000000000000000000

.2 is .00110011 (repeating) or 1.100110011 (repeating) x 2^-3 or

0 01111111100 100110011001100110011001100110011001100110011001100

The last one is interesting in that .2 is a repeating fraction in binary. Whether a fraction is repeating or not is not an inherent property of the value. For instance, the repeating decimal fraction 1/3 is simply .1 in base 3 since the place value after the trinary point is 1/3. The value 1/X repeats if X has any prime factors that are not prime factors of the base. In decimal, that means X may have only prime factors 2 and 5. In binary, it may have only 2 as a prime factor. So 1/5, 1/10, 1/20 etc are all repeating fractions in binary. Those 52 bits of mantissa make more sense when you consider such cases.

Question 4 Write out the full binary values, including sign, exponent, and mantissa for -32.5 and .375.

Answer 4

-32.5 is -100000.1 or 1.000001 x 2^5 or

1 10000000100 00000100000000000000000000000000000000000000000000

.375 is .011 or 1.1 x 2^-2 or

0 01111111101 10000000000000000000000000000000000000000000000000

Question 5 We use base 10 simply because we have that many fingers. If we were choosing a base from scratch, base 12 would be better. Why?

Answer 5

12 has more factors and thus more fractions would be nonrepeating. 1/3 is 4/12, and thus .4 in base 12 for instance.

Indeed, base 60 is even better since 2,3,4,5 and 6 divide it evenly. This is why we see it as a value in time units (minutes per hour, seconds per minute).

Single vs double precision

Floating point values come in different sizes. In particular, there is a 32-bit or single precision version of the IEEE format, with 1 sign bit, 8 exponent bits, and 23 mantissa bits. Single precision computation is generally faster, and the values take half as much memory. But, single precision floating point is quite imprecise, since 23 bits of mantissa are equivalent to 6-7 digits of decimal accuracy, compared to the 15-16 digits provided by a 52 bit mantissa. 6-7 digits are sufficient for approximate computations, in particular for many computer-graphic computations. But any serious scientific work requires greater precision.

There are also larger floating point formats, e.g. for 80 bits, or even 128 bits, all with IEEE formats like that for double precision. The additional bits are almost all devoted to the mantissa to increase accuracy.

In the next segment we'll look at how floating point operations are done in ARM.