Quote:
|
Originally Posted by miketwalker
That sounds like a good idea. Could you show me an example of what you mean? I don't know how you can treat the lower 8 bits as a fractional portion.
|
Those are the fixed-point pseudo-floats that I keep telling people to use! (I picked it up programming in Pascal under DOS, where there are similarly annoying memory limits.)
Here's how it works:
Take a number, say the sine of 75° (or any arbitrary number), and express it as a 64-bit real (which is a float on this platform, if I'm not mistaken). You're using 64 bits to store a lot of different things--the numerical part, the sign (i.e. + or -) and the exponent (i.e. 10
n). As it happens, the value sin 75° = 9.65925826289068E-0001 is stored as 965925826289068, with an exponent of 10
-1 and a sign of +.
We've got about 15 significant digits here (965925826289068 fits in the 52 bits reserved for it); the question is, do we
want or even
need 15 significant digits? Probably not. So let's toss some. (This is what he meant.) We can take the most significant few, say 9659, which stores neatly in 15 bits (with lots left over, actually).
While we're at it, we have that exponent block of 11 bits; it's no use to us, so long as we can say for sure that all of our values are going to land within a certain range. For sine, it's between -1 and 1--which is to say, in the neighbourhood of 10
0. So we don't need 11 bits to represent that either. We instead
assume that our data is going to be in that range, and don't even store the exponent. (This of course means that we can't use this as easily as a float, because the order of magnitude isn't stored in the variable itself--it's in our heads!)
The sign bit stays, because we have both positive and negative numbers.
What we're left with is 15 + 1 = 16 bits, which happens to be equivalent to an integer type that is directly supported on the PIC (16-bit signed int). So instead of working with the number 9.65925826289068E-0001 (a float), we use the number 9659 to represent the same thing (knowing that we really mean 0.9659). All of our routines, custom-written, of course, make that same assumption, but can therefore store in 16 bits what would have taken 64, sacrificing useless precision and gaining the marked advantage of using the built-in integer operations.
But wait--you can do better. We've naïvely assumed that we need to use the 15 bits to store a number up to 10000 (representing 1.0000). Actually, 2
15 is 32768. So, if we want, we can scale everything by this proportion (3.2768) and end up with a little free precision. Once again, our functions have to know that when we input 32767*, we really mean 1.0000. Similarly, sin 75° would be handled internally as 31650.
Is this representation easy to read? No. Is it easy to code? Not trivial, but not hard either. Certainly not beyond what's expected of a student in any reputable senior high school programming course. Basically, if the mere humans can get their heads around it, they can write a function to printf whatever they please, while letting the PIC do its thing with integers (which it likes).
*Don't do 32768--that's actually an overflow, because we need to account for 0 in the integer number line on the interval [-32768, 32767]; it is intentionally asymmetrical.