From d2f6b36cf761a17b8a579d57f6ab87589213c361 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Thu, 4 Dec 2014 11:33:10 +0300 Subject: [PATCH] Implementing Math.sin and Math.cos built-in routines. --- src/libecmabuiltins/ecma-builtin-math.c | 120 +++++++++++++++++++++++- tests/jerry/math_trig.js | 71 ++++++++++++++ 2 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 tests/jerry/math_trig.js diff --git a/src/libecmabuiltins/ecma-builtin-math.c b/src/libecmabuiltins/ecma-builtin-math.c index a58aac898..a322eda30 100644 --- a/src/libecmabuiltins/ecma-builtin-math.c +++ b/src/libecmabuiltins/ecma-builtin-math.c @@ -175,10 +175,66 @@ ecma_builtin_math_object_ceil (ecma_value_t this_arg, /**< 'this' argument */ * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_math_object_cos (ecma_value_t this_arg, /**< 'this' argument */ +ecma_builtin_math_object_cos (ecma_value_t this_arg __unused, /**< 'this' argument */ ecma_value_t arg) /**< routine's argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg); + ecma_completion_value_t ret_value; + + ECMA_TRY_CATCH (arg_num_value, + ecma_op_to_number (arg), + ret_value); + + ecma_number_t *num_p = ecma_alloc_number (); + + const ecma_number_t arg_num = *ecma_get_number_from_completion_value (arg_num_value); + + if (ecma_number_is_nan (arg_num) + || ecma_number_is_infinity (arg_num)) + { + *num_p = ecma_number_make_nan (); + } + else if (ecma_number_is_zero (arg_num)) + { + *num_p = ECMA_NUMBER_ONE; + } + else + { + /* Taylor series of cos (x) around x = 0 is 1 - x^2/2! + x^4/4! - x^6/6! + ... */ + + ecma_number_t x = ecma_op_number_remainder (arg_num, 2 * ECMA_NUMBER_PI); + ecma_number_t neg_sqr_x = ecma_number_negate (ecma_number_multiply (x, x)); + + ecma_number_t sum = ECMA_NUMBER_ZERO; + ecma_number_t next_addendum = ECMA_NUMBER_ONE; + ecma_number_t next_factorial_factor = ECMA_NUMBER_ZERO; + + ecma_number_t diff = ecma_number_make_infinity (false); + + while ((ecma_number_is_zero (sum) && !ecma_number_is_zero (diff)) + || (!ecma_number_is_zero (sum) + && ecma_number_abs (ecma_number_divide (diff, sum)) > ecma_number_relative_eps)) + { + ecma_number_t next_sum = ecma_number_add (sum, next_addendum); + + next_addendum = ecma_number_multiply (next_addendum, neg_sqr_x); + next_factorial_factor = ecma_number_add (next_factorial_factor, ECMA_NUMBER_ONE); + next_addendum = ecma_number_divide (next_addendum, next_factorial_factor); + next_factorial_factor = ecma_number_add (next_factorial_factor, ECMA_NUMBER_ONE); + next_addendum = ecma_number_divide (next_addendum, next_factorial_factor); + + diff = ecma_number_abs (ecma_number_substract (sum, next_sum)); + + sum = next_sum; + } + + *num_p = sum; + } + + ret_value = ecma_make_normal_completion_value (ecma_make_number_value (num_p)); + + ECMA_FINALIZE (arg_num_value); + + return ret_value; } /* ecma_builtin_math_object_cos */ /** @@ -834,10 +890,66 @@ ecma_builtin_math_object_round (ecma_value_t this_arg __unused, /**< 'this' argu * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_math_object_sin (ecma_value_t this_arg, /**< 'this' argument */ +ecma_builtin_math_object_sin (ecma_value_t this_arg __unused, /**< 'this' argument */ ecma_value_t arg) /**< routine's argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg); + ecma_completion_value_t ret_value; + + ECMA_TRY_CATCH (arg_num_value, + ecma_op_to_number (arg), + ret_value); + + ecma_number_t *num_p = ecma_alloc_number (); + + const ecma_number_t arg_num = *ecma_get_number_from_completion_value (arg_num_value); + + if (ecma_number_is_nan (arg_num) + || ecma_number_is_infinity (arg_num)) + { + *num_p = ecma_number_make_nan (); + } + else if (ecma_number_is_zero (arg_num)) + { + *num_p = arg_num; + } + else + { + /* Taylor series of sin (x) around x = 0 is x - x^3/3! + x^5/5! - x^7/7! + ... */ + + ecma_number_t x = ecma_op_number_remainder (arg_num, 2 * ECMA_NUMBER_PI); + ecma_number_t neg_sqr_x = ecma_number_negate (ecma_number_multiply (x, x)); + + ecma_number_t sum = ECMA_NUMBER_ZERO; + ecma_number_t next_addendum = ecma_number_divide (x, ECMA_NUMBER_ONE); + ecma_number_t next_factorial_factor = ECMA_NUMBER_ONE; + + ecma_number_t diff = ecma_number_make_infinity (false); + + while ((ecma_number_is_zero (sum) && !ecma_number_is_zero (diff)) + || (!ecma_number_is_zero (sum) + && ecma_number_abs (ecma_number_divide (diff, sum)) > ecma_number_relative_eps)) + { + ecma_number_t next_sum = ecma_number_add (sum, next_addendum); + + next_addendum = ecma_number_multiply (next_addendum, neg_sqr_x); + next_factorial_factor = ecma_number_add (next_factorial_factor, ECMA_NUMBER_ONE); + next_addendum = ecma_number_divide (next_addendum, next_factorial_factor); + next_factorial_factor = ecma_number_add (next_factorial_factor, ECMA_NUMBER_ONE); + next_addendum = ecma_number_divide (next_addendum, next_factorial_factor); + + diff = ecma_number_abs (ecma_number_substract (sum, next_sum)); + + sum = next_sum; + } + + *num_p = sum; + } + + ret_value = ecma_make_normal_completion_value (ecma_make_number_value (num_p)); + + ECMA_FINALIZE (arg_num_value); + + return ret_value; } /* ecma_builtin_math_object_sin */ /** diff --git a/tests/jerry/math_trig.js b/tests/jerry/math_trig.js new file mode 100644 index 000000000..c462809f3 --- /dev/null +++ b/tests/jerry/math_trig.js @@ -0,0 +1,71 @@ +// Copyright 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var delta = 0.0001; +var mod_m = 1.0 - delta; +var mod_p = 1.0 + delta; + +assert (isNaN (Math.cos (NaN))); +assert ((Math.cos (+0.0)) == 1.0); +assert ((Math.cos (-0.0)) == 1.0); +assert (isNaN (Math.cos (Infinity))); +assert (isNaN (Math.cos (-Infinity))); + +assert (Math.cos (Math.PI) > -1.0 * mod_p); +assert (Math.cos (Math.PI) < -1.0 * mod_m); + +assert (Math.cos (Math.PI / 2) > -delta); +assert (Math.cos (Math.PI / 2) < +delta); +assert (Math.cos (-Math.PI / 2) > -delta); +assert (Math.cos (-Math.PI / 2) < +delta); + +assert (Math.cos (Math.PI / 4) > mod_m * Math.SQRT2 / 2); +assert (Math.cos (Math.PI / 4) < mod_p * Math.SQRT2 / 2); + +assert (Math.cos (-Math.PI / 4) > mod_m * Math.SQRT2 / 2); +assert (Math.cos (-Math.PI / 4) < mod_p * Math.SQRT2 / 2); + +assert (isNaN (Math.sin (NaN))); +assert (1.0 / Math.sin (0.0) == Infinity); +assert (1.0 / Math.sin (-0.0) == -Infinity); +assert (isNaN (Math.sin (Infinity))); +assert (isNaN (Math.sin (-Infinity))); + +assert (Math.sin (Math.PI) > -delta); +assert (Math.sin (Math.PI) < +delta); + +assert (Math.sin (Math.PI / 2) > 1.0 * mod_m); +assert (Math.sin (Math.PI / 2) < 1.0 * mod_p); + +assert (Math.sin (-Math.PI / 2) > -1.0 * mod_p); +assert (Math.sin (-Math.PI / 2) < -1.0 * mod_m); + +assert (Math.sin (Math.PI / 4) > mod_m * Math.SQRT2 / 2); +assert (Math.sin (Math.PI / 4) < mod_p * Math.SQRT2 / 2); + +assert (Math.sin (-Math.PI / 4) > -mod_p * Math.SQRT2 / 2); +assert (Math.sin (-Math.PI / 4) < -mod_m * Math.SQRT2 / 2); + +var step = 0.01; + +for (var x = -2 * Math.PI; x <= 2 * Math.PI; x += step) +{ + var s = Math.sin (x); + var c = Math.cos (x); + var sqr_s = s * s; + var sqr_c = c * c; + + assert (sqr_s + sqr_c > mod_m); + assert (sqr_s + sqr_c < mod_p); +}