diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2018-05-22 18:15:23 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-06-21 19:43:45 +0000 |
commit | 16d40d5c2784146dd8bffceb7df52a0a1761e4b4 (patch) | |
tree | c7afd37bc1b579fbbf336e96cccf6ad69dcbdee4 /src/qml/jsruntime/qv4mathobject.cpp | |
parent | 12bc11e5af20e68c504ab56de8ef0e0b76efd12c (diff) |
Extend V4's Math object with methods new in ES6
Added: acosh, asinh, atanh, cbrt, clz32, cosh, expm1, fround, hypot,
imul, sinh, tanh and trunc. Some needed hand-coding for android,
whose std:: is defective. Fixed some buglets in existing asin and
round in the process.
Change-Id: I0858d45430dc0f5944c53723545717ca1ffa6ef7
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4mathobject.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4mathobject.cpp | 198 |
1 files changed, 193 insertions, 5 deletions
diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 0e91a30ba3..652f6c603e 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -71,13 +71,23 @@ void Heap::MathObject::init() m->defineDefaultProperty(QStringLiteral("abs"), QV4::MathObject::method_abs, 1); m->defineDefaultProperty(QStringLiteral("acos"), QV4::MathObject::method_acos, 1); - m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 0); + m->defineDefaultProperty(QStringLiteral("acosh"), QV4::MathObject::method_acosh, 1); + m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 1); + m->defineDefaultProperty(QStringLiteral("asinh"), QV4::MathObject::method_asinh, 1); m->defineDefaultProperty(QStringLiteral("atan"), QV4::MathObject::method_atan, 1); + m->defineDefaultProperty(QStringLiteral("atanh"), QV4::MathObject::method_atanh, 1); m->defineDefaultProperty(QStringLiteral("atan2"), QV4::MathObject::method_atan2, 2); + m->defineDefaultProperty(QStringLiteral("cbrt"), QV4::MathObject::method_cbrt, 1); m->defineDefaultProperty(QStringLiteral("ceil"), QV4::MathObject::method_ceil, 1); + m->defineDefaultProperty(QStringLiteral("clz32"), QV4::MathObject::method_clz32, 1); m->defineDefaultProperty(QStringLiteral("cos"), QV4::MathObject::method_cos, 1); + m->defineDefaultProperty(QStringLiteral("cosh"), QV4::MathObject::method_cosh, 1); m->defineDefaultProperty(QStringLiteral("exp"), QV4::MathObject::method_exp, 1); + m->defineDefaultProperty(QStringLiteral("expm1"), QV4::MathObject::method_expm1, 1); m->defineDefaultProperty(QStringLiteral("floor"), QV4::MathObject::method_floor, 1); + m->defineDefaultProperty(QStringLiteral("fround"), QV4::MathObject::method_fround, 1); + m->defineDefaultProperty(QStringLiteral("hypot"), QV4::MathObject::method_hypot, 2); + m->defineDefaultProperty(QStringLiteral("imul"), QV4::MathObject::method_imul, 2); m->defineDefaultProperty(QStringLiteral("log"), QV4::MathObject::method_log, 1); m->defineDefaultProperty(QStringLiteral("log10"), QV4::MathObject::method_log10, 1); m->defineDefaultProperty(QStringLiteral("log1p"), QV4::MathObject::method_log1p, 1); @@ -89,8 +99,11 @@ void Heap::MathObject::init() m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1); m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1); m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1); + m->defineDefaultProperty(QStringLiteral("sinh"), QV4::MathObject::method_sinh, 1); m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1); m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1); + m->defineDefaultProperty(QStringLiteral("tanh"), QV4::MathObject::method_tanh, 1); + m->defineDefaultProperty(QStringLiteral("trunc"), QV4::MathObject::method_trunc, 1); ScopedString name(scope, scope.engine->newString(QStringLiteral("Math"))); m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); @@ -127,6 +140,19 @@ ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::acos(v))); } +ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : 2; + if (v < 1) + RETURN_RESULT(Encode(qt_qnan())); + +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::log(v +std::sqrt(v + 1) * std::sqrt(v - 1)))); +#else + RETURN_RESULT(Encode(std::acosh(v))); +#endif +} + ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : 2; @@ -136,6 +162,19 @@ ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::asin(v))); } +ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : 2; + if (v == 0.0) + RETURN_RESULT(Encode(v)); + +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::log(v +std::sqrt(1 + v * v)))); +#else + RETURN_RESULT(Encode(std::asinh(v))); +#endif +} + ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -145,6 +184,25 @@ ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::atan(v))); } +ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + +#ifdef Q_OS_ANDROID // incomplete std :-( + if (-1 < v && v < 1) + RETURN_RESULT(Encode(0.5 * (std::log(v + 1) - std::log(v - 1)))); + + if (v > 1 || v < -1) + RETURN_RESULT(Encode(qt_qnan())); + + RETURN_RESULT(Encode(copySign(qt_inf(), v))); +#else + RETURN_RESULT(Encode(std::atanh(v))); +#endif +} + ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc) { double v1 = argc ? argv[0].toNumber() : qt_qnan(); @@ -163,6 +221,16 @@ ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, co RETURN_RESULT(Encode(std::atan2(v1, v2))); } +ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(copySign(std::exp(std::log(std::abs(v)) / 3), v))); +#else + RETURN_RESULT(Encode(std::cbrt(v))); // cube root +#endif +} + ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -172,12 +240,24 @@ ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::ceil(v))); } +ReturnedValue MathObject::method_clz32(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + quint32 v = argc ? argv[0].toUInt32() : 0; + RETURN_RESULT(Encode(qint32(qCountLeadingZeroBits(v)))); +} + ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::cos(v))); } +ReturnedValue MathObject::method_cosh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + RETURN_RESULT(Encode(std::cosh(v))); +} + ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -191,6 +271,25 @@ ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, cons } } +ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (std::isnan(v) || qIsNull(v)) { + RETURN_RESULT(Encode(v)); + } else if (qt_is_inf(v)) { + if (copySign(1.0, v) == -1.0) + RETURN_RESULT(Encode(-1.0)); + else + RETURN_RESULT(Encode(qt_inf())); + } else { +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::exp(v) - 1)); +#else + RETURN_RESULT(Encode(std::expm1(v))); +#endif + } +} + ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -199,6 +298,53 @@ ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, co RETURN_RESULT(result); } +ReturnedValue MathObject::method_fround(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + else // convert to 32-bit float using roundTiesToEven, then convert back to 64-bit double + RETURN_RESULT(Encode(double(float(v)))); +} + +ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to + // avoid the loss of precision from overflows and underflows" (as std::hypot does). + double v = argc ? argv[0].toNumber() : 0; + // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ... +#ifdef Q_OS_ANDROID // incomplete std :-( + bool big = qt_is_inf(v), bad = std::isnan(v); + v *= v; + for (int i = 1; !big && i < argc; i++) { + double u = argv[i].toNumber(); + if (qt_is_inf(u)) + big = true; + if (std::isnan(u)) + bad = true; + v += u * u; + } + if (big) + RETURN_RESULT(Encode(qt_inf())); + if (bad) + RETURN_RESULT(Encode(qt_qnan())); + // Should actually check for {und,ov}erflow, but too fiddly ! + RETURN_RESULT(Primitive::fromDouble(sqrt(v))); +#else + for (int i = 1; i < argc; i++) + v = std::hypot(v, argv[i].toNumber()); +#endif + RETURN_RESULT(Primitive::fromDouble(v)); +} + +ReturnedValue MathObject::method_imul(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + quint32 a = argc ? argv[0].toUInt32() : 0; + quint32 b = argc > 0 ? argv[1].toUInt32() : 0; + qint32 product = a * b; + RETURN_RESULT(Encode(product)); +} + ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -235,8 +381,13 @@ ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, con if (v < 0) { RETURN_RESULT(Encode(qt_qnan())); } else { +#ifdef Q_OS_ANDROID // incomplete std :-( // Android ndk r10e doesn't have std::log2, so fall back. - RETURN_RESULT(Encode(std::log(v) / std::log(2.0))); + const double ln2 = std::log(2.0); + RETURN_RESULT(Encode(std::log(v) / ln2)); +#else + RETURN_RESULT(Encode(std::log2(v))); +#endif } } @@ -322,8 +473,11 @@ ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, c ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); - v = copySign(std::floor(v + 0.5), v); - RETURN_RESULT(Encode(v)); + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + + v = copySign(std::floor(v + 0.5), v); + RETURN_RESULT(Encode(v)); } ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -342,7 +496,19 @@ ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, con ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); - RETURN_RESULT(Encode(std::sin(v))); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::sin(v))); +} + +ReturnedValue MathObject::method_sinh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::sinh(v))); } ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -360,3 +526,25 @@ ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, cons RETURN_RESULT(Encode(std::tan(v))); } +ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::tanh(v))); +} + +ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); +#ifdef Q_OS_ANDROID // incomplete std :-( + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + // Nearest integer not greater in magnitude: + quint64 whole = std::abs(v); + RETURN_RESULT(Encode(copySign(whole, v))); +#else + RETURN_RESULT(Encode(std::trunc(v))); +#endif +} |