mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-30 12:10:03 +02:00
[poincare] Fix precision of approximation of arc trigonomtry when the
result is in real numbers
This commit is contained in:
committed by
LeaNumworks
parent
06264b2ea8
commit
bc1a1c25d2
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user