[poincare] Cheat on evaluation of power, cosine and sine

Change-Id: Iebe4433b6bde35b92df78986f3360aa3a936024a
This commit is contained in:
Émilie Feral
2017-08-08 16:32:02 +02:00
parent c0a178b464
commit cdee87527e
5 changed files with 29 additions and 5 deletions

View File

@@ -141,6 +141,7 @@ public:
protected:
typedef float SinglePrecision;
typedef double DoublePrecision;
template<typename T> static T epsilon();
private:
virtual ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const = 0;
virtual Evaluation<float> * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const = 0;

View File

@@ -53,7 +53,16 @@ Complex<T> Complex<T>::Cartesian(T a, T b) {
template<typename T>
Complex<T> Complex<T>::Polar(T r, T th) {
return Complex(r*std::cos(th),r*std::sin(th));
T c = std::cos(th);
T s = std::sin(th);
/* Cheat: see comment on cosine.cpp.
* Sine and cosine openbsd immplementationd are numerical approximation.
* We though want to avoid evaluating e^(i*pi) to -1+1E-17i. We thus round
* cosine and sine results to 0 if they are negligible compared to the
* argument th. */
c = th != 0 && std::fabs(c/th) <= Expression::epsilon<T>() ? 0 : c;
s = th != 0 && std::fabs(s/th) <= Expression::epsilon<T>() ? 0 : s;
return Complex(r*c,r*s);
}
template<typename T>

View File

@@ -34,8 +34,14 @@ Complex<T> Cosine::compute(const Complex<T> c, AngleUnit angleUnit) {
input *= M_PI/180.0f;
}
T result = std::cos(input);
// TODO: See if necessary with double????
if (input != 0 && std::fabs(result/input) <= 1E-7f) {
/* Cheat: openbsd trigonometric functions (cos, sin & tan) 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
* cos(90) = 6E-17, we neglect the result when its ratio with the argument
* (pi in the exemple) is smaller than epsilon.
* 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. */
if (input != 0 && std::fabs(result/input) <= epsilon<T>()) {
return Complex<T>::Float(0);
}
return Complex<T>::Float(result);

View File

@@ -87,6 +87,11 @@ template<typename T> T Expression::approximate(const char * text, Context& conte
return result;
}
template<typename T> T Expression::epsilon() {
static T epsilon = sizeof(T) == sizeof(double) ? 1E-15 : 1E-7f;
return epsilon;
}
Expression * Expression::simplify() const {
/* We make sure that the simplification is deletable.
* Indeed, we don't want an expression with some parts deletable and some not
@@ -254,3 +259,5 @@ template double Poincare::Expression::approximate<double>(char const*, Poincare:
template float Poincare::Expression::approximate<float>(char const*, Poincare::Context&, Poincare::Expression::AngleUnit);
template double Poincare::Expression::approximate<double>(Poincare::Context&, Poincare::Expression::AngleUnit) const;
template float Poincare::Expression::approximate<float>(Poincare::Context&, Poincare::Expression::AngleUnit) const;
template double Poincare::Expression::epsilon<double>();
template float Poincare::Expression::epsilon<float>();

View File

@@ -34,8 +34,9 @@ Complex<T> Sine::compute(const Complex<T> c, AngleUnit angleUnit) {
input *= M_PI/180;
}
T result = std::sin(input);
// TODO: See if necessary with double????
if (input != 0 && std::fabs(result/input) <= 1E-7f) {
/* Cheat: see comment in cosine.cpp
* We cheat to avoid returning sin(Pi) = epsilon */
if (input != 0 && std::fabs(result/input) <= epsilon<T>()) {
return Complex<T>::Float(0);
}
return Complex<T>::Float(result);