Very often a lookup table or set of lookup tables is the simplest solution. By using a little creativity you can greatly reduce the size lookup table needed. Let's say you want to find the Sine of x. First off, the sine of x only has to be calculated or stored for the first 90 degrees. In this quadrant the value ranges from 0 to 1. In the second quadrant (90 to 180) the value ranges from 1 to 0. In this second quadrant you can get the equivalent 1st quadrant value by using 180-x. For example, 180-110=70, so sin(110)=sin(70). In the 3rd quadrant (180 to 270) the value ranges from 0 to -1. In the 3rd quadrant sin(x)=-sin(x-180). In the 4th quadrant (270 to 360) the value ranges from -1 to 0. In the 4th quadrant Sin(x)=-sin(360-x). The implication of this is that you can use a single table that covers from 0 to 90 degrees. You test the incoming x value and branch to one of four routines, based on the quadrant to which x belongs. Each subroutine would then convert x to the proper value, look up the value in the table, and set a posneg indicator that will tell us whether the last value converted is considered a positive or negative value. (The returned value is in positive format, and the posneg flag tells us whether it is really positive or negative). The lookup table can have the answer stored in either binary or packed decimal format. Which form you use depends on what you are going to do with the answer. If you are going to display the answer on an LCD, then use packed decimal format. If you are going to perform further math on the value, then binary might be best. If the math perfomed is something simple like multiply the value by some constant, then you can even store the table values so that you have the value multiplied by the constant already in the table. In packed decimal format you use the upper 4 bits to store one digit, and the lower 4 bits to store another digit. Two bytes will thus get you 4 decimal digits. You can thus store values from .0000 to .9999 You could get 6 digit accuracy if you use 3 bytes of storage per value. Let's assume you want 6 digits of accuracy and a resolution of .5 degrees. There are 90 degrees times 2 to give us .5 degree resolution, times 3 bytes of storage. 90x2x3=540 bytes of storage. A lookup table is limited to a maximum size of 256. This is due to the fact that the addressing mechanism is limited to a one byte value. The way you can get around this limitation is to use three jump tables each having a length of 180. Let's call these tablehigh, tablemiddle, and tablelow. You would load the tables with the required data: SINE Angle In Degrees | Offset Address | VALUE | High | Middle | Low ---------------------------------------------------------------- 0.0 | 0 |.000000| 00 | 00 | 00 0.5 | 1 |.008727| 00 | 87 | 27 1.0 | 2 |.017452| 01 | 74 | 52 1.5 | 3 |.026177| 02 | 61 | 77 etcetera | etcetera | etc. | etc. | etc. | etc. etcetera | etcetera | etc. | etc. | etc. | etc. 89.5 | 179 |.999962| 99 | 99 | 62 90.0 | 180 |.999999| 99 | 99 | 99 ---------------------------------------------------------------- So let's say you feed the value 179 degrees in to the Sine Converter. First the Sine Converter would determine that 179 is in the second quadrant because it is between 90 and 180 degrees. The SecondQuadrant converter would set the posneg flag to "1" to indicate a positive polarity for the answer. Then 180-179=1. We multiply this by 2 to convert the equivalent angle into an Offset Address of 2. The SecondQuadrant routine now jumps to the TableDecode routine. The Offset Address now points to the proper table element. We call a subroutine that looks at posneg and outputs either a + or a - symbol to indicate polarity. It also prints the decimal point (preceded by a "0" if desired). We access tablehigh and get back 0x01. We call a subroutine that sends "01" to the LCD. We access tablemiddle and get back 0x74. We call a subroutine that sends "74" to the LCD. We access tablelow and get back 0x52. We call a subroutine that sends "52" to the LCD. The above assumes you want the sine value displayed. If you instead want the sine value so it can be used with more math processing, then store the value in binary. Here is a simple method for generating the binary equivalents of numbers such as .017452: Bigger than or equal to 1/2? No .0 Bigger than or equal to 1/4? No .00 Bigger than or equal to 1/8? No .000 Bigger than or equal to 1/16? No .0000 Bigger than or equal to 1/32? No .00000 Bigger than or equal to 1/64? Yes .000001 .017452-.015625=.001827 Bigger than or equal to 1/128? No .0000010 Bigger than or equal to 1/256? No .00000100 (just finished high byte) Bigger than or equal to 1/512? No .00000100 0 (we just began middle) Bigger than or equal to 1/1024? Yes .00000100 01 .001827-.0009765625=.0008504375 Bigger than or equal to 1/2048? Yes .00000100 011 .0008504375-.00048828125=.00036215625 Bigger than or equal to 1/4096? Yes .00000100 0111 .00036215625-.000244140625=.000118015625 Bigger than or equal to 1/8192? No .00000100 01110 Continue this process until you have all 24 bits converted. Yes, you are right, this is a regular bun-buster of a thing to have to do for 180 values (to get 0 to 90 degrees in .5 degree increments). Only a fool would attempt to build such a table manually. Instead, write a simple program on your PC in BASIC, or C, or FOXPRO, or whatever language you like. Then use the program to compile the table values for you. Hint: the output is a "0" for No, and a "1" for Yes. When the answer is Yes you subtract and now use the result as your value to compare against. I use FOXPRO to do all this nitty gritty boring stuff, and I have it actually produce three ASCII files that make up the 3 tables of RETLW instructions (High, Middle, and Low). I Then append these files into my assembler at the proper places. Each line contains something like this: RETLW 00110101b where the "b" means that the number is in binary. Well, I would write some more, but I have some other things to get to right now. I hope that someone out there finds this useful. Is there any great desire out there for a series of tutorials on topics like binary addition, subtraction, division, multiplication, etc.? I could probably also write some stuff on interrupt routines and various other topics that are oriented towards the PIC way of doing things. I am willing to write some tutorial type stuff if it is useful to some and not obnoxious to those who are already experts. I should warn you though that I am more inclined to write about the HOW and WHY aspects and leave a complete and full program listing to the user. It is not just a matter of time, but my mindset as an educator. I am willing to teach and explain and show someone how to go about something. I usually won't hand my students a complete program to accomplish the task, because that is what I want *them* to learn how to do. My objective is to educate, not to be a source of finished product so that the student has nothing to do. I believe we learn by doing more than any other way. Fr. Tom McGahee -----Original Message----- From: Phu T. Van To: PICLIST@MITVMA.MIT.EDU Date: Tuesday, February 01, 2000 1:22 AM Subject: Math, part II >Thanks for all your help. I think part (most/all?) of my dilema lies in >the fact that I'm using the PIC, small-ish in both memory and raw >processing power. But I'm also lacking a very fundamental thing : the >knowledge of how to do elementary math with the PIC's very limited >insruction set, specifically its bit-wise operations. >How do you, for example : >--Multiple/divide ? >--Calculate exponents ? >--Anything taught in Elementary Algebra ? > >Is it futile to hope that previous PIC gurus had written concise, >dummy-oriented tutorials on how to do these operations ? Obviously >someone has had to multiply and divide with a PIC once in their life. It >frustrates me slightly that I can't do these basic things, even though >it appears to me that PIC programs follow the formula : >code,code,code,miracle,code. > >Thanks again. >--Phu T. Van >P.S. : I've dug up some information on the CORDIC algorithm. Is there >any way to implement it (painlessly, if possible) on the 16F84 ? The >page claimed it was successfully used on the Basic Stamp II, which is I >think more powerful than the PIC. >