sin(x) and cos(x) have increasing error for |x|>128 #1255

Open
opened 2023-02-13 21:53:16 -06:00 by dpgeorge · 4 comments
dpgeorge commented 2023-02-13 21:53:16 -06:00 (Migrated from github.com)

When using single precision float, sin(x) and cos(x) (and possibly sincos and tan) are accurate for |x| < 128 (here, |x| means absolute value of x) but show increasing error as |x| grows beyond 128.

Here is a plot of the error of sin(x) as a function of x (compared to the single-precision float musl implementation of sin):

rp2040-sin-error

You can see that the error grows linearly beyond -128 / +128.

Looking at the code, there's a wrapper function that reduces the argument down to within +/- 128 and then calls the ROM code: https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_float/float_aeabi.S#L637-L664 . That reduction is probably introducing a small floating point error for each loop of the reduction (that's a guess, I did not investigate further).

(Note: this was found running MicroPython's tests/float/cmath_fun.py test, which fails on a Pico board due to the above problem.)

When using single precision float, `sin(x)` and `cos(x)` (and possibly `sincos` and `tan`) are accurate for `|x| < 128` (here, `|x|` means absolute value of `x`) but show increasing error as `|x|` grows beyond 128. Here is a plot of the error of `sin(x)` as a function of `x` (compared to the single-precision float musl implementation of `sin`): ![rp2040-sin-error](https://user-images.githubusercontent.com/6187689/218634149-cc18b137-41a3-4e7a-b2be-d3bcfedd26ed.png) You can see that the error grows linearly beyond -128 / +128. Looking at the code, there's a wrapper function that reduces the argument down to within +/- 128 and then calls the ROM code: https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_float/float_aeabi.S#L637-L664 . That reduction is probably introducing a small floating point error for each loop of the reduction (that's a guess, I did not investigate further). (Note: this was found running MicroPython's `tests/float/cmath_fun.py` test, which fails on a Pico board due to the above problem.)
znmeb commented 2023-02-13 22:08:58 -06:00 (Migrated from github.com)

One should never call sin (x) or cos(x) with |x| greater than 128! And code for sin(x) and cos(x) should take advantage of trigonometric identities to reduce the range of the argument. See https://realtimecollisiondetection.net/blog/?p=9, for example. The graph tells me the code may have range reduction but it's broken.

One should never call _sin (x)_ or _cos(x)_ with _|x|_ greater than 128! And code for _sin(x)_ and _cos(x)_ should take advantage of trigonometric identities to reduce the range of the argument. See https://realtimecollisiondetection.net/blog/?p=9, for example. The graph tells me the code may have range reduction but it's broken.
kilograham commented 2023-02-15 14:51:49 -06:00 (Migrated from github.com)

it is indeed broken ;-) oops.

it is indeed broken ;-) oops.
dpgeorge commented 2023-02-15 16:50:57 -06:00 (Migrated from github.com)

@kilograham did you intend to close this?

@kilograham did you intend to close this?
kilograham commented 2023-02-15 17:17:11 -06:00 (Migrated from github.com)

nope (wrong button)

nope (wrong button)
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-raspberrypi/pico-sdk#1255
No description provided.