Phil Pilgrim (PhiPi)

09-15-2009, 03:20 AM

The values in the Propeller's internal ROM sine table are normalized to $ffff. In other words normalized(1) == $ffff, and anything less than 1 is multiplied by $ffff and rounded to the nearest integer to get the normalized value.

This isn't the way I would have done it. To me it makes more sense for $1_0000 to equal one. That way sines and cosines can be used with fixed point multiplication and yield the expected results. Granted, this would mean that some values in the table would be too big to fit in a word, but that would have been okay. Just set them to zero and use a comparison on the table index to know when to set bit 16 of the looked-up value.

But that's water over the dam; the question is how to fix it. The table values are under their "true" $1_0000-normalized values by one, 1300 times out of 2048. If we could roughly characterize those table indices for which the values are too low and add one to the result just for those, we could reduce the error. As a rule, the values at the upper end of the table are low more often than those in the lower part. So it makes sense to find an index threshold above which we add one to the result. I've done so and have found that a threshold of 707 on the index minimizes the number of errors to 176 too low by one and 216 too high by one, for a total of 392, or 30% of the original number.

Here's the Spin code that implements this adjustment:

PUB cos(x)

'' Cosine of the angle x: 0 to 360 degrees == $0000 to $2000,

'' with result renormalized to $1_0000.

return sin(x + $800)

PUB sin(x) : value | t

'' Sine of the angle x: 0 to 360 degrees == $0000 to $2000,

'' with result renormalized to $1_0000.

if (x & $fff == $800)

value := $1_0000

else

if (x & $800)

t := -x & $7ff

else

t := x & $7ff

value := word[*SINE][*t] - (t > 707)

if (x & $1000)

value := -value

It would be interesting to see if further refinements can be made, for example, by considering neighboring table values to determine which direction the value in question was rounded. But this simple correction, at least, is an improvement.

-Phil

This isn't the way I would have done it. To me it makes more sense for $1_0000 to equal one. That way sines and cosines can be used with fixed point multiplication and yield the expected results. Granted, this would mean that some values in the table would be too big to fit in a word, but that would have been okay. Just set them to zero and use a comparison on the table index to know when to set bit 16 of the looked-up value.

But that's water over the dam; the question is how to fix it. The table values are under their "true" $1_0000-normalized values by one, 1300 times out of 2048. If we could roughly characterize those table indices for which the values are too low and add one to the result just for those, we could reduce the error. As a rule, the values at the upper end of the table are low more often than those in the lower part. So it makes sense to find an index threshold above which we add one to the result. I've done so and have found that a threshold of 707 on the index minimizes the number of errors to 176 too low by one and 216 too high by one, for a total of 392, or 30% of the original number.

Here's the Spin code that implements this adjustment:

PUB cos(x)

'' Cosine of the angle x: 0 to 360 degrees == $0000 to $2000,

'' with result renormalized to $1_0000.

return sin(x + $800)

PUB sin(x) : value | t

'' Sine of the angle x: 0 to 360 degrees == $0000 to $2000,

'' with result renormalized to $1_0000.

if (x & $fff == $800)

value := $1_0000

else

if (x & $800)

t := -x & $7ff

else

t := x & $7ff

value := word[*SINE][*t] - (t > 707)

if (x & $1000)

value := -value

It would be interesting to see if further refinements can be made, for example, by considering neighboring table values to determine which direction the value in question was rounded. But this simple correction, at least, is an improvement.

-Phil