mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[poincare] Wrap the std::complex methods in Poincare to choose the right branch cut
and avoid residual real or imaginary pars due to lost precision
This commit is contained in:
@@ -9,6 +9,8 @@ namespace Poincare {
|
||||
|
||||
class ApproximationEngine {
|
||||
public:
|
||||
template <typename T> static std::complex<T> truncateRealOrImaginaryPartAccordingToArgument(std::complex<T> c);
|
||||
|
||||
template <typename T> using ComplexCompute = std::complex<T>(*)(const std::complex<T>, Expression::AngleUnit angleUnit);
|
||||
template<typename T> static Evaluation<T> * map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute<T> compute);
|
||||
|
||||
|
||||
@@ -27,9 +27,7 @@ private:
|
||||
/* Simplification */
|
||||
Expression * shallowReduce(Context& context, AngleUnit angleUnit) override;
|
||||
/* Evaluation */
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return Trigonometry::computeInverseOnComplex(c, angleUnit, std::acos);
|
||||
}
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit);
|
||||
Evaluation<float> * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override {
|
||||
return ApproximationEngine::map<float>(this, context, angleUnit,computeOnComplex<float>);
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ private:
|
||||
/* Simplification */
|
||||
Expression * shallowReduce(Context & context, AngleUnit angleUnit) override;
|
||||
/* Evaluation */
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return Trigonometry::computeInverseOnComplex(c, angleUnit, std::asin);
|
||||
}
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit);
|
||||
Evaluation<float> * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override {
|
||||
return ApproximationEngine::map<float>(this, context, angleUnit, computeOnComplex<float>);
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ private:
|
||||
/* Simplification */
|
||||
Expression * shallowReduce(Context& context, AngleUnit angleUnit) override;
|
||||
/* Evaluation */
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return Trigonometry::computeInverseOnComplex(c, angleUnit, std::atan);
|
||||
}
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit);
|
||||
Evaluation<float> * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override {
|
||||
return ApproximationEngine::map<float>(this, context, angleUnit,computeOnComplex<float>);
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ public:
|
||||
Type type() const override;
|
||||
Expression * clone() const override;
|
||||
float characteristicXRange(Context & context, AngleUnit angleUnit) const override;
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit = AngleUnit::Radian) {
|
||||
return Trigonometry::computeDirectOnComplex(c, angleUnit, Complex<T>::cos);
|
||||
}
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit = AngleUnit::Radian);
|
||||
private:
|
||||
/* Layout */
|
||||
ExpressionLayout * createLayout(PrintFloat::Mode floatDisplayMode, int numberOfSignificantDigits) const override {
|
||||
|
||||
@@ -15,9 +15,7 @@ class Sine : public StaticHierarchy<1> {
|
||||
public:
|
||||
Type type() const override;
|
||||
Expression * clone() const override;
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit = AngleUnit::Radian) {
|
||||
return Trigonometry::computeDirectOnComplex(c, angleUnit, Complex<T>::sin);
|
||||
}
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit = AngleUnit::Radian);
|
||||
private:
|
||||
/* Layout */
|
||||
ExpressionLayout * createLayout(PrintFloat::Mode floatDisplayMode, int numberOfSignificantDigits) const override {
|
||||
|
||||
@@ -26,9 +26,7 @@ private:
|
||||
/* Simplication */
|
||||
Expression * shallowReduce(Context& context, AngleUnit angleUnit) override;
|
||||
/* Evaluation */
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit = AngleUnit::Radian) {
|
||||
return Trigonometry::computeDirectOnComplex(c, angleUnit, Complex<T>::tan);
|
||||
}
|
||||
template<typename T> static std::complex<T> computeOnComplex(const std::complex<T> c, AngleUnit angleUnit = AngleUnit::Radian);
|
||||
Evaluation<float> * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override {
|
||||
return ApproximationEngine::map<float>(this, context, angleUnit,computeOnComplex<float>);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ public:
|
||||
static bool ExpressionIsEquivalentToTangent(const Expression * e);
|
||||
constexpr static int k_numberOfEntries = 37;
|
||||
static Expression * table(const Expression * e, Expression::Type type, Context & context, Expression::AngleUnit angleUnit); // , Function f, bool inverse
|
||||
template <typename T> static std::complex<T> computeDirectOnComplex(const std::complex<T> c, Expression::AngleUnit angleUnit, ComplexFunction<T> approximate);
|
||||
template <typename T> static std::complex<T> computeInverseOnComplex(const std::complex<T> c, Expression::AngleUnit angleUnit, ComplexFunction<T> approximate);
|
||||
template <typename T> static std::complex<T> ConvertToRadian(const std::complex<T> c, Expression::AngleUnit angleUnit);
|
||||
template <typename T> static std::complex<T> ConvertRadianToAngleUnit(const std::complex<T> c, Expression::AngleUnit angleUnit);
|
||||
template <typename T> static std::complex<T> RoundToMeaningfulDigits(const std::complex<T> c);
|
||||
private:
|
||||
template <typename T> static T RoundToMeaningfulDigits(T f);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,23 @@ extern "C" {
|
||||
|
||||
namespace Poincare {
|
||||
|
||||
template <typename T> T absMod(T a, T b) {
|
||||
T result = std::fmod(std::fabs(a), b);
|
||||
return result > b/2 ? b-result : result;
|
||||
}
|
||||
|
||||
template <typename T> std::complex<T> ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(std::complex<T> c) {
|
||||
T arg = std::arg(c);
|
||||
T precision = 10*Expression::epsilon<T>();
|
||||
if (absMod<T>(arg, (T)M_PI) <= precision) {
|
||||
c.imag(0);
|
||||
}
|
||||
if (absMod<T>(arg-(T)M_PI/2.0, (T)M_PI) <= precision) {
|
||||
c.real(0);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
template<typename T> Evaluation<T> * ApproximationEngine::map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute<T> compute) {
|
||||
assert(expression->numberOfOperands() == 1);
|
||||
Evaluation<T> * input = expression->operand(0)->privateApproximate(T(), context, angleUnit);
|
||||
@@ -93,6 +110,8 @@ template<typename T> MatrixComplex<T> ApproximationEngine::elementWiseOnComplexM
|
||||
return result;
|
||||
}
|
||||
|
||||
template std::complex<float> Poincare::ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument<float>(std::complex<float>);
|
||||
template std::complex<double> Poincare::ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument<double>(std::complex<double>);
|
||||
template Poincare::Evaluation<float> * Poincare::ApproximationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexCompute<float> compute);
|
||||
template Poincare::Evaluation<double> * Poincare::ApproximationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexCompute<double> compute);
|
||||
template Poincare::Evaluation<float> * Poincare::ApproximationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexAndComplexReduction<float> computeOnComplexes, Poincare::ApproximationEngine::ComplexAndMatrixReduction<float> computeOnComplexAndMatrix, Poincare::ApproximationEngine::MatrixAndComplexReduction<float> computeOnMatrixAndComplex, Poincare::ApproximationEngine::MatrixAndMatrixReduction<float> computeOnMatrices);
|
||||
|
||||
@@ -30,4 +30,19 @@ Expression * ArcCosine::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> ArcCosine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
std::complex<T> result = std::acos(c);
|
||||
/* acos has a branch cut on ]-inf, -1[U]1, +inf[: it is then multivalued on
|
||||
* this cut. We followed the convention chosen by the lib c++ of llvm on
|
||||
* ]-inf+0i, -1+0i[ (warning: acos takes the other side of the cut values on
|
||||
* ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with
|
||||
* acos(-x) = Pi - acos(x) and tan(arccos(x)) = sqrt(1-x^2)/x. */
|
||||
if (c.imag() == 0 && c.real() > 1) {
|
||||
result.imag(-result.imag()); // other side of the cut
|
||||
}
|
||||
result = Trigonometry::RoundToMeaningfulDigits(result);
|
||||
return Trigonometry::ConvertRadianToAngleUnit(result, angleUnit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,4 +30,19 @@ Expression * ArcSine::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> ArcSine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
std::complex<T> result = std::asin(c);
|
||||
/* asin has a branch cut on ]-inf, -1[U]1, +inf[: it is then multivalued on
|
||||
* this cut. We followed the convention chosen by the lib c++ of llvm on
|
||||
* ]-inf+0i, -1+0i[ (warning: asin takes the other side of the cut values on
|
||||
* ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with
|
||||
* asin(-x) = -asin(x) and tan(arcsin(x)) = x/sqrt(1-x^2). */
|
||||
if (c.imag() == 0 && c.real() > 1) {
|
||||
result.imag(-result.imag()); // other side of the cut
|
||||
}
|
||||
result = Trigonometry::RoundToMeaningfulDigits(result);
|
||||
return Trigonometry::ConvertRadianToAngleUnit(result, angleUnit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,4 +30,19 @@ Expression * ArcTangent::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> ArcTangent::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
std::complex<T> result = std::atan(c);
|
||||
/* atan has a branch cut on ]-inf*i, -i[U]i, +inf*i[: it is then multivalued
|
||||
* on this cut. We followed the convention chosen by the lib c++ of llvm on
|
||||
* ]-i+0, -i*inf+0[ (warning: atan takes the other side of the cut values on
|
||||
* ]-i+0, -i*inf+0[) and choose the values on ]-inf*i, -i[ to comply with
|
||||
* atan(-x) = -atan(x) and sin(arctan(x)) = x/sqrt(1+x^2). */
|
||||
if (c.real() == 0 && c.imag() < -1) {
|
||||
result.real(-result.real()); // other side of the cut
|
||||
}
|
||||
result = Trigonometry::RoundToMeaningfulDigits(result);
|
||||
return Trigonometry::ConvertRadianToAngleUnit(result, angleUnit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,13 @@ float Cosine::characteristicXRange(Context & context, AngleUnit angleUnit) const
|
||||
return Trigonometry::characteristicXRange(this, context, angleUnit);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> Cosine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
std::complex<T> angleInput = Trigonometry::ConvertToRadian(c, angleUnit);
|
||||
std::complex<T> res = std::cos(angleInput);
|
||||
return Trigonometry::RoundToMeaningfulDigits(res);
|
||||
}
|
||||
|
||||
Expression * Cosine::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
Expression * e = Expression::shallowReduce(context, angleUnit);
|
||||
if (e != this) {
|
||||
|
||||
@@ -109,7 +109,6 @@ Expression * Complex<T>::complexToExpression(Expression::ComplexFormat complexFo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
Complex<T> * Complex<T>::createInverse() const {
|
||||
return new Complex<T>(Division::compute(std::complex<T>(1.0), *this));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <poincare/hyperbolic_arc_cosine.h>
|
||||
#include <poincare/simplification_engine.h>
|
||||
#include <poincare/trigonometry.h>
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
@@ -32,7 +33,12 @@ Expression * HyperbolicArcCosine::shallowReduce(Context& context, AngleUnit angl
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> HyperbolicArcCosine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return std::acosh(c);
|
||||
std::complex<T> result = std::acosh(c);
|
||||
/* asinh has a branch cut on ]-inf, 1]: it is then multivalued
|
||||
* on this cut. We followed the convention chosen by the lib c++ of llvm on
|
||||
* ]-inf+0i, 1+0i] (warning: atanh takes the other side of the cut values on
|
||||
* ]-inf-0i, 1-0i[).*/
|
||||
return Trigonometry::RoundToMeaningfulDigits(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <poincare/hyperbolic_arc_sine.h>
|
||||
#include <poincare/simplification_engine.h>
|
||||
#include <poincare/trigonometry.h>
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
@@ -32,7 +33,16 @@ Expression * HyperbolicArcSine::shallowReduce(Context& context, AngleUnit angleU
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> HyperbolicArcSine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return std::asinh(c);
|
||||
std::complex<T> result = std::asinh(c);
|
||||
/* asinh has a branch cut on ]-inf*i, -i[U]i, +inf*i[: it is then multivalued
|
||||
* on this cut. We followed the convention chosen by the lib c++ of llvm on
|
||||
* ]+i+0, +i*inf+0[ (warning: atanh takes the other side of the cut values on
|
||||
* ]+i-0, +i*inf+0[) and choose the values on ]-inf*i, -i[ to comply with
|
||||
* asinh(-x) = -asinh(x). */
|
||||
if (c.real() == 0 && c.imag() < 1) {
|
||||
result.real(-result.real()); // other side of the cut
|
||||
}
|
||||
return Trigonometry::RoundToMeaningfulDigits(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <poincare/hyperbolic_arc_tangent.h>
|
||||
#include <poincare/simplification_engine.h>
|
||||
#include <poincare/trigonometry.h>
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
@@ -32,7 +33,16 @@ Expression * HyperbolicArcTangent::shallowReduce(Context& context, AngleUnit ang
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> HyperbolicArcTangent::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return std::atanh(c);
|
||||
std::complex<T> result = std::atanh(c);
|
||||
/* atanh has a branch cut on ]-inf, -1[U]1, +inf[: it is then multivalued on
|
||||
* this cut. We followed the convention chosen by the lib c++ of llvm on
|
||||
* ]-inf+0i, -1+0i[ (warning: atanh takes the other side of the cut values on
|
||||
* ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with
|
||||
* atanh(-x) = -atanh(x) and sin(artanh(x)) = x/sqrt(1-x^2). */
|
||||
if (c.imag() == 0 && c.real() > 1) {
|
||||
result.imag(-result.imag()); // other side of the cut
|
||||
}
|
||||
return Trigonometry::RoundToMeaningfulDigits(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <poincare/division.h>
|
||||
#include <poincare/opposite.h>
|
||||
#include <poincare/simplification_engine.h>
|
||||
#include <poincare/trigonometry.h>
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
@@ -36,7 +37,7 @@ Expression * HyperbolicCosine::shallowReduce(Context& context, AngleUnit angleUn
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> HyperbolicCosine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return std::cosh(c);
|
||||
return Trigonometry::RoundToMeaningfulDigits(std::cosh(c));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <poincare/division.h>
|
||||
#include <poincare/opposite.h>
|
||||
#include <poincare/simplification_engine.h>
|
||||
#include <poincare/trigonometry.h>
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
@@ -36,7 +37,7 @@ Expression * HyperbolicSine::shallowReduce(Context& context, AngleUnit angleUnit
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> HyperbolicSine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return std::sinh(c);
|
||||
return Trigonometry::RoundToMeaningfulDigits(std::sinh(c));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <poincare/hyperbolic_sine.h>
|
||||
#include <poincare/division.h>
|
||||
#include <poincare/simplification_engine.h>
|
||||
#include <poincare/trigonometry.h>
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
@@ -35,7 +36,7 @@ Expression * HyperbolicTangent::shallowReduce(Context& context, AngleUnit angleU
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> HyperbolicTangent::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return std::tanh(c);
|
||||
return Trigonometry::RoundToMeaningfulDigits(std::tanh(c));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ Expression * Logarithm::clone() const {
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> Logarithm::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
/* log has a branch cut on ]-inf, 0]: it is then multivalued on this cut. We
|
||||
* followed the convention chosen by the lib c++ of llvm on ]-inf+0i, 0+0i]
|
||||
* (warning: log takes the other side of the cut values on ]-inf-0i, 0-0i]). */
|
||||
return std::log10(c);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ Expression * NaperianLogarithm::shallowReduce(Context& context, AngleUnit angleU
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> NaperianLogarithm::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
/* ln has a branch cut on ]-inf, 0]: it is then multivalued on this cut. We
|
||||
* followed the convention chosen by the lib c++ of llvm on ]-inf+0i, 0+0i]
|
||||
* (warning: ln takes the other side of the cut values on ]-inf-0i, 0-0i]). */
|
||||
return std::log(c);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ Evaluation<T> * NthRoot::templatedApproximate(Context& context, AngleUnit angleU
|
||||
if (base->type() == Evaluation<T>::Type::Complex && index->type() == Evaluation<T>::Type::Complex) {
|
||||
Complex<T> * basec = static_cast<Complex<T> *>(base);
|
||||
Complex<T> * indexc = static_cast<Complex<T> *>(index);
|
||||
result = Complex<T>::pow(*basec, std::complex<T>(1)/(*indexc));
|
||||
result = Power::compute(*basec, std::complex<T>(1)/(*indexc));
|
||||
}
|
||||
delete base;
|
||||
delete index;
|
||||
|
||||
@@ -119,7 +119,14 @@ Expression * Power::setSign(Sign s, Context & context, AngleUnit angleUnit) {
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> Power::compute(const std::complex<T> c, const std::complex<T> d) {
|
||||
return Complex<T>::pow(c, d);
|
||||
/* Openbsd trigonometric functions are numerical implementation and thus are
|
||||
* approximative.
|
||||
* The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to
|
||||
* avoid weird results as e(i*pi) = -1+6E-17*i, we compute the argument of
|
||||
* the result of c^d and if arg ~ 0 [Pi], we discard the residual imaginary
|
||||
* part and if arg ~ Pi/2 [Pi], we discard the residual real part. */
|
||||
std::complex<T> result = std::pow(c, d);
|
||||
return ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(result);
|
||||
}
|
||||
|
||||
template<typename T> MatrixComplex<T> Power::computeOnComplexAndMatrix(const std::complex<T> c, const MatrixComplex<T> n) {
|
||||
|
||||
@@ -25,6 +25,13 @@ float Sine::characteristicXRange(Context & context, AngleUnit angleUnit) const {
|
||||
return Trigonometry::characteristicXRange(this, context, angleUnit);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> Sine::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
std::complex<T> angleInput = Trigonometry::ConvertToRadian(c, angleUnit);
|
||||
std::complex<T> res = std::sin(angleInput);
|
||||
return Trigonometry::RoundToMeaningfulDigits(res);
|
||||
}
|
||||
|
||||
Expression * Sine::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
Expression * e = Expression::shallowReduce(context, angleUnit);
|
||||
if (e != this) {
|
||||
|
||||
@@ -26,7 +26,14 @@ int SquareRoot::writeTextInBuffer(char * buffer, int bufferSize, PrintFloat::Mod
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> SquareRoot::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
return Complex<T>::sqrt(c);
|
||||
std::complex<T> result = std::sqrt(c);
|
||||
/* Openbsd trigonometric functions are numerical implementation and thus are
|
||||
* approximative.
|
||||
* The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to
|
||||
* avoid weird results as sqrt(-1) = 6E-16+i, we compute the argument of
|
||||
* the result of sqrt(c) and if arg ~ 0 [Pi], we discard the residual imaginary
|
||||
* part and if arg ~ Pi/2 [Pi], we discard the residual real part. */
|
||||
return ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(result);
|
||||
}
|
||||
|
||||
Expression * SquareRoot::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
|
||||
@@ -26,6 +26,13 @@ float Tangent::characteristicXRange(Context & context, AngleUnit angleUnit) cons
|
||||
return Trigonometry::characteristicXRange(this, context, angleUnit);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> Tangent::computeOnComplex(const std::complex<T> c, AngleUnit angleUnit) {
|
||||
std::complex<T> angleInput = Trigonometry::ConvertToRadian(c, angleUnit);
|
||||
std::complex<T> res = std::tan(angleInput);
|
||||
return Trigonometry::RoundToMeaningfulDigits(res);
|
||||
}
|
||||
|
||||
Expression * Tangent::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
Expression * e = Expression::shallowReduce(context, angleUnit);
|
||||
if (e != this) {
|
||||
|
||||
@@ -229,27 +229,46 @@ Expression * Trigonometry::table(const Expression * e, Expression::Type type, Co
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::complex<T> Trigonometry::computeDirectOnComplex(const std::complex<T> c, Expression::AngleUnit angleUnit, ComplexFunction<T> approximate) {
|
||||
std::complex<T> input(c);
|
||||
std::complex<T> Trigonometry::ConvertToRadian(const std::complex<T> c, Expression::AngleUnit angleUnit) {
|
||||
if (angleUnit == Expression::AngleUnit::Degree) {
|
||||
input = input*std::complex<T>(M_PI/180.0);
|
||||
return c*std::complex<T>(M_PI/180.0);
|
||||
}
|
||||
return approximate(input);
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::complex<T> Trigonometry::ConvertRadianToAngleUnit(const std::complex<T> c, Expression::AngleUnit angleUnit) {
|
||||
if (angleUnit == Expression::AngleUnit::Degree) {
|
||||
return c*std::complex<T>(180/M_PI);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::complex<T> Trigonometry::computeInverseOnComplex(const std::complex<T> c, Expression::AngleUnit angleUnit, ComplexFunction<T> approximate) {
|
||||
std::complex<T> result = approximate(c);
|
||||
if (angleUnit == Expression::AngleUnit::Degree) {
|
||||
result *= 180/M_PI;
|
||||
}
|
||||
return result;
|
||||
T Trigonometry::RoundToMeaningfulDigits(T f) {
|
||||
/* Cheat: openbsd trigonometric functions are numerical implementation and
|
||||
* thus are approximative.
|
||||
* The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to
|
||||
* avoid weird results as acos(1) = 6E-17 or cos(Pi/2) = 4E-17, we keep only
|
||||
* 15 (or 7) decimals.
|
||||
* We can't do that for all evaluation as the user can operate on values as
|
||||
* small as 1E-308 (in double) and most results still be correct. */
|
||||
T precision = 10*Expression::epsilon<T>();
|
||||
return std::round(f/precision)*precision;
|
||||
}
|
||||
|
||||
template std::complex<double> Trigonometry::computeDirectOnComplex<double>(const std::complex<double>, Expression::AngleUnit, ComplexFunction<double>);
|
||||
template std::complex<float> Trigonometry::computeDirectOnComplex<float>(const std::complex<float>, Expression::AngleUnit, ComplexFunction<float>);
|
||||
template std::complex<double> Trigonometry::computeInverseOnComplex<double>(const std::complex<double>, Expression::AngleUnit, ComplexFunction<double>);
|
||||
template std::complex<float> Trigonometry::computeInverseOnComplex<float>(const std::complex<float>, Expression::AngleUnit, ComplexFunction<float>);
|
||||
template <typename T>
|
||||
std::complex<T> Trigonometry::RoundToMeaningfulDigits(const std::complex<T> c) {
|
||||
return std::complex<T>(RoundToMeaningfulDigits(c.real()), RoundToMeaningfulDigits(c.imag()));
|
||||
}
|
||||
|
||||
template std::complex<float> Trigonometry::ConvertToRadian<float>(std::complex<float>, Expression::AngleUnit);
|
||||
template std::complex<double> Trigonometry::ConvertToRadian<double>(std::complex<double>, Expression::AngleUnit);
|
||||
template std::complex<float> Trigonometry::ConvertRadianToAngleUnit<float>(std::complex<float>, Expression::AngleUnit);
|
||||
template std::complex<double> Trigonometry::ConvertRadianToAngleUnit<double>(std::complex<double>, Expression::AngleUnit);
|
||||
template std::complex<float> Trigonometry::RoundToMeaningfulDigits<float>(std::complex<float>);
|
||||
template std::complex<double> Trigonometry::RoundToMeaningfulDigits<double>(std::complex<double>);
|
||||
|
||||
}
|
||||
|
||||
@@ -22,63 +22,308 @@ QUIZ_CASE(poincare_parse_trigo) {
|
||||
}
|
||||
|
||||
QUIZ_CASE(poincare_trigo_evaluate) {
|
||||
// TODO: check with Léo how to handle cos/acos on complex in Degree...
|
||||
/* cos: R -> R (oscillator)
|
||||
* Ri -> R (even)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("cos(2)", "-4.1614683654714E-1", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("cos(2)", "0.9993908270191", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("cos(I-4)", "(-1.008625)-0.8893952*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("cos(I-4)", "0.997716+0.00121754*I", Degree, Cartesian, 6); // TODO: should we?
|
||||
assert_parsed_expression_evaluates_to<double>("sin(2)", "9.0929742682568E-1", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("sin(2)", "3.4899496702501E-2", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("sin(I-4)", "1.16781-0.768163*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("sin(I-4)", "(-0.0697671)+0.0174117*I", Degree, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<double>("tan(2)", "-2.1850398632615", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("tan(2)", "3.4920769491748E-2", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("tan(I-4)", "(-0.2735531)+1.00281*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tan(I-4)", "(-0.0699054)+0.0175368*I", Degree, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<double>("cosh(2)", "3.7621956910836", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("cosh(2)", "3.7621956910836", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(I-4)", "14.7547-22.9637*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(I-4)", "14.7547-22.9637*I", Degree, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<double>("sinh(2)", "3.626860407847", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("sinh(2)", "3.626860407847", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(I-4)", "(-14.7448)+22.9791*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(I-4)", "(-14.7448)+22.9791*I", Degree, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<double>("tanh(2)", "9.6402758007582E-1", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("tanh(2)", "9.6402758007582E-1", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.000279)+0.0006102409*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.000279)+0.0006102409*I", Degree);
|
||||
// Oscillator
|
||||
assert_parsed_expression_evaluates_to<float>("cos(P/2)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("cos(3*P/2)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("cos(3*P)", "-1", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("cos(-540)", "-1", Degree);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("cos(-2*I)", "3.7621956910836", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("cos(-2*I)", "1.0006092967033", Degree);
|
||||
// Symmetry: even
|
||||
assert_parsed_expression_evaluates_to<double>("cos(2*I)", "3.7621956910836", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("cos(2*I)", "1.0006092967033", Degree);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("cos(I-4)", "(-1.008625)-0.889395*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("cos(I-4)", "0.997716+0.001218*I", Degree, Cartesian, 6);
|
||||
|
||||
/* sin: R -> R (oscillator)
|
||||
* Ri -> Ri (odd)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("sin(2)", "9.0929742682568E-1", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("sin(2)", "0.0348994967025", Degree);
|
||||
// Oscillator
|
||||
assert_parsed_expression_evaluates_to<float>("sin(P/2)", "1", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("sin(3*P/2)", "-1", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sin(3*P)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sin(-540)", "0", Degree);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("sin(3*I)", "10.01787492741*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sin(3*I)", "0.052384*I", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("sin(-3*I)", "-10.01787492741*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sin(-3*I)", "-0.052384*I", Degree);
|
||||
// On: C
|
||||
assert_parsed_expression_evaluates_to<float>("sin(I-4)", "1.16781-0.768163*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("sin(I-4)", "(-0.069767)+0.017412*I", Degree, Cartesian, 6);
|
||||
|
||||
/* tan: R -> R (tangent-style)
|
||||
* Ri -> Ri (odd)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("tan(2)", "-2.1850398632615", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("tan(2)", "3.492076949175E-2", Degree);
|
||||
// Tangent-style
|
||||
assert_parsed_expression_evaluates_to<float>("tan(P/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("tan(3*P/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tan(3*P)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tan(-540)", "0", Degree);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("tan(-2*I)", "-9.6402758007582E-1*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tan(-2*I)", "-0.034892*I", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("tan(2*I)", "9.6402758007582E-1*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tan(2*I)", "0.034892*I", Degree);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("tan(I-4)", "(-0.273553)+1.002811*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tan(I-4)", "(-0.069905)+0.017537*I", Degree, Cartesian);
|
||||
|
||||
/* acos: [-1,1] -> R
|
||||
* ]-inf,-1[ -> Pi+R*i (odd imaginary)
|
||||
* ]1, inf[ -> R*i (odd imaginary)
|
||||
* R*i -> Pi/2+R*i (odd imaginary)
|
||||
*/
|
||||
// On [-1, 1]
|
||||
assert_parsed_expression_evaluates_to<double>("acos(0.5)", "1.0471975511966", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acos(0.5)", "60", Degree);
|
||||
// On [1, inf[
|
||||
assert_parsed_expression_evaluates_to<double>("acos(2)", "1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acos(2)", "75.456129290217*I", Degree);
|
||||
// Symmetry: odd on imaginary
|
||||
assert_parsed_expression_evaluates_to<double>("acos(-2)", "3.1415926535898-1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acos(-2)", "180-75.456129290217*I", Degree);
|
||||
// On ]-inf, -1[
|
||||
assert_parsed_expression_evaluates_to<double>("acos(-32)", "3.1415926535898-4.1586388532792*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("acos(-32)", "180-238.2725*I", Degree);
|
||||
// On R*i
|
||||
//assert_parsed_expression_evaluates_to<float>("acos(3*I)", "1.5707963-1.818446*I", Radian);
|
||||
//assert_parsed_expression_evaluates_to<float>("acos(3*I)", "90-104.1892*I", Degree);
|
||||
// Symmetry: odd on imaginary
|
||||
//assert_parsed_expression_evaluates_to<float>("acos(-3*I)", "1.5707963+1.818446*I", Radian);
|
||||
//assert_parsed_expression_evaluates_to<float>("acos(-3*I)", "90+104.1892*I", Degree);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("acos(I-4)", "2.8894-2.0966*I", Radian, Cartesian, 5);
|
||||
assert_parsed_expression_evaluates_to<float>("acos(I-4)", "165.551-120.126*I", Degree, Cartesian, 6);
|
||||
// Key values
|
||||
assert_parsed_expression_evaluates_to<double>("acos(0)", "90", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("acos(-1)", "180", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("acos(1)", "0", Degree);
|
||||
|
||||
/* asin: [-1,1] -> R
|
||||
* ]-inf,-1[ -> -Pi/2+R*i (odd)
|
||||
* ]1, inf[ -> Pi/2+R*i (odd)
|
||||
* R*i -> R*i (odd)
|
||||
*/
|
||||
// On [-1, 1]
|
||||
assert_parsed_expression_evaluates_to<double>("asin(0.5)", "0.5235987755983", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(0.5)", "30", Degree);
|
||||
// On [1, inf[
|
||||
assert_parsed_expression_evaluates_to<double>("asin(2)", "1.5707963267949-1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(2)", "90-75.456129290217*I", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("asin(-2)", "(-1.5707963267949)+1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(-2)", "(-90)+75.456129290217*I", Degree);
|
||||
// On ]-inf, -1[
|
||||
assert_parsed_expression_evaluates_to<float>("asin(-32)", "(-1.571)+4.159*I", Radian, Cartesian, 4);
|
||||
assert_parsed_expression_evaluates_to<float>("asin(-32)", "(-90)+238*I", Degree, Cartesian, 3);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("asin(3*I)", "1.8184464592321*I", Radian);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("asin(-3*I)", "-1.8184464592321*I", Radian);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("asin(I-4)", "(-1.3186)+2.0966*I", Radian, Cartesian, 5);
|
||||
assert_parsed_expression_evaluates_to<float>("asin(I-4)", "(-75.551)+120.13*I", Degree, Cartesian, 5);
|
||||
// Key values
|
||||
assert_parsed_expression_evaluates_to<double>("asin(0)", "0", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("asin(-1)", "-90", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(1)", "90", Degree);
|
||||
|
||||
/* atan: R -> R (odd)
|
||||
* [-i,i] -> R*i (odd)
|
||||
* ]-inf*i,-i[ -> -Pi/2+R*i (odd)
|
||||
* ]i, inf*i[ -> Pi/2+R*i (odd)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("atan(2)", "1.1071487177941", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("atan(2)", "63.434948822922", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("atan(I-4)", "(-1.338973)+0.05578589*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("atan(I-4)", "(-76.71748)+3.196296*I", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("atan(0.5)", "0.463648", Radian);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("atan(-2)", "-1.1071487177941", Radian);
|
||||
// On [-i, i]
|
||||
assert_parsed_expression_evaluates_to<float>("atan(0.2*I)", "0.202733*I", Radian, Cartesian, 6);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<float>("atan(-0.2*I)", "-0.202733*I", Radian, Cartesian, 6);
|
||||
// On [i, inf*i[
|
||||
assert_parsed_expression_evaluates_to<double>("atan(26*I)", "1.5707963267949+3.848052056806E-2*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("atan(26*I)", "90+2.2047714220162*I", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("atan(-26*I)", "(-1.5707963267949)-3.848052056806E-2*I", Radian);
|
||||
// On ]-inf*i, -i[
|
||||
assert_parsed_expression_evaluates_to<float>("atan(-3.4*I)", "(-1.570796)-0.303068*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("atan(-3.4*I)", "(-90)-17.3645*I", Degree, Cartesian, 6);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("atan(I-4)", "(-1.338973)+0.055786*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("atan(I-4)", "(-76.7175)+3.1963*I", Degree, Cartesian, 6);
|
||||
// Key values
|
||||
assert_parsed_expression_evaluates_to<float>("atan(0)", "0", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("atan(-I)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("atan(I)", "undef", Radian);
|
||||
|
||||
/* cosh: R -> R (even)
|
||||
* R*i -> R (oscillator)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("cosh(2)", "3.7621956910836", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("cosh(2)", "3.7621956910836", Degree);
|
||||
// Symmetry: even
|
||||
assert_parsed_expression_evaluates_to<double>("cosh(-2)", "3.7621956910836", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("cosh(-2)", "3.7621956910836", Degree);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("cosh(43*I)", "5.5511330152063E-1", Radian);
|
||||
// Oscillator
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(P*I/2)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(5*P*I/2)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(8*P*I/2)", "1", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(9*P*I/2)", "0", Radian);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(I-4)", "14.7547-22.9637*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("cosh(I-4)", "14.7547-22.9637*I", Degree, Cartesian, 6);
|
||||
|
||||
/* sinh: R -> R (odd)
|
||||
* R*i -> R*i (oscillator)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("sinh(2)", "3.626860407847", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("sinh(2)", "3.626860407847", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("sinh(-2)", "-3.626860407847", Radian);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("sinh(43*I)", "-0.8317747426286*I", Radian);
|
||||
// Oscillator
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(P*I/2)", "I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(5*P*I/2)", "I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(7*P*I/2)", "-I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(8*P*I/2)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(9*P*I/2)", "I", Radian);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(I-4)", "(-14.7448)+22.9791*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("sinh(I-4)", "(-14.7448)+22.9791*I", Degree, Cartesian, 6);
|
||||
|
||||
/* tanh: R -> R (odd)
|
||||
* R*i -> R*i (tangent-style)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("tanh(2)", "9.6402758007582E-1", Radian);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("tanh(-2)", "-9.6402758007582E-1", Degree);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("tanh(43*I)", "-1.4983873388552*I", Radian);
|
||||
// Tangent-style
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(P*I/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(5*P*I/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(7*P*I/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(8*P*I/2)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(9*P*I/2)", "undef", Radian);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.000279)+0.00061*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.000279)+0.00061*I", Degree);
|
||||
|
||||
/* acosh: [-1,1] -> R*i
|
||||
* ]-inf,-1[ -> Pi*i+R (even on real)
|
||||
* ]1, inf[ -> R (even on real)
|
||||
* ]-inf*i, 0[ -> -Pi/2*i+R (even on real)
|
||||
* ]0, inf*i[ -> Pi/2*i+R (even on real)
|
||||
*/
|
||||
// On [-1,1]
|
||||
assert_parsed_expression_evaluates_to<double>("acosh(2)", "1.3169578969248", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acosh(2)", "1.3169578969248", Degree);
|
||||
// On ]-inf, -1[
|
||||
assert_parsed_expression_evaluates_to<double>("acosh(-4)", "2.0634370688956+3.1415926535898*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("acosh(-4)", "2.06344+3.14159*I", Radian, Cartesian, 6);
|
||||
// On ]1,inf[: Symmetry: even on real
|
||||
assert_parsed_expression_evaluates_to<double>("acosh(4)", "2.0634370688956", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("acosh(4)", "2.063437", Radian);
|
||||
// On ]-inf*i, 0[
|
||||
assert_parsed_expression_evaluates_to<double>("acosh(-42*I)", "4.4309584920805-1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("acosh(-42*I)", "4.431-1.571*I", Radian, Cartesian, 4);
|
||||
// On ]0, i*inf[: Symmetry: even on real
|
||||
assert_parsed_expression_evaluates_to<double>("acosh(42*I)", "4.4309584920805+1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("acosh(42*I)", "4.431+1.571*I", Radian, Cartesian, 4);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("acosh(I-4)", "2.0966+2.8894*I", Radian, Cartesian, 5);
|
||||
assert_parsed_expression_evaluates_to<float>("acosh(I-4)", "2.0966+2.8894*I", Degree, Cartesian, 5);
|
||||
// Key values
|
||||
//assert_parsed_expression_evaluates_to<double>("acosh(-1)", "3.1415926535898*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acosh(1)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("acosh(0)", "1.570796*I", Radian);
|
||||
|
||||
/* acosh: R -> R (odd)
|
||||
* [-i,i] -> R*i (odd)
|
||||
* ]-inf*i,-i[ -> -Pi/2*i+R (odd)
|
||||
* ]i, inf*I[ -> Pi/2*I+R (odd)
|
||||
*/
|
||||
// On R
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(2)", "1.4436354751788", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(2)", "1.4436354751788", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(-2)", "-1.4436354751788", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(-2)", "-1.4436354751788", Degree);
|
||||
// On [-i,i]
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(0.2*I)", "2.0135792079033E-1*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("asinh(0.2*I)", "0.201358*I", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(-0.2*I)", "-2.0135792079033E-1*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("asinh(-0.2*I)", "-0.201358*I", Degree);
|
||||
// On ]-inf*i, -i[
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(-22*I)", "(-3.7836727043295)-1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("asinh(-22*I)", "(-3.784)-1.571*I", Degree, Cartesian, 4);
|
||||
// On ]i, inf*i[, Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(22*I)", "3.7836727043295+1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("asinh(22*I)", "3.784+1.571*I", Degree, Cartesian, 4);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("asinh(I-4)", "(-2.123)+0.2383*I", Radian, Cartesian, 4);
|
||||
assert_parsed_expression_evaluates_to<float>("asinh(I-4)", "(-2.123)+0.2383*I", Degree, Cartesian, 4);
|
||||
|
||||
/* acosh: [-1,1] -> R (odd)
|
||||
* ]-inf,-1[ -> Pi/2*i+R (odd)
|
||||
* ]1, inf[ -> -Pi/2*i+R (odd)
|
||||
* R*i -> R*i (odd)
|
||||
*/
|
||||
// On [-1,1]
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(0.4)", "0.4236489301936", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(0.4)", "0.4236489301936", Degree);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(-0.4)", "-0.4236489301936", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(-0.4)", "-0.4236489301936", Degree);
|
||||
// On ]1, inf[
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(4)", "0.255412811883-1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("atanh(4)", "0.255413-1.570796*I", Degree);
|
||||
// On ]-inf,-1[, Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(-4)", "(-0.255412811883)+1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("atanh(-4)", "(-0.255413)+1.570796*I", Degree);
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(4*I)", "1.325817663668*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("atanh(4*I)", "1.325818*I", Radian);
|
||||
// Symmetry: odd
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(-4*I)", "-1.325817663668*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("atanh(-4*I)", "-1.325818*I", Radian);
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("atanh(I-4)", "(-0.238878)+1.50862*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("atanh(I-4)", "(-0.238878)+1.50862*I", Degree, Cartesian, 6);
|
||||
|
||||
// WARNING: evaluate on branch cut can be multivalued
|
||||
assert_parsed_expression_evaluates_to<double>("acos(2)", "-1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acos(2)", "-75.456129290217*I", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(2)", "1.5707963267949+1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(2)", "90+75.456129290217*I", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(2)", "5.4930614433405E-1+1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acos(2)", "1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("acos(2)", "75.456129290217*I", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(2)", "1.5707963267949-1.3169578969248*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("asin(2)", "90-75.456129290217*I", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("atanh(2)", "5.4930614433405E-1-1.5707963267949*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("atan(2I)", "1.5707963267949+5.4930614433405E-1*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<double>("atan(2I)", "90+31.472923730945*I", Degree);
|
||||
assert_parsed_expression_evaluates_to<double>("asinh(2I)", "1.3169578969248+1.5707963267949*I", Radian);
|
||||
|
||||
Reference in New Issue
Block a user