/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.objects.number; import static com.github.anba.es6draft.runtime.AbstractOperations.ToInt32; import static com.github.anba.es6draft.runtime.AbstractOperations.ToNumber; import static com.github.anba.es6draft.runtime.internal.Properties.createProperties; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.internal.Initializable; import com.github.anba.es6draft.runtime.internal.MathImpl; import com.github.anba.es6draft.runtime.internal.Properties.Attributes; import com.github.anba.es6draft.runtime.internal.Properties.Function; import com.github.anba.es6draft.runtime.internal.Properties.Value; import com.github.anba.es6draft.runtime.types.BuiltinSymbol; import com.github.anba.es6draft.runtime.types.Intrinsics; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject; /** * <h1>20 Numbers and Dates</h1><br> * <h2>20.2 The Math Object</h2> * <ul> * <li>20.2.1 Value Properties of the Math Object * <li>20.2.2 Function Properties of the Math Object * </ul> */ public final class MathObject extends OrdinaryObject implements Initializable { /** * Constructs a new Math object. * * @param realm * the realm object */ public MathObject(Realm realm) { super(realm); } @Override public void initialize(Realm realm) { setPrototype(realm.getIntrinsic(Intrinsics.ObjectPrototype)); createProperties(realm, this, ValueProperties.class); createProperties(realm, this, FunctionProperties.class); } /** * 20.2.1 Value Properties of the Math Object */ public enum ValueProperties { ; /** * 20.2.1.1 Math.E */ @Value(name = "E", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double E = Math.E; /** * 20.2.1.2 Math.LN10 */ @Value(name = "LN10", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double LN10 = Math.log(10d); /** * 20.2.1.3 Math.LN2 */ @Value(name = "LN2", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double LN2 = Math.log(2d); /** * 20.2.1.4 Math.LOG10E */ @Value(name = "LOG10E", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double LOG10E = Math.log10(Math.E); /** * 20.2.1.5 Math.LOG2E */ @Value(name = "LOG2E", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double LOG2E = 1d / Math.log(2d); /** * 20.2.1.6 Math.PI */ @Value(name = "PI", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double PI = Math.PI; /** * 20.2.1.7 Math.SQRT1_2 */ @Value(name = "SQRT1_2", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double SQRT1_2 = Math.sqrt(.5d); /** * 20.2.1.8 Math.SQRT2 */ @Value(name = "SQRT2", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Double SQRT2 = Math.sqrt(2d); /** * 20.2.1.9 Math[ @@toStringTag ] */ @Value(name = "[Symbol.toStringTag]", symbol = BuiltinSymbol.toStringTag, attributes = @Attributes(writable = false, enumerable = false, configurable = true)) public static final String toStringTag = "Math"; } /** * 20.2.2 Function Properties of the Math Object */ public enum FunctionProperties { ; /** * 20.2.2.1 Math.abs (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the absolute number value */ @Function(name = "abs", arity = 1) public static Object abs(ExecutionContext cx, Object thisValue, Object x) { return Math.abs(ToNumber(cx, x)); } /** * 20.2.2.2 Math.acos (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the arc cosine of <var>x</var> */ @Function(name = "acos", arity = 1) public static Object acos(ExecutionContext cx, Object thisValue, Object x) { return Math.acos(ToNumber(cx, x)); } /** * 20.2.2.3 Math.acosh(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the hyperbolic arc cosine of <var>x</var> */ @Function(name = "acosh", arity = 1) public static Object acosh(ExecutionContext cx, Object thisValue, Object x) { double d = ToNumber(cx, x); if (Double.isNaN(d) || d < 1.0) { return Double.NaN; } if (d == 1) { return +0.0; } if (d == Double.POSITIVE_INFINITY) { return Double.POSITIVE_INFINITY; } // return Math.log(d + Math.sqrt(d * d - 1.0)); return MathImpl.acosh(d); } /** * 20.2.2.4 Math.asin (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the arc sine of <var>x</var> */ @Function(name = "asin", arity = 1) public static Object asin(ExecutionContext cx, Object thisValue, Object x) { return Math.asin(ToNumber(cx, x)); } /** * 20.2.2.5 Math.asinh(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the hyperbolic arc sine of <var>x</var> */ @Function(name = "asinh", arity = 1) public static Object asinh(ExecutionContext cx, Object thisValue, Object x) { double d = ToNumber(cx, x); if (Double.isNaN(d) || d == 0.0 || Double.isInfinite(d)) { return d; } // return Math.log(d + Math.sqrt(d * d + 1.0)); return MathImpl.asinh(d); } /** * 20.2.2.6 Math.atan (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the arc tangent of <var>x</var> */ @Function(name = "atan", arity = 1) public static Object atan(ExecutionContext cx, Object thisValue, Object x) { return Math.atan(ToNumber(cx, x)); } /** * 20.2.2.7 Math.atanh(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the hyperbolic arc tangent of <var>x</var> */ @Function(name = "atanh", arity = 1) public static Object atanh(ExecutionContext cx, Object thisValue, Object x) { double d = ToNumber(cx, x); if (Double.isNaN(d) || d < -1.0 || d > 1.0) { return Double.NaN; } if (d == -1.0) { return Double.NEGATIVE_INFINITY; } if (d == +1.0) { return Double.POSITIVE_INFINITY; } if (d == 0.0) { return d; } // return (Math.log(1.0 + d) - Math.log(1.0 - d)) / 2.0; return MathImpl.atanh(d); } /** * 20.2.2.8 Math.atan2 (y, x) * * @param cx * the execution context * @param thisValue * the function this-value * @param y * the first argument number * @param x * the second argument number * @return the arc tangent of <var>y</var> and <var>x</var> */ @Function(name = "atan2", arity = 2) public static Object atan2(ExecutionContext cx, Object thisValue, Object y, Object x) { return Math.atan2(ToNumber(cx, y), ToNumber(cx, x)); } /** * 20.2.2.9 Math.cbrt(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the cubic root of its argument */ @Function(name = "cbrt", arity = 1) public static Object cbrt(ExecutionContext cx, Object thisValue, Object x) { return Math.cbrt(ToNumber(cx, x)); } /** * 20.2.2.10 Math.ceil (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the smallest integer number not less than <var>x</var> */ @Function(name = "ceil", arity = 1) public static Object ceil(ExecutionContext cx, Object thisValue, Object x) { return Math.ceil(ToNumber(cx, x)); } /** * 20.2.2.11 Math.clz32 ( x ) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the number of leading zeroes in the 32-bit integer representation of the number */ @Function(name = "clz32", arity = 1) public static Object clz32(ExecutionContext cx, Object thisValue, Object x) { return Integer.numberOfLeadingZeros((int) ToInt32(cx, x)); } /** * 20.2.2.12 Math.cos (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the cosine of <var>x</var> */ @Function(name = "cos", arity = 1) public static Object cos(ExecutionContext cx, Object thisValue, Object x) { return Math.cos(ToNumber(cx, x)); } /** * 20.2.2.13 Math.cosh(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the hyperbolic cosine of <var>x</var> */ @Function(name = "cosh", arity = 1) public static Object cosh(ExecutionContext cx, Object thisValue, Object x) { return Math.cosh(ToNumber(cx, x)); } /** * 20.2.2.14 Math.exp (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the value <i>e</i><span><sup><var>x</var></sup></span> */ @Function(name = "exp", arity = 1) public static Object exp(ExecutionContext cx, Object thisValue, Object x) { return Math.exp(ToNumber(cx, x)); } /** * 20.2.2.15 Math.expm1 (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the value <i>e</i><span><sup><var>x</var></sup></span>-1 */ @Function(name = "expm1", arity = 1) public static Object expm1(ExecutionContext cx, Object thisValue, Object x) { return Math.expm1(ToNumber(cx, x)); } /** * 20.2.2.16 Math.floor (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the largest integer number not greater than <var>x</var> */ @Function(name = "floor", arity = 1) public static Object floor(ExecutionContext cx, Object thisValue, Object x) { return Math.floor(ToNumber(cx, x)); } /** * 20.2.2.17 Math.fround (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the value <i>float32</i>(<var>x</var>) */ @Function(name = "fround", arity = 1) public static Object fround(ExecutionContext cx, Object thisValue, Object x) { double d = ToNumber(cx, x); /* step 1 */ if (Double.isNaN(d)) { return Double.NaN; } /* step 2 */ if (d == 0 || Double.isInfinite(d)) { return d; } /* steps 3-5 */ return (double) (float) d; } /** * 20.2.2.18 Math.hypot ( value1, value2, ...values ) * * @param cx * the execution context * @param thisValue * the function this-value * @param values * the argument values * @return the square root of the sum of squares of its arguments */ @Function(name = "hypot", arity = 2) public static Object hypot(ExecutionContext cx, Object thisValue, Object... values) { if (values.length == 0) { return +0.0; } if (values.length == 1) { return Math.abs(ToNumber(cx, values[0])); // sqrt(x * x) == |x| } if (values.length == 2) { return Math.hypot(ToNumber(cx, values[0]), ToNumber(cx, values[1])); } return hypotN(cx, values); } private static double hypotN(ExecutionContext cx, Object... values) { assert values.length >= 3; boolean hasInfinity = false, hasNaN = false; double max = 0; double[] numbers = new double[values.length]; for (int i = 0, len = values.length; i < len; ++i) { double v = ToNumber(cx, values[i]); if (Double.isInfinite(v)) { hasInfinity = true; } else if (Double.isNaN(v)) { hasNaN = true; } else { v = Math.abs(v); max = Math.max(max, v); numbers[i] = v; } } if (hasInfinity) { return Double.POSITIVE_INFINITY; } else if (hasNaN) { return Double.NaN; } else if (max == 0.0) { return +0.0; } // Kahan summation with normalisation double result = 0.0, c = 0.0; for (int i = 0, len = numbers.length; i < len; ++i) { double v = numbers[i] / max; double y = v * v - c; double t = result + y; c = (t - result) - y; result = t; } return Math.sqrt(result) * max; } /** * 20.2.2.19 Math.imul(x, y) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the first argument number * @param y * the second argument number * @return the integer multiplication of its arguments */ @Function(name = "imul", arity = 2) public static Object imul(ExecutionContext cx, Object thisValue, Object x, Object y) { return ToInt32(cx, x) * ToInt32(cx, y); } /** * 20.2.2.20 Math.log (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the natural logarithm of <var>x</var> */ @Function(name = "log", arity = 1) public static Object log(ExecutionContext cx, Object thisValue, Object x) { return Math.log(ToNumber(cx, x)); } /** * 20.2.2.21 Math.log1p (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the natural logarithm of <var>x</var> + 1 */ @Function(name = "log1p", arity = 1) public static Object log1p(ExecutionContext cx, Object thisValue, Object x) { return Math.log1p(ToNumber(cx, x)); } /** * 20.2.2.22 Math.log10 (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the decimal logarithm of <var>x</var> */ @Function(name = "log10", arity = 1) public static Object log10(ExecutionContext cx, Object thisValue, Object x) { return Math.log10(ToNumber(cx, x)); } /** * 20.2.2.23 Math.log2 (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the binary logarithm of <var>x</var> */ @Function(name = "log2", arity = 1) public static Object log2(ExecutionContext cx, Object thisValue, Object x) { // return Math.log(ToNumber(cx, x)) / Math.log(2d); return MathImpl.log2(ToNumber(cx, x)); } /** * 20.2.2.24 Math.max ( value1, value2, ...values ) * * @param cx * the execution context * @param thisValue * the function this-value * @param values * the argument values * @return the maximum number */ @Function(name = "max", arity = 2) public static Object max(ExecutionContext cx, Object thisValue, Object... values) { double result = Double.NEGATIVE_INFINITY; for (Object value : values) { result = Math.max(result, ToNumber(cx, value)); } return result; } /** * 20.2.2.25 Math.min ( value1, value2, ...values ) * * @param cx * the execution context * @param thisValue * the function this-value * @param values * the argument values * @return the minimum number */ @Function(name = "min", arity = 2) public static Object min(ExecutionContext cx, Object thisValue, Object... values) { double result = Double.POSITIVE_INFINITY; for (Object value : values) { result = Math.min(result, ToNumber(cx, value)); } return result; } /** * 20.2.2.26 Math.pow (x, y) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the first argument number * @param y * the second argument number * @return <var>x</var> raised to the power <var>y</var> */ @Function(name = "pow", arity = 2) public static Object pow(ExecutionContext cx, Object thisValue, Object x, Object y) { return Math.pow(ToNumber(cx, x), ToNumber(cx, y)); } /** * 20.2.2.27 Math.random ( ) * * @param cx * the execution context * @param thisValue * the function this-value * @return a random number in the interval [0, 1) */ @Function(name = "random", arity = 0) public static Object random(ExecutionContext cx, Object thisValue) { return cx.getRealm().getRandom().nextDouble(); } /** * 20.2.2.28 Math.round (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the nearest integer value to <var>x</var> */ @Function(name = "round", arity = 1) public static Object round(ExecutionContext cx, Object thisValue, Object x) { double d = ToNumber(cx, x); if (d != d || d == 0 || Double.isInfinite(d)) { return d; } if (d > 0 && d < 0.5) { return +0.0; } if (d < 0 && d >= -0.5) { return -0.0; } int exp = Math.getExponent(d); if (exp >= 52) { return d; } return Math.floor(d + 0.5); } /** * 20.2.2.29 Math.sign(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the sign of <var>x</var> */ @Function(name = "sign", arity = 1) public static Object sign(ExecutionContext cx, Object thisValue, Object x) { return Math.signum(ToNumber(cx, x)); } /** * 20.2.2.30 Math.sin (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the sine of <var>x</var> */ @Function(name = "sin", arity = 1) public static Object sin(ExecutionContext cx, Object thisValue, Object x) { return Math.sin(ToNumber(cx, x)); } /** * 20.2.2.31 Math.sinh(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the hyperbolic sine of <var>x</var> */ @Function(name = "sinh", arity = 1) public static Object sinh(ExecutionContext cx, Object thisValue, Object x) { return Math.sinh(ToNumber(cx, x)); } /** * 20.2.2.32 Math.sqrt (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the square root of its argument */ @Function(name = "sqrt", arity = 1) public static Object sqrt(ExecutionContext cx, Object thisValue, Object x) { return Math.sqrt(ToNumber(cx, x)); } /** * 20.2.2.33 Math.tan (x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the tangent of <var>x</var> */ @Function(name = "tan", arity = 1) public static Object tan(ExecutionContext cx, Object thisValue, Object x) { return Math.tan(ToNumber(cx, x)); } /** * 20.2.2.34 Math.tanh(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the hyperbolic tangent of <var>x</var> */ @Function(name = "tanh", arity = 1) public static Object tanh(ExecutionContext cx, Object thisValue, Object x) { return Math.tanh(ToNumber(cx, x)); } /** * 20.2.2.35 Math.trunc(x) * * @param cx * the execution context * @param thisValue * the function this-value * @param x * the argument number * @return the integral part of <var>x</var> */ @Function(name = "trunc", arity = 1) public static Object trunc(ExecutionContext cx, Object thisValue, Object x) { double d = ToNumber(cx, x); return d < 0 ? Math.ceil(d) : Math.floor(d); } } }