1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 // Test Math.sin and Math.cos. 29 30 // Flags: --allow-natives-syntax 31 32 assertEquals("-Infinity", String(1/Math.sin(-0))); 33 assertEquals(1, Math.cos(-0)); 34 assertEquals("-Infinity", String(1/Math.tan(-0))); 35 36 // Assert that minus zero does not cause deopt. 37 function no_deopt_on_minus_zero(x) { 38 return Math.sin(x) + Math.cos(x) + Math.tan(x); 39 } 40 41 no_deopt_on_minus_zero(1); 42 no_deopt_on_minus_zero(1); 43 %OptimizeFunctionOnNextCall(no_deopt_on_minus_zero); 44 no_deopt_on_minus_zero(-0); 45 assertOptimized(no_deopt_on_minus_zero); 46 47 48 function sinTest() { 49 assertEquals(0, Math.sin(0)); 50 assertEquals(1, Math.sin(Math.PI / 2)); 51 } 52 53 function cosTest() { 54 assertEquals(1, Math.cos(0)); 55 assertEquals(-1, Math.cos(Math.PI)); 56 } 57 58 sinTest(); 59 cosTest(); 60 61 // By accident, the slow case for sine and cosine were both sine at 62 // some point. This is a regression test for that issue. 63 var x = Math.pow(2, 30); 64 assertTrue(Math.sin(x) != Math.cos(x)); 65 66 // Ensure that sine and log are not the same. 67 x = 0.5; 68 assertTrue(Math.sin(x) != Math.log(x)); 69 70 // Test against approximation by series. 71 var factorial = [1]; 72 var accuracy = 50; 73 for (var i = 1; i < accuracy; i++) { 74 factorial[i] = factorial[i-1] * i; 75 } 76 77 // We sum up in the reverse order for higher precision, as we expect the terms 78 // to grow smaller for x reasonably close to 0. 79 function precision_sum(array) { 80 var result = 0; 81 while (array.length > 0) { 82 result += array.pop(); 83 } 84 return result; 85 } 86 87 function sin(x) { 88 var sign = 1; 89 var x2 = x*x; 90 var terms = []; 91 for (var i = 1; i < accuracy; i += 2) { 92 terms.push(sign * x / factorial[i]); 93 x *= x2; 94 sign *= -1; 95 } 96 return precision_sum(terms); 97 } 98 99 function cos(x) { 100 var sign = -1; 101 var x2 = x*x; 102 x = x2; 103 var terms = [1]; 104 for (var i = 2; i < accuracy; i += 2) { 105 terms.push(sign * x / factorial[i]); 106 x *= x2; 107 sign *= -1; 108 } 109 return precision_sum(terms); 110 } 111 112 function abs_error(fun, ref, x) { 113 return Math.abs(ref(x) - fun(x)); 114 } 115 116 var test_inputs = []; 117 for (var i = -10000; i < 10000; i += 177) test_inputs.push(i/1257); 118 var epsilon = 0.0000001; 119 120 test_inputs.push(0); 121 test_inputs.push(0 + epsilon); 122 test_inputs.push(0 - epsilon); 123 test_inputs.push(Math.PI/2); 124 test_inputs.push(Math.PI/2 + epsilon); 125 test_inputs.push(Math.PI/2 - epsilon); 126 test_inputs.push(Math.PI); 127 test_inputs.push(Math.PI + epsilon); 128 test_inputs.push(Math.PI - epsilon); 129 test_inputs.push(- 2*Math.PI); 130 test_inputs.push(- 2*Math.PI + epsilon); 131 test_inputs.push(- 2*Math.PI - epsilon); 132 133 var squares = []; 134 for (var i = 0; i < test_inputs.length; i++) { 135 var x = test_inputs[i]; 136 var err_sin = abs_error(Math.sin, sin, x); 137 var err_cos = abs_error(Math.cos, cos, x) 138 assertEqualsDelta(0, err_sin, 1E-13); 139 assertEqualsDelta(0, err_cos, 1E-13); 140 squares.push(err_sin*err_sin + err_cos*err_cos); 141 } 142 143 // Sum squares up by adding them pairwise, to avoid losing precision. 144 while (squares.length > 1) { 145 var reduced = []; 146 if (squares.length % 2 == 1) reduced.push(squares.pop()); 147 // Remaining number of elements is even. 148 while(squares.length > 1) reduced.push(squares.pop() + squares.pop()); 149 squares = reduced; 150 } 151 152 var err_rms = Math.sqrt(squares[0] / test_inputs.length / 2); 153 assertEqualsDelta(0, err_rms, 1E-14); 154 155 assertEquals(-1, Math.cos({ valueOf: function() { return Math.PI; } })); 156 assertEquals(0, Math.sin("0x00000")); 157 assertEquals(1, Math.cos("0x00000")); 158 assertTrue(isNaN(Math.sin(Infinity))); 159 assertTrue(isNaN(Math.cos("-Infinity"))); 160 assertTrue(Math.tan(Math.PI/2) > 1e16); 161 assertTrue(Math.tan(-Math.PI/2) < -1e16); 162 assertEquals("-Infinity", String(1/Math.sin("-0"))); 163 164 // Assert that the remainder after division by pi is reasonably precise. 165 function assertError(expected, x, epsilon) { 166 assertTrue(Math.abs(x - expected) < epsilon); 167 } 168 169 assertEqualsDelta(0.9367521275331447, Math.cos(1e06), 1e-15); 170 assertEqualsDelta(0.8731196226768560, Math.cos(1e10), 1e-08); 171 assertEqualsDelta(0.9367521275331447, Math.cos(-1e06), 1e-15); 172 assertEqualsDelta(0.8731196226768560, Math.cos(-1e10), 1e-08); 173 assertEqualsDelta(-0.3499935021712929, Math.sin(1e06), 1e-15); 174 assertEqualsDelta(-0.4875060250875106, Math.sin(1e10), 1e-08); 175 assertEqualsDelta(0.3499935021712929, Math.sin(-1e06), 1e-15); 176 assertEqualsDelta(0.4875060250875106, Math.sin(-1e10), 1e-08); 177 assertEqualsDelta(0.7796880066069787, Math.sin(1e16), 1e-05); 178 assertEqualsDelta(-0.6261681981330861, Math.cos(1e16), 1e-05); 179 180 // Assert that remainder calculation terminates. 181 for (var i = -1024; i < 1024; i++) { 182 assertFalse(isNaN(Math.sin(Math.pow(2, i)))); 183 } 184 185 assertFalse(isNaN(Math.cos(1.57079632679489700))); 186 assertFalse(isNaN(Math.cos(-1e-100))); 187 assertFalse(isNaN(Math.cos(-1e-323))); 188 189 // Tests for specific values expected from the fdlibm implementation. 190 191 var two_32 = Math.pow(2, -32); 192 var two_28 = Math.pow(2, -28); 193 194 // Tests for Math.sin for |x| < pi/4 195 assertEquals(Infinity, 1/Math.sin(+0.0)); 196 assertEquals(-Infinity, 1/Math.sin(-0.0)); 197 // sin(x) = x for x < 2^-27 198 assertEquals(two_32, Math.sin(two_32)); 199 assertEquals(-two_32, Math.sin(-two_32)); 200 // sin(pi/8) = sqrt(sqrt(2)-1)/2^(3/4) 201 assertEquals(0.3826834323650898, Math.sin(Math.PI/8)); 202 assertEquals(-0.3826834323650898, -Math.sin(Math.PI/8)); 203 204 // Tests for Math.cos for |x| < pi/4 205 // cos(x) = 1 for |x| < 2^-27 206 assertEquals(1, Math.cos(two_32)); 207 assertEquals(1, Math.cos(-two_32)); 208 // Test KERNELCOS for |x| < 0.3. 209 // cos(pi/20) = sqrt(sqrt(2)*sqrt(sqrt(5)+5)+4)/2^(3/2) 210 assertEquals(0.9876883405951378, Math.cos(Math.PI/20)); 211 // Test KERNELCOS for x ~= 0.78125 212 assertEquals(0.7100335477927638, Math.cos(0.7812504768371582)); 213 assertEquals(0.7100338835660797, Math.cos(0.78125)); 214 // Test KERNELCOS for |x| > 0.3. 215 // cos(pi/8) = sqrt(sqrt(2)+1)/2^(3/4) 216 assertEquals(0.9238795325112867, Math.cos(Math.PI/8)); 217 // Test KERNELTAN for |x| < 0.67434. 218 assertEquals(0.9238795325112867, Math.cos(-Math.PI/8)); 219 220 // Tests for Math.tan for |x| < pi/4 221 assertEquals(Infinity, 1/Math.tan(0.0)); 222 assertEquals(-Infinity, 1/Math.tan(-0.0)); 223 // tan(x) = x for |x| < 2^-28 224 assertEquals(two_32, Math.tan(two_32)); 225 assertEquals(-two_32, Math.tan(-two_32)); 226 // Test KERNELTAN for |x| > 0.67434. 227 assertEquals(0.8211418015898941, Math.tan(11/16)); 228 assertEquals(-0.8211418015898941, Math.tan(-11/16)); 229 assertEquals(0.41421356237309503, Math.tan(Math.PI / 8)); 230 // crbug/427468 231 assertEquals(0.7993357819992383, Math.tan(0.6743358)); 232 233 // Tests for Math.sin. 234 assertEquals(0.479425538604203, Math.sin(0.5)); 235 assertEquals(-0.479425538604203, Math.sin(-0.5)); 236 assertEquals(1, Math.sin(Math.PI/2)); 237 assertEquals(-1, Math.sin(-Math.PI/2)); 238 // Test that Math.sin(Math.PI) != 0 since Math.PI is not exact. 239 assertEquals(1.2246467991473532e-16, Math.sin(Math.PI)); 240 assertEquals(-7.047032979958965e-14, Math.sin(2200*Math.PI)); 241 // Test Math.sin for various phases. 242 assertEquals(-0.7071067811865477, Math.sin(7/4 * Math.PI)); 243 assertEquals(0.7071067811865474, Math.sin(9/4 * Math.PI)); 244 assertEquals(0.7071067811865483, Math.sin(11/4 * Math.PI)); 245 assertEquals(-0.7071067811865479, Math.sin(13/4 * Math.PI)); 246 assertEquals(-3.2103381051568376e-11, Math.sin(1048576/4 * Math.PI)); 247 248 // Tests for Math.cos. 249 assertEquals(1, Math.cos(two_28)); 250 // Cover different code paths in KERNELCOS. 251 assertEquals(0.9689124217106447, Math.cos(0.25)); 252 assertEquals(0.8775825618903728, Math.cos(0.5)); 253 assertEquals(0.7073882691671998, Math.cos(0.785)); 254 // Test that Math.cos(Math.PI/2) != 0 since Math.PI is not exact. 255 assertEquals(6.123233995736766e-17, Math.cos(Math.PI/2)); 256 // Test Math.cos for various phases. 257 assertEquals(0.7071067811865474, Math.cos(7/4 * Math.PI)); 258 assertEquals(0.7071067811865477, Math.cos(9/4 * Math.PI)); 259 assertEquals(-0.7071067811865467, Math.cos(11/4 * Math.PI)); 260 assertEquals(-0.7071067811865471, Math.cos(13/4 * Math.PI)); 261 assertEquals(0.9367521275331447, Math.cos(1000000)); 262 assertEquals(-3.435757038074824e-12, Math.cos(1048575/2 * Math.PI)); 263 264 // Tests for Math.tan. 265 assertEquals(two_28, Math.tan(two_28)); 266 // Test that Math.tan(Math.PI/2) != Infinity since Math.PI is not exact. 267 assertEquals(1.633123935319537e16, Math.tan(Math.PI/2)); 268 // Cover different code paths in KERNELTAN (tangent and cotangent) 269 assertEquals(0.5463024898437905, Math.tan(0.5)); 270 assertEquals(2.0000000000000027, Math.tan(1.107148717794091)); 271 assertEquals(-1.0000000000000004, Math.tan(7/4*Math.PI)); 272 assertEquals(0.9999999999999994, Math.tan(9/4*Math.PI)); 273 assertEquals(-6.420676210313675e-11, Math.tan(1048576/2*Math.PI)); 274 assertEquals(2.910566692924059e11, Math.tan(1048575/2*Math.PI)); 275 276 // Test Hayne-Panek reduction. 277 assertEquals(0.377820109360752e0, Math.sin(Math.pow(2, 120))); 278 assertEquals(-0.9258790228548379e0, Math.cos(Math.pow(2, 120))); 279 assertEquals(-0.40806638884180424e0, Math.tan(Math.pow(2, 120))); 280 assertEquals(-0.377820109360752e0, Math.sin(-Math.pow(2, 120))); 281 assertEquals(-0.9258790228548379e0, Math.cos(-Math.pow(2, 120))); 282 assertEquals(0.40806638884180424e0, Math.tan(-Math.pow(2, 120))); 283