[poincare] Fix precision of approximation of arc trigonomtry when the

result is in real numbers
This commit is contained in:
Émilie Feral
2018-12-14 17:56:59 +01:00
committed by LeaNumworks
parent 06264b2ea8
commit bc1a1c25d2
4 changed files with 57 additions and 24 deletions

View File

@@ -25,14 +25,24 @@ Expression ArcCosineNode::shallowReduce(Context & context, Preferences::AngleUni
template<typename T>
Complex<T> ArcCosineNode::computeOnComplex(const std::complex<T> c, Preferences::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
std::complex<T> result;
if (c.imag() == 0 && std::fabs(c.real()) <= 1.0) {
/* acos: [-1;1] -> R
* In these cases we rather use std::acos(double) because acos on complexes
* is not as precise as pow on double in std library. For instance,
* - acos(complex<double>(0.03,0.0) = complex(1.54079,-1.11022e-16)
* - acos(0.03) = 1.54079 */
result = std::acos(c.real());
} else {
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, c);
return Complex<T>(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit));

View File

@@ -25,14 +25,24 @@ Expression ArcSineNode::shallowReduce(Context & context, Preferences::AngleUnit
template<typename T>
Complex<T> ArcSineNode::computeOnComplex(const std::complex<T> c, Preferences::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
std::complex<T> result;
if (c.imag() == 0 && std::fabs(c.real()) <= 1.0) {
/* asin: [-1;1] -> R
* In these cases we rather use std::asin(double) because asin on complexes
* is not as precise as asin on double in std library. For instance,
* - asin(complex<double>(0.03,0.0) = complex(0.0300045,1.11022e-16)
* - asin(0.03) = 0.0300045 */
result = std::asin(c.real());
} else {
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, c);
return Complex<T>(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit));

View File

@@ -21,14 +21,24 @@ int ArcTangentNode::serialize(char * buffer, int bufferSize, Preferences::PrintF
template<typename T>
Complex<T> ArcTangentNode::computeOnComplex(const std::complex<T> c, Preferences::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
std::complex<T> result;
if (c.imag() == 0 && std::fabs(c.real()) <= 1.0) {
/* atan: R -> R
* In these cases we rather use std::atan(double) because atan on complexes
* is not as precise as atan on double in std library. For instance,
* - atan(complex<double>(0.01,0.0) = complex(9.9996666866652E-3,5.5511151231258E-17)
* - atan(0.03) = 9.9996666866652E-3 */
result = std::atan(c.real());
} else {
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, c);
return Complex<T>(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit));

View File

@@ -93,6 +93,7 @@ QUIZ_CASE(poincare_trigo_evaluate) {
*/
// On [-1, 1]
assert_parsed_expression_evaluates_to<double>("acos(0.5)", "1.0471975511966", Radian);
assert_parsed_expression_evaluates_to<double>("acos(0.03)", "1.5407918249714", 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);
@@ -124,6 +125,7 @@ QUIZ_CASE(poincare_trigo_evaluate) {
*/
// On [-1, 1]
assert_parsed_expression_evaluates_to<double>("asin(0.5)", "0.5235987755983", Radian);
assert_parsed_expression_evaluates_to<double>("asin(0.03)", "3.0004501823477E-2", 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);
@@ -153,6 +155,7 @@ QUIZ_CASE(poincare_trigo_evaluate) {
*/
// On R
assert_parsed_expression_evaluates_to<double>("atan(2)", "1.1071487177941", Radian);
assert_parsed_expression_evaluates_to<double>("atan(0.01)", "9.9996666866652E-3", Radian);
assert_parsed_expression_evaluates_to<double>("atan(2)", "63.434948822922", Degree);
assert_parsed_expression_evaluates_to<float>("atan(0.5)", "0.4636476", Radian);
// Symmetry: odd