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 32assertEquals("-Infinity", String(1/Math.sin(-0))); 33assertEquals(1, Math.cos(-0)); 34assertEquals("-Infinity", String(1/Math.tan(-0))); 35 36// Assert that minus zero does not cause deopt. 37function no_deopt_on_minus_zero(x) { 38 return Math.sin(x) + Math.cos(x) + Math.tan(x); 39} 40 41no_deopt_on_minus_zero(1); 42no_deopt_on_minus_zero(1); 43%OptimizeFunctionOnNextCall(no_deopt_on_minus_zero); 44no_deopt_on_minus_zero(-0); 45assertOptimized(no_deopt_on_minus_zero); 46 47 48function sinTest() { 49 assertEquals(0, Math.sin(0)); 50 assertEquals(1, Math.sin(Math.PI / 2)); 51} 52 53function cosTest() { 54 assertEquals(1, Math.cos(0)); 55 assertEquals(-1, Math.cos(Math.PI)); 56} 57 58sinTest(); 59cosTest(); 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. 63var x = Math.pow(2, 30); 64assertTrue(Math.sin(x) != Math.cos(x)); 65 66// Ensure that sine and log are not the same. 67x = 0.5; 68assertTrue(Math.sin(x) != Math.log(x)); 69 70// Test against approximation by series. 71var factorial = [1]; 72var accuracy = 50; 73for (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. 79function precision_sum(array) { 80 var result = 0; 81 while (array.length > 0) { 82 result += array.pop(); 83 } 84 return result; 85} 86 87function 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 99function 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 112function abs_error(fun, ref, x) { 113 return Math.abs(ref(x) - fun(x)); 114} 115 116var test_inputs = []; 117for (var i = -10000; i < 10000; i += 177) test_inputs.push(i/1257); 118var epsilon = 0.0000001; 119 120test_inputs.push(0); 121test_inputs.push(0 + epsilon); 122test_inputs.push(0 - epsilon); 123test_inputs.push(Math.PI/2); 124test_inputs.push(Math.PI/2 + epsilon); 125test_inputs.push(Math.PI/2 - epsilon); 126test_inputs.push(Math.PI); 127test_inputs.push(Math.PI + epsilon); 128test_inputs.push(Math.PI - epsilon); 129test_inputs.push(- 2*Math.PI); 130test_inputs.push(- 2*Math.PI + epsilon); 131test_inputs.push(- 2*Math.PI - epsilon); 132 133var squares = []; 134for (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. 144while (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 152var err_rms = Math.sqrt(squares[0] / test_inputs.length / 2); 153assertEqualsDelta(0, err_rms, 1E-14); 154 155assertEquals(-1, Math.cos({ valueOf: function() { return Math.PI; } })); 156assertEquals(0, Math.sin("0x00000")); 157assertEquals(1, Math.cos("0x00000")); 158assertTrue(isNaN(Math.sin(Infinity))); 159assertTrue(isNaN(Math.cos("-Infinity"))); 160assertTrue(Math.tan(Math.PI/2) > 1e16); 161assertTrue(Math.tan(-Math.PI/2) < -1e16); 162assertEquals("-Infinity", String(1/Math.sin("-0"))); 163 164// Assert that the remainder after division by pi is reasonably precise. 165function assertError(expected, x, epsilon) { 166 assertTrue(Math.abs(x - expected) < epsilon); 167} 168 169assertEqualsDelta(0.9367521275331447, Math.cos(1e06), 1e-15); 170assertEqualsDelta(0.8731196226768560, Math.cos(1e10), 1e-08); 171assertEqualsDelta(0.9367521275331447, Math.cos(-1e06), 1e-15); 172assertEqualsDelta(0.8731196226768560, Math.cos(-1e10), 1e-08); 173assertEqualsDelta(-0.3499935021712929, Math.sin(1e06), 1e-15); 174assertEqualsDelta(-0.4875060250875106, Math.sin(1e10), 1e-08); 175assertEqualsDelta(0.3499935021712929, Math.sin(-1e06), 1e-15); 176assertEqualsDelta(0.4875060250875106, Math.sin(-1e10), 1e-08); 177assertEqualsDelta(0.7796880066069787, Math.sin(1e16), 1e-05); 178assertEqualsDelta(-0.6261681981330861, Math.cos(1e16), 1e-05); 179 180// Assert that remainder calculation terminates. 181for (var i = -1024; i < 1024; i++) { 182 assertFalse(isNaN(Math.sin(Math.pow(2, i)))); 183} 184 185assertFalse(isNaN(Math.cos(1.57079632679489700))); 186assertFalse(isNaN(Math.cos(-1e-100))); 187assertFalse(isNaN(Math.cos(-1e-323))); 188 189// Tests for specific values expected from the fdlibm implementation. 190 191var two_32 = Math.pow(2, -32); 192var two_28 = Math.pow(2, -28); 193 194// Tests for Math.sin for |x| < pi/4 195assertEquals(Infinity, 1/Math.sin(+0.0)); 196assertEquals(-Infinity, 1/Math.sin(-0.0)); 197// sin(x) = x for x < 2^-27 198assertEquals(two_32, Math.sin(two_32)); 199assertEquals(-two_32, Math.sin(-two_32)); 200// sin(pi/8) = sqrt(sqrt(2)-1)/2^(3/4) 201assertEquals(0.3826834323650898, Math.sin(Math.PI/8)); 202assertEquals(-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 206assertEquals(1, Math.cos(two_32)); 207assertEquals(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) 210assertEquals(0.9876883405951378, Math.cos(Math.PI/20)); 211// Test KERNELCOS for x ~= 0.78125 212assertEquals(0.7100335477927638, Math.cos(0.7812504768371582)); 213assertEquals(0.7100338835660797, Math.cos(0.78125)); 214// Test KERNELCOS for |x| > 0.3. 215// cos(pi/8) = sqrt(sqrt(2)+1)/2^(3/4) 216assertEquals(0.9238795325112867, Math.cos(Math.PI/8)); 217// Test KERNELTAN for |x| < 0.67434. 218assertEquals(0.9238795325112867, Math.cos(-Math.PI/8)); 219 220// Tests for Math.tan for |x| < pi/4 221assertEquals(Infinity, 1/Math.tan(0.0)); 222assertEquals(-Infinity, 1/Math.tan(-0.0)); 223// tan(x) = x for |x| < 2^-28 224assertEquals(two_32, Math.tan(two_32)); 225assertEquals(-two_32, Math.tan(-two_32)); 226// Test KERNELTAN for |x| > 0.67434. 227assertEquals(0.8211418015898941, Math.tan(11/16)); 228assertEquals(-0.8211418015898941, Math.tan(-11/16)); 229assertEquals(0.41421356237309503, Math.tan(Math.PI / 8)); 230// crbug/427468 231assertEquals(0.7993357819992383, Math.tan(0.6743358)); 232 233// Tests for Math.sin. 234assertEquals(0.479425538604203, Math.sin(0.5)); 235assertEquals(-0.479425538604203, Math.sin(-0.5)); 236assertEquals(1, Math.sin(Math.PI/2)); 237assertEquals(-1, Math.sin(-Math.PI/2)); 238// Test that Math.sin(Math.PI) != 0 since Math.PI is not exact. 239assertEquals(1.2246467991473532e-16, Math.sin(Math.PI)); 240assertEquals(-7.047032979958965e-14, Math.sin(2200*Math.PI)); 241// Test Math.sin for various phases. 242assertEquals(-0.7071067811865477, Math.sin(7/4 * Math.PI)); 243assertEquals(0.7071067811865474, Math.sin(9/4 * Math.PI)); 244assertEquals(0.7071067811865483, Math.sin(11/4 * Math.PI)); 245assertEquals(-0.7071067811865479, Math.sin(13/4 * Math.PI)); 246assertEquals(-3.2103381051568376e-11, Math.sin(1048576/4 * Math.PI)); 247 248// Tests for Math.cos. 249assertEquals(1, Math.cos(two_28)); 250// Cover different code paths in KERNELCOS. 251assertEquals(0.9689124217106447, Math.cos(0.25)); 252assertEquals(0.8775825618903728, Math.cos(0.5)); 253assertEquals(0.7073882691671998, Math.cos(0.785)); 254// Test that Math.cos(Math.PI/2) != 0 since Math.PI is not exact. 255assertEquals(6.123233995736766e-17, Math.cos(Math.PI/2)); 256// Test Math.cos for various phases. 257assertEquals(0.7071067811865474, Math.cos(7/4 * Math.PI)); 258assertEquals(0.7071067811865477, Math.cos(9/4 * Math.PI)); 259assertEquals(-0.7071067811865467, Math.cos(11/4 * Math.PI)); 260assertEquals(-0.7071067811865471, Math.cos(13/4 * Math.PI)); 261assertEquals(0.9367521275331447, Math.cos(1000000)); 262assertEquals(-3.435757038074824e-12, Math.cos(1048575/2 * Math.PI)); 263 264// Tests for Math.tan. 265assertEquals(two_28, Math.tan(two_28)); 266// Test that Math.tan(Math.PI/2) != Infinity since Math.PI is not exact. 267assertEquals(1.633123935319537e16, Math.tan(Math.PI/2)); 268// Cover different code paths in KERNELTAN (tangent and cotangent) 269assertEquals(0.5463024898437905, Math.tan(0.5)); 270assertEquals(2.0000000000000027, Math.tan(1.107148717794091)); 271assertEquals(-1.0000000000000004, Math.tan(7/4*Math.PI)); 272assertEquals(0.9999999999999994, Math.tan(9/4*Math.PI)); 273assertEquals(-6.420676210313675e-11, Math.tan(1048576/2*Math.PI)); 274assertEquals(2.910566692924059e11, Math.tan(1048575/2*Math.PI)); 275 276// Test Hayne-Panek reduction. 277assertEquals(0.377820109360752e0, Math.sin(Math.pow(2, 120))); 278assertEquals(-0.9258790228548379e0, Math.cos(Math.pow(2, 120))); 279assertEquals(-0.40806638884180424e0, Math.tan(Math.pow(2, 120))); 280assertEquals(-0.377820109360752e0, Math.sin(-Math.pow(2, 120))); 281assertEquals(-0.9258790228548379e0, Math.cos(-Math.pow(2, 120))); 282assertEquals(0.40806638884180424e0, Math.tan(-Math.pow(2, 120))); 283