decimal math and scratch ram storage problems
Lone_husky
Posts: 13
First I want to say how great this forum is...very open and very responsive and helpful people.
Anyways I have another issue.
I have a detector that makes two different measurements in rapid succession and then calculates the ratio. I have used a bit wise approach to the division within in the confines of BCD math (see below) and had been storing the value as two words in scratch ram to use later. The program must acquire multiple readings and then calculate an average which it stores and then uses later to compare to the run-time (real-time) ratio. I have been having a number of issues, with changes in the number of significant digits that the division code produces sometimes 3 digits, sometimes 4 despite the number of inputs significant digits remaining the same. The real issue is then because I store the decimal part and fractional part in two separate words, and multiply the decimal part by a 1000 and add the fractional part (assuming 3 significant digits) when comparing the average to the current measured ratio. Obvisously if the number of significant digits changes, my multiply by 1000 and adding the fractional portion does not produce the correct result.
so is there a better way to do the division part? and is there a better to store and re-consistent the floating point number?
code is below
thanks in advance
-V
Anyways I have another issue.
I have a detector that makes two different measurements in rapid succession and then calculates the ratio. I have used a bit wise approach to the division within in the confines of BCD math (see below) and had been storing the value as two words in scratch ram to use later. The program must acquire multiple readings and then calculate an average which it stores and then uses later to compare to the run-time (real-time) ratio. I have been having a number of issues, with changes in the number of significant digits that the division code produces sometimes 3 digits, sometimes 4 despite the number of inputs significant digits remaining the same. The real issue is then because I store the decimal part and fractional part in two separate words, and multiply the decimal part by a 1000 and add the fractional part (assuming 3 significant digits) when comparing the average to the current measured ratio. Obvisously if the number of significant digits changes, my multiply by 1000 and adding the fractional portion does not produce the correct result.
so is there a better way to do the division part? and is there a better to store and re-consistent the floating point number?
code is below
'light1 and light2 are measurements 'now proceed to calculate ratio '---------------Calculation of Ratio----------------- I=1-(light1.BIT15 ^ light2.BIT15*2) ' sign of result light1=ABS light1 ' divide + numbers light2=ABS light2 I=light1/light2*I ' integer part F=0 ' initialize '---------binary division loop----------- FOR J=15 TO 0 ' 16 bits light1=light1//light2<<1 ' remainder*2 F.BIT0(J)=light1/light2 ' next bit NEXT '---------------------------------------- F=F**10000 ' normalize F*10000/65536 ratio = (I*1000)+(F) PUT mem_idx, WORD ratio ' put ratio in scratch ram to be retrieved later for calculation of average mem_idx=mem_idx+2 'to retrieve stored value and calculate average GET avg_mem_start, WORD I DEBUG CR, "location =", DEC avg_mem_start, " ",DEC I,CR FOR mem_idx = avg_mem_start+2 TO avg_mem_stop STEP 2 ' each loop add 2 GET mem_idx, WORD F DEBUG "location =", DEC mem_idx, " ",DEC F,CR I=I+F NEXT avg=I/6 DEBUG "average->",DEC avg
thanks in advance
-V
Comments
What's the range and precision of your values? It's hard to give you specific advice without more information.
I realize this is a little clumsy but I wasn't sure if the really divison statement would correctly perform the division as I need it to and then I don't really understand the scaling.
Any simplification would be greatly appreciated. Also I suspect my code would run faster.
-V
That allows an integer part only up to 5 or 6, the representable numbers being ratios from
0.0000 to 6.5535
Do the ratios you expect fit in that range?
Stepping back to look at the problem, I don't think the light level will ever be negative. (?!) So you don't need to deal with the sign in the first integer division. Will one light level always be larger than the other? If that is the case, that could be always the dividend, so there would never be an integer part and never a problem with overflow when it goes to add up the 6 entries. But maybe either light level can be greater. How much greater is significant? Maybe you need a log ratio to cover a huge range of ratios.
This is not BCD math. It is strictly binary. It is possible to go to double precision math if you need to add up a longer list of numbers to go into the average and to maintain precision, but that is more work.
If you can work with 3 digits of precision in the fractional part, use
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
A broader question is whether or not I need to use the bit wise binary division loop that I'm using...is there a simpler way?
-Vassilios
the number will go smoothly from 0987 to 1185, say, representing 0.987 to 1.185. Store the numbers like 987 and 1185 in memory. The integer is a fraction in thousandths, and you can add them up, average them and compare them without trouble. No need to filter the wavelengths.
I can almost guarantee you that the binary division loop is the most accurate and fastest why to get there.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
rather than
light2/light1 = F/65536.
On the BASIC Stamp with the ** operator, the division by 65536 is implicit. You can see that in Mike's explanation. If you take the most significant 16 bits that result from a 16x16 multiply, that is really the same as multiplying two numbers and shifting the result 16 to the right, the same as division by 65536.
When you do N = 1000**F, you are renormalizing, to make the implied denominator 1000 instead of 65536...
light1/light2 = N / 1000
For example, if light1/light2 = 5/7, then F = 46811, and N = 714.
5/7 = 46811/65536 = 714/1000 = 0.714
There are round-off errors, but not too bad within three digits of precision.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
I'm sorry to bother you with this and I hope I don't appear as hopelessly stupid, but I can't seem to get only 3 digits of precision using what you recommend.
if I use the following
everything works fine, but because I want to do some smoothing, it rapidly overfills the 16 bit word. see below
Output
ratio = 0.9453 stored as 9453
ratio = 0.9958 stored as 9958
ratio = 1.0737 stored as 10737
ratio = 1.1221 stored as 11221
but how do I get only 3 digits of precision....
trying
Doesn't work and I get the following....with
output
ratio = 0.9401 stored as 143
ratio = 0.9396 stored as 143
ratio = 0.9481 stored as 144
ratio = 0.9897 stored as 151
ratio = 1.0607 stored as 1009
ratio = 1.0940 stored as 1014
ratio = 1.0973 stored as 1014
ratio = 1.1150 stored as 1017
So the ratio is still calculated with 4 digits of precision and then overflows the 16bit register when stored...
What am I doing wrong?
-V
never mind my last post....I figured it out... I had been screwing up the normalization.
BTW
do you have a good procedure for calculating a running differential or derivative?
thanks for all of your help
- V
and included only the line
which includes the F**1000
A running differential or derivative. It is easy enough to take differences from one measurement to the next. The fun part is determining what to make of them.
By the way, since the light levels are always going to be positive numbers you can replace
with simply
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com