From: jzakiya Date: 2009-12-28T11:00:14+09:00 Subject: Re: Trig value errors On Dec 27, 3:10 am, Brian Candler wrote: > If the objective is for sin() to be equally inaccurate/imprecise around > 0.0 as it is around 1.0, then try this: > > module Math >   def sin2(x) >     1.0 - (1.0 - sin(x)) >   end >   module_function :sin2 > end > > >> include Math > => Object > >> od = 2*PI/360 > > => 0.0174532925199433>> sin(od/1e8) > > => 1.74532925199433e-10>> sin2(od/1e8) > > => 1.74532943653105e-10>> sin(od/1e16) > > => 1.74532925199433e-18>> sin2(od/1e16) > => 0.0 > > -- > Posted viahttp://www.ruby-forum.com/. Here's the nature of the problem. Ruby currently gives incorrect values for x|y axis angles cos PI/2 => 6.12303176911189e-17 sin PI => 1.22460635382238e-16 cos 3*PI/2 => -1.83690953073357e-16 sin 2*PI => -2.44921970764475e-16 Thus, sin gives incorrect answers on the +|- x axis and, cos gives incorrect answers on the +|- y axis Here's the total solution. In order to produce the mathematically correct answers we must FORCE the answers to meet mathematical necessity. i.e. (1) 1 = cos^2 + sin^2 thus, (2) cos|sin = [1-(sin|cos)^2]^(1/2) also, (3) for angle x when sin|cos= -1|1 > cos|sin= 0.0 To 'fix totally' these errors in sin|cos near the x|y axis define sin1| cos1 here using Ruby's native cos|sin. def sin1(x) y=(1-cos(x)**2)**0.5 # calculate (2) return 0.0 if y==0 return sin(x) < 0 ? -y : y # for correct quadrant sign end def cos1(x) y=(1-sin(x)**2)**0.5 # calculate (2) return 0.0 if y==0 return cos(x) < 0 ? -y : y # for correct quadrant sign end Now sin|cos == sin1|cos1 for all angles -1|1 degree from each axis. For |x| < 1 degree from 0, 90, 180, 270, 360, and their multiples, sin1|cos1 will produce the mathematically correct complimentary answers to satisfy (1) and (3). In testing, I found only one instance on each side of each +|- axis where the floating point approximations gave answers which violated (3). Here's an example at 0 degrees|radians first let: >degrees = PI/180 x|y axis check >sin1(0*degrees) => 0.0 >cos1(0*degrees) => 1.0 >sin1(90*degrees) => 1.0 >cos1(90*degrees) => 0.0 >sin1(180*degrees) => 0.0 >cos1(180*degrees) => -1.0 >sin1(270*degrees) => -1.0 >cos1(270*degrees) => 0.0 >sin1(360*degrees) => 0.0 >cos1(360*degrees) => 1.0 1st Quadrant >sin1((0+1e-5)*degrees) => 1.74413620095247e-07 >cos1((0+1e-5)*degrees) => 0.999999999999985 >sin1((0+1e-6)*degrees) => 1.49011611938477e-08 >cos1((0+1e-6)*degrees) => 1.0 # this pair violates (3) >sin1((0+1e-7)*degrees) => 0.0 # correct now for >cos1((0+1e-7)*degrees) => 1.0 # both as angle gets >sin1((0+1e-8)*degrees) => 0.0 # closer and closer >cos1((0+1e-8)*degrees) => 1.0 # to x-axis 4th Quandrant >sin1((0-1e-5)*degrees) => -1.74413620095247e-07 >cos1((0-1e-5)*degrees) => 0.999999999999985 >sin1((0-1e-6)*degrees) => -1.49011611938477e-08 >cos1((0-1e-6)*degrees) => 1.0 # this pair violates (3) >sin1((0-1e-7)*degrees) => 0.0 # correct now for >cos1((0-1e-7)*degrees) => 1.0 # both as angle gets >sin1((0-1e-8)*degrees) => 0.0 # closer and closer >cos1((0-1e-8)*degrees) => 1.0 # to x-axis Also for 4th Quadrant >sin1((360-1e-5)*degrees)=> -1.74413620095247e-07 >cos1((360-1e-5)*degrees)=> 0.999999999999985 >sin1((360-1e-6)*degrees)=> -1.49011611938477e-08 >cos1((360-1e-6)*degrees)=> 1.0 # this pair violates (3) >sin1((360-1e-7)*degrees)=> 0.0 # correct now for >cos1((360-1e-7)*degrees)=> 1.0 # both as angle gets >sin1((360-1e-8)*degrees)=> 0.0 # closer and closer >cos1((360-1e-8)*degrees)=> 1.0 # to x-axis This shows that the precision, or the smallest unit value of an angle you can resolve, is on the order of |1e-5| at x|y-axis. Any thing smaller provides no precision toward the answer, no matter how many decimal places past this point you choose to calculate. These are the most non-linear regions in the series expansion for cos|sin. The same effect occurs as you increase the angle size, but you can get greater precision (angle resolution), with the greatest resolution occurring at |45| degrees. Here, you get about |1e-14| degrees of angular precsion. ie, as you go from 45-1e-14 > 45-1e-15 cos|sin stay the same for smaller and smaller delta angles. Now, sin1|cos1 (and tan1=sin1/cos1) meet the mathematical requirements of (1),(2),(3) and are as inherently precise as the underlying trig lib, and can become more precise as the trig lib functions get better and/or the cpu size grows They can NEVER BE WORSE than the native trig lib functions, and will ALWAYS be mathematically MORE accurate, because they are DESIGNED to meet mathematical trig requirements. I would PROPOSE that Ruby adopt the structure presented here and implement them in the C source to make them as fast and efficient to compute as possible. I would ENCOURAGE people to actually run|test the code against the present trig functions to see for themselves that they work for ALL angles. This was done with Ruby 1.9.1p243 on Intel P4 cpu system.