Design Ideas: November 23, 1995
Fractions eliminate floating-point multiply
Michael Gauland,
Tektronix Inc.
Beaverton, OR
Inexpensive µCs offer better than average performance for many applications. The devices do not include hardware to do floating-point math, however. You often need to multiply or divide an integer by a floating-point constant, such as pi, square root of 2, or such circuit-specific values as the gain of an amplifier. You are interested only in the integer portion of the result. Using a floating-point representation for the constant would entail linking in -- or, worse -- writing, large, slow library functions. Fortunately, you can achieve excellent accuracy by using the ratio of two integers to approximate the constant.
For example, 355/113 approximates pi to within (8.5E-6)%. Therefore, you can replace the statement a=b*3.1415926539 with a=(b*355)/113. You've thus substituted an integer multiply and divide for a floating-point multiply. In doing so, you generally get a substantial improvement in execution speed. The function in Listing 1 determines the best integers to use for a given word size. Table 1 gives you approximations for several mathematical constants.
| TABLE 1 -- MULTIPLY/DIVIDE INTEGERS FOR MATH CONSTANTS |
| Value |
8-bit signed |
8-bit unsigned |
16-bit unsigned |
16-bit unsigned |
| pi[3.1415...] |
22/7 [.04%] |
245/78 [.02%] |
355/113 [8.5E-6%] |
65,298/20,785 [5.1E-6%] |
| sqrt(2) [1.414...] |
99/70 [.005%] |
239/169 [8.7E-4%] |
19,601/13,860 [1.3-7%] |
47,321/33,461 [2.2E-8%] |
| sqrt(3) [1.732...] |
97/56 [.005%] |
168/97 [.005%] |
32,592/18,817 [1.4E-7%] |
51,409/29,681 [3.8E-8%] |
| e [2.71828...] |
106/39 [.01%] |
193/71 [.001%] |
25,946/9,545 [2.0E-7%] |
49,171/18,089 [1.0E-8%] |
| cos(pi/6) [.866....] |
84/97 [.005%] |
181/209 [3.8E-4%] |
18,817/21,728 [1.4E-7%] |
35,113/40,545 [1.0E-8%] |
| TABLE 2 -- MULTIPLY/DIVIDE INTEGERS FOR MATH CONSTANTS OF BINARY DENOMINATORS |
| Value |
8-bit signed |
8-bit unsigned |
16-bit signed |
16-bit unsigned |
| pi[3.1415...] |
101/32 [.5%] |
201/64 [.03%] |
3,217/1,024 [2.8E-4%] |
3,217/1,024 [2.8E-4%] |
| sqrt(2) [1.414...] |
91/64 [.5%] |
181/128 [.01%] |
11,585/8,192 [.002%] |
46,341/32,768 [1.1E-4%] |
| sqrt(3) [1.732...] |
111/64 [.13%] |
111/64 [.13%] |
14,189/8,192 [2.8E-4%] |
14,189/8,192 [2.8E-4%] |
| e [2.71828...] |
87/32 [.02%] |
87/32 [.02%] |
5,567/2,048 [7.4E-4%] |
5,567/2,048 [7.4E-4%] |
| cos(pi/6) [.866....] |
55/64 [.8%] |
111/128 [.13%] |
14,189/16,384 [2.8E-4%] |
14,189/16,384 [2.8E-4%] |
| LISTING 1OPTIMUM INTEGERS VS WORD SIZE |
void div_approx(#double val, long int maxNumerator, long int
*maxX, long int*maxY) {
long int x, y;
double e, maxE;
unsigned long int stop;
if (val>1)stop=maxNumerator/val+.5; else stop=maxNumerator ;
maxE = 10;
for (x=1; x<stop; x++){
y = x*val+.5;
e = fabs((float)y/x-val);
if (e < maxE) {
maxE = e;
*maxX = x;
*maxY = y;
};
};
}
|
You can realize even greater efficiency by making the denominator a power of 2 and substituting right shifts for the divide. Listing 2 gives a function to calculate ratios of this type. Table 2 shows the approximations for the constants in Table 1. (DI #1774)
| LISTING 2OPTIMUM INTEGERS FOR 2N DENOMINATORS |
void shift_approx(#double val, long int maxNumerator, int
*maxShifts, long int*maxY) {
int shifts;
long int x, y;
double e, maxE;
unsigned long int stop;
if (val>1)stop=maxNumerator/val+.5; else stop=maxNumerator ;
maxE = 10; shifts = 0;
for (x=1; x<stop; x<<=1){
y = x*val+.5;
e = fabs((float)y/x-val);
if (e < maxE) {
maxE = e;
*maxShifts = shifts;
*maxY = y;
};
shifts++;
};
}
|
| EDN Access | feedback | subscribe to EDN! |
| design features | out in front | design ideas | columnist | departments | products |
Copyright © 1995 EDN Magazine. EDN is a registered trademark of Reed Properties Inc, used under license.