From 515405a5df2ca6847f0e51799b4e5c01aed08618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 25 Feb 2020 14:39:29 +0100 Subject: [PATCH] [poincare] In Power::approximation and SquareRoot::approximation, the real (or imaginary) part negligence should depend on the argument value relatively to some other values (norms of the parameters) and not of its value absolutely! --- .../include/poincare/approximation_helper.h | 2 +- poincare/src/approximation_helper.cpp | 18 ++++++++++++------ poincare/src/power.cpp | 9 +++++++-- poincare/src/square_root.cpp | 8 ++++++-- poincare/test/approximation.cpp | 4 ++++ 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/poincare/include/poincare/approximation_helper.h b/poincare/include/poincare/approximation_helper.h index 169143faf..e284c18e7 100644 --- a/poincare/include/poincare/approximation_helper.h +++ b/poincare/include/poincare/approximation_helper.h @@ -10,7 +10,7 @@ namespace Poincare { namespace ApproximationHelper { template int PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); - template std::complex TruncateRealOrImaginaryPartAccordingToArgument(std::complex c); + template std::complex TruncateRealOrImaginaryPartAccordingToArgument(std::complex c, T norm1, T norm2 = 1.0); template using ComplexCompute = Complex(*)(const std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); template Evaluation Map(const ExpressionNode * expression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute compute); diff --git a/poincare/src/approximation_helper.cpp b/poincare/src/approximation_helper.cpp index 32f1f88ad..58b655d5d 100644 --- a/poincare/src/approximation_helper.cpp +++ b/poincare/src/approximation_helper.cpp @@ -15,6 +15,10 @@ template T absMod(T a, T b) { return result > b/2 ? b-result : result; } +template bool isNegligeable(T x, T precision, T norm1, T norm2) { + return x <= precision && x/norm1 <= precision && x/norm2 <= precision; +} + static inline int absInt(int x) { return x < 0 ? -x : x; } template int ApproximationHelper::PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { @@ -27,13 +31,15 @@ template int ApproximationHelper::PositiveIntegerApproximationIfPos return absInt((int)scalar); } -template std::complex ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex c) { +template std::complex ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex c, T norm1, T norm2) { T arg = std::arg(c); - T precision = 10*Expression::Epsilon(); - if (absMod(arg, (T)M_PI) <= precision) { + T precision = 10.0*Expression::Epsilon(); + T argModPi = absMod(arg, (T)M_PI); + T argModHalfPi = absMod(arg-(T)M_PI/2.0, (T)M_PI); + if (isNegligeable(argModPi, precision, norm1, norm2)) { c.imag(0); } - if (absMod(arg-(T)M_PI/2.0, (T)M_PI) <= precision) { + if (isNegligeable(argModHalfPi, precision, norm1, norm2)) { c.real(0); } return c; @@ -106,8 +112,8 @@ template MatrixComplex ApproximationHelper::ElementWiseOnComplexM template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); -template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); -template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); +template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex,float,float); +template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex,double,double); template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); template Poincare::Evaluation Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction computeOnMatrices); diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 495aa697a..798de23ae 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -151,8 +151,13 @@ Complex PowerNode::compute(const std::complex c, const std::complex d, * 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. */ - return Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); + * part and if arg ~ Pi/2 [Pi], we discard the residual real part. + * Let's determine when the arg [Pi] (or arg [Pi/2]) is negligeable: + * With c = r*e^(iθ) and d = x+iy, c^d = r^x*e^(yθ)*e^i(yln(r)+xθ) + * so arg(c^d) = y*ln(r)+xθ. + * We consider that arg[π] is negligeable if it is negligeable compared to + * norm(d) = sqrt(x^2+y^2) and ln(r) = ln(norm(c)).*/ + return Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result, std::log(std::abs(c)), std::abs(d))); } // Layout diff --git a/poincare/src/square_root.cpp b/poincare/src/square_root.cpp index ed0954476..360d6056d 100644 --- a/poincare/src/square_root.cpp +++ b/poincare/src/square_root.cpp @@ -35,8 +35,12 @@ Complex SquareRootNode::computeOnComplex(const std::complex c, Preferences * 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 Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); + * if arg ~ Pi/2 [Pi], we discard the residual real part. + * Let's determine when the arg [Pi] (or arg [Pi/2]) is negligeable: + * With c = r*e^(iθ), so arg(sqrt(c)) = θ/2. + * We consider that arg[Pi] is negligeable if it is negligeable compared to + * θ. */ + return Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result, std::arg(c))); } Expression SquareRootNode::shallowReduce(ReductionContext reductionContext) { diff --git a/poincare/test/approximation.cpp b/poincare/test/approximation.cpp index 89bc3b8ac..0be56dd6a 100644 --- a/poincare/test/approximation.cpp +++ b/poincare/test/approximation.cpp @@ -149,6 +149,10 @@ QUIZ_CASE(poincare_approximation_power) { assert_expression_approximates_to_scalar("2^3", 8.0f); assert_expression_approximates_to_scalar("(3+𝐢)^(4+𝐢)", NAN); assert_expression_approximates_to_scalar("[[1,2][3,4]]^2", NAN); + + + assert_expression_approximates_to("(-10)^0.00000001", "unreal", Radian, Real); + assert_expression_approximates_to("(-10)^0.00000001", "1+3.141593ᴇ-8×𝐢", Radian, Cartesian); } QUIZ_CASE(poincare_approximation_subtraction) {