High-precision reciprocal
Lord Steve
Posts: 206
Could anyone provide PASM code to calculate 2^32 divided by x?
Comments
It's Spin, not PASM, but the translation should be straightforward.
Michael, you had asked in that other thread how Kye and I came up with the algorithms. In my case I needed it for the PBASIC on the Stamp, where with 16 bits words it is even more critical. I consider it a fundamental integer algorithm, right up there with multiplication and division. From basic principles, after you do a division, there is a quotient and a remainder. The remainder then is what is to the right of the radix point. This algorithm then calculates the binary fraction to whatever number of binary digits is required. It is just like ordinary long division. At each step it asks if the numerator is greater than the denominator. If no, it inserts a zero into the lsb, if yes, it inserts a 1 and subtracts to get a new numerator. Then it doubles the numerator and repeats.
In the Prop, this is how a program calculates from a desired frequency in Hertz to the angular frequency required by frqx. But it is also often the best way to handle any integer calculation involving fractions. On the Stamp with 16 bits it is more important than on the Prop with 32 bits, but it is still important for highest levels of precision. The main caveat on the Prop is to watch out for the sign bit.
spin:
pasm:
sorry, back to your regularly scheduled programming [8^)
Jonathan
and wc on the initial mov should do that I think.
Jonathan
The indentation of the spin version above was off -- I went back up and corrected it. The f++ is inside the IF ...
Say you have 1 / 7531 which is your reciprocal. The algorithm kicks out 32 binary digits:
result = %00000000000010001011001111000001
And that value is stored as an integer in a long. The binary radix is implied at the left end. The decimal value of that integer is 570305. What this means is that the original number 1/7531 is now approximated by a number with a much larger denominator, 2^32. Due to the fact that the denominator is large, the approximation is good. ("good" can be easily quantified) You can check for yourself that 570305 / 4_294_967_296 is very close to 1 / 7531
To transform that to a decimal fraction, use the Prop ** operator.
decimalResult := result ** 100_000_000
That gives decimalResult = 13278. What that has done is to renormalize the denominator from 2^32, to 10^8.
Now the approximation is 1 / 7531 = 13278 / 100_000_000.
To print that, place the decimal point 8 positons to the left: 1 / 7531 = 0.00013278.
I hope that might a help to other spinners.