From 472521f2819c20b4b7fc3f6ecb331cdef1775f06 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 24 Apr 2013 01:59:17 +0200 Subject: [PATCH] Further details on classes like epsilon compare, further unit tests. --- .../samplesphysicalquantities.cpp | 6 ++-- src/blackmisc/basestreamstringifier.h | 3 +- src/blackmisc/coordinatetransformation.cpp | 2 +- src/blackmisc/mathematics.cpp | 28 ++++++++++++++++ src/blackmisc/mathematics.h | 28 ++++++++++++---- src/blackmisc/mathmatrixbase.cpp | 17 ++++++++-- src/blackmisc/mathmatrixbase.h | 29 ++++++++++++++++ src/blackmisc/mathvector3dbase.cpp | 4 +-- src/blackmisc/mathvector3dbase.h | 33 +++++++++++++++++++ src/blackmisc/pqangle.h | 9 +++-- src/blackmisc/pqbase.cpp | 30 +++++++---------- src/blackmisc/pqbase.h | 33 +++++++++---------- src/blackmisc/pqphysicalquantity.h | 14 +++----- src/blackmisc/pqunits.cpp | 2 +- src/blackmisc/pqunits.h | 4 +-- .../blackmisc/testphysicalquantitiesbase.cpp | 2 +- tests/blackmisc/testvectormatrixbase.cpp | 13 ++++++-- 17 files changed, 187 insertions(+), 70 deletions(-) diff --git a/samples/blackmiscquantities/samplesphysicalquantities.cpp b/samples/blackmiscquantities/samplesphysicalquantities.cpp index b018d0874..6594cc157 100644 --- a/samples/blackmiscquantities/samplesphysicalquantities.cpp +++ b/samples/blackmiscquantities/samplesphysicalquantities.cpp @@ -22,12 +22,12 @@ int CSamplesPhysicalQuantities::samples() CMeasurementPrefix pf1 = CMeasurementPrefix::h(); CMeasurementPrefix pf2 = CMeasurementPrefix::M(); - qDebug() << pf1 << pf2 << 1.0 * pf1; + qDebug() << pf1 << pf2 << (1.0 * pf1.toDouble()); CLengthUnit lu1(CLengthUnit::cm()); CLengthUnit lu2(CLengthUnit::ft()); - QString lu1s = lu1; - QString lu2s = lu2; + QString lu1s = lu1.toQString(); + QString lu2s = lu2.toQString(); qDebug() << lu1 << lu2 << lu1s << lu2s; const CLength l1(5.0, CLengthUnit::ft()); // 5 ft CLength l2(1, CLengthUnit::NM()); // 1NM diff --git a/src/blackmisc/basestreamstringifier.h b/src/blackmisc/basestreamstringifier.h index 02a528430..322b9e59d 100644 --- a/src/blackmisc/basestreamstringifier.h +++ b/src/blackmisc/basestreamstringifier.h @@ -75,8 +75,9 @@ public: /*! * \brief Cast as QString + * \remarks operator cast caused too many ambiguity trouble */ - operator QString() const + QString toQString() const { return this->stringForConverter(); } diff --git a/src/blackmisc/coordinatetransformation.cpp b/src/blackmisc/coordinatetransformation.cpp index d41b15e71..9ca6edf5b 100644 --- a/src/blackmisc/coordinatetransformation.cpp +++ b/src/blackmisc/coordinatetransformation.cpp @@ -66,7 +66,7 @@ CCoordinateEcef CCoordinateTransformation::toEcef(const CCoordinateNed &ned) */ CCoordinateEcef CCoordinateTransformation::toEcef(const CCoordinateGeodetic &geo) { - // TODO: Clarify the comparions with fixed angles (==90, ==180) -> what happens here + // TODO: Clarify the comparisons with fixed angles (==90, ==180) -> what happens here CLatitude lat = geo.latitude(); CLongitude lon = geo.longitude(); diff --git a/src/blackmisc/mathematics.cpp b/src/blackmisc/mathematics.cpp index ca46c0b3e..26e210c6f 100644 --- a/src/blackmisc/mathematics.cpp +++ b/src/blackmisc/mathematics.cpp @@ -34,5 +34,33 @@ qreal CMath::cubicRootReal(qreal x) return x < 0 ? -result : result; } +/* + * Round utility method + */ +double CMath::round(double value, int digits) +{ + // gosh, is there no Qt method for this??? It's year 2013 + double fractpart, intpart; + fractpart = modf(value, &intpart); + if (fractpart == 0) return value; // do not mess any "integers" to the worse + double m = pow(10.0, digits); + qint64 ri = qRound(value * m); // do not loose any range here + double rv = double(ri) / m; + return rv; +} + +/* + * Round by given epsilon value + */ +double CMath::roundEpsilon(double value, double epsilon) +{ + double fractpart, intpart; + fractpart = modf(value, &intpart); + if (fractpart == 0) return value; // do not mess any "integers" to the worse + qint64 ri = qRound(value / epsilon); + double rv = double(ri) * epsilon; // do not loose any range here + return rv; +} + } // namespace } // namespace diff --git a/src/blackmisc/mathematics.h b/src/blackmisc/mathematics.h index 4910c63c6..528a366da 100644 --- a/src/blackmisc/mathematics.h +++ b/src/blackmisc/mathematics.h @@ -15,10 +15,10 @@ namespace Math // Some namespace wide constant values //! Mathematical constant Pi -const qreal PI = 4.0 * qAtan(1.0); +const double PI = 4.0 * qAtan(1.0); //! 2 * Pi -const qreal TwoPI = 2.0 * PI; +const double TwoPI = 2.0 * PI; /*! * \brief Math utils @@ -33,14 +33,14 @@ public: * \param y * \return */ - static qreal hypot(qreal x, qreal y); + static double hypot(double x, double y); /*! * \brief Calculates the square of x * \param x * \return */ - static inline qreal square(qreal x) + static inline double square(double x) { return x * x; } @@ -50,7 +50,7 @@ public: * \param x * \return */ - static inline qreal cubic(const qreal x) + static inline double cubic(const double x) { return x * x * x; } @@ -60,7 +60,23 @@ public: * \param x * \return */ - static qreal cubicRootReal(qreal x); + static double cubicRootReal(double x); + + /*! + * \brief Utility round method + * \param value + * \param digits + * \return + */ + static double round(double value, int digits); + + /*! + * \brief Round by given epsilon, e.g. + * \param value + * \param epsilon + * \return + */ + static double roundEpsilon(double value, double epsilon); private: /*! diff --git a/src/blackmisc/mathmatrixbase.cpp b/src/blackmisc/mathmatrixbase.cpp index 44fa3e547..e5ad28153 100644 --- a/src/blackmisc/mathmatrixbase.cpp +++ b/src/blackmisc/mathmatrixbase.cpp @@ -69,11 +69,11 @@ template bool CMatrixBase bool CMatrixBase::allValuesEqual() const { - double v = this->getElement(0,0); + double v = this->getElement(0, 0); for (int r = 0; r < Rows; r++) { for (int c = 0; c < Columns; c++) @@ -84,6 +84,19 @@ template bool CMatrixBase void CMatrixBase::round() +{ + for (int r = 0; r < Rows; r++) + { + for (int c = 0; c < Columns; c++) + { + this->m_matrix(r, c) = CMath::roundEpsilon(this->m_matrix(r, c), 1E-10); + } + } +} /* * Convert to string diff --git a/src/blackmisc/mathmatrixbase.h b/src/blackmisc/mathmatrixbase.h index ee1ecc310..1156d91a0 100644 --- a/src/blackmisc/mathmatrixbase.h +++ b/src/blackmisc/mathmatrixbase.h @@ -203,6 +203,18 @@ public: return this->m_matrix.isIdentity(); } + /*! + * \brief Is identity matrix? Epsilon considered. + * \return + */ + bool isIdentityEpsilon() const + { + ImplMatrix m(0.0); + m += (*this); + m.round(); + return m.isIdentity(); + } + /*! * \brief Set as identity matrix * \return @@ -228,6 +240,18 @@ public: */ bool isZero() const; + /*! + * \brief Is identity matrix? Epsilon considered. + * \return + */ + bool isZeroEpsilon() const + { + ImplMatrix m(0.0); + m += (*this); + m.round(); + return m.isZero(); + } + /*! * \brief All values equal, if so matirx is not invertible * \return @@ -240,6 +264,11 @@ public: */ void fill(double value) { this->m_matrix.fill(value); } + /*! + * \brief Round all values + */ + void round(); + /*! * \brief Get element * \param row diff --git a/src/blackmisc/mathvector3dbase.cpp b/src/blackmisc/mathvector3dbase.cpp index 813d308f2..0dc6992a1 100644 --- a/src/blackmisc/mathvector3dbase.cpp +++ b/src/blackmisc/mathvector3dbase.cpp @@ -92,13 +92,13 @@ template void CVector3DBase::setElement(size_t row, } /* - * Corss product + * Cross product */ template ImplClass CVector3DBase::crossProduct(const ImplClass &otherVector) const { ImplClass v(otherVector); v.m_i = this->m_j * otherVector.m_k - this->m_k * otherVector.m_j; - v.m_j = this->m_k * otherVector.m_i - this->m_j * otherVector.m_k; + v.m_j = this->m_k * otherVector.m_i - this->m_i * otherVector.m_k; v.m_k = this->m_i * otherVector.m_j - this->m_j * otherVector.m_i; return v; } diff --git a/src/blackmisc/mathvector3dbase.h b/src/blackmisc/mathvector3dbase.h index bcf1c40d1..240b5422b 100644 --- a/src/blackmisc/mathvector3dbase.h +++ b/src/blackmisc/mathvector3dbase.h @@ -7,6 +7,7 @@ #define BLACKMISC_MATHVECTOR3DBASE_H #include "blackmisc/basestreamstringifier.h" +#include "blackmisc/mathematics.h" namespace BlackMisc { @@ -75,6 +76,26 @@ public: */ void setZero(); + /*! + * \brief Set zeros + */ + bool isZero() const + { + return this->m_i == 0 && this->m_j == 0 && this->m_k == 0; + } + + /*! + * \brief Is identity matrix? Epsilon considered. + * \return + */ + bool isZeroEpsilon() const + { + ImplClass v; + v += (*this); + v.round(); + return v.isZero(); + } + /*! * \brief Set zeros */ @@ -370,6 +391,18 @@ public: { return sqrt(this->lengthSquared()); } + + /*! + * \brief Round this vector + */ + void round() + { + const double epsilon = 1E-10; + this->m_i = BlackMisc::Math::CMath::roundEpsilon(this->m_i, epsilon); + this->m_j = BlackMisc::Math::CMath::roundEpsilon(this->m_j, epsilon); + this->m_k = BlackMisc::Math::CMath::roundEpsilon(this->m_k, epsilon); + } + }; } // namespace diff --git a/src/blackmisc/pqangle.h b/src/blackmisc/pqangle.h index 5de9eb9ab..4f0ffc207 100644 --- a/src/blackmisc/pqangle.h +++ b/src/blackmisc/pqangle.h @@ -6,6 +6,7 @@ #ifndef BLACKMISC_PQANGLE_H #define BLACKMISC_PQANGLE_H #include "blackmisc/pqphysicalquantity.h" +#include "blackmisc/mathematics.h" namespace BlackMisc { @@ -46,15 +47,17 @@ public: * \brief Convenience method PI * \return */ - const static double pi() { + const static double pi() + { return M_PI; } /*! * \brief Value as factor of PI (e.g. 0.5PI) * \return */ - double piFactor() const { - return CMeasurementUnit::round(this->convertedSiValueToDouble() / M_PI, 6); + double piFactor() const + { + return BlackMisc::Math::CMath::round(this->convertedSiValueToDouble() / M_PI, 6); } }; diff --git a/src/blackmisc/pqbase.cpp b/src/blackmisc/pqbase.cpp index 913d38a61..a59b2697b 100644 --- a/src/blackmisc/pqbase.cpp +++ b/src/blackmisc/pqbase.cpp @@ -1,9 +1,12 @@ -/* Copyright (C) 2013 VATSIM Community +/* Copyright (C) 2013 VATSIM Community / contributors * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "blackmisc/pqbase.h" +#include "blackmisc/mathematics.h" + +using namespace BlackMisc::Math; namespace BlackMisc { @@ -35,7 +38,7 @@ CMeasurementPrefix::CMeasurementPrefix(const CMeasurementPrefix &otherMultiplier /* * Assignment operator */ -CMeasurementPrefix& CMeasurementPrefix::operator=(const CMeasurementPrefix &otherMultiplier) +CMeasurementPrefix &CMeasurementPrefix::operator=(const CMeasurementPrefix &otherMultiplier) { if (this == &otherMultiplier) return *this; // Same object? Yes, so skip assignment, and just return *this @@ -170,7 +173,7 @@ QString CMeasurementUnit::valueRoundedWithUnit(double value, int digits) const double CMeasurementUnit::valueRounded(double value, int digits) const { if (digits < 0) digits = this->m_displayDigits; - return CMeasurementUnit::round(value, digits); + return CMath::round(value, digits); } /* @@ -179,30 +182,19 @@ double CMeasurementUnit::valueRounded(double value, int digits) const QString CMeasurementUnit::toQStringRounded(double value, int digits) const { if (digits < 0) digits = this->m_displayDigits; - double v = CMeasurementUnit::round(value, digits); + double v = CMath::round(value, digits); QString s = QLocale::system().toString(v, 'f', digits); return s; } -/* - * Round utility method - */ -double CMeasurementUnit::round(double value, int digits) -{ - // gosh, is there no Qt method for this??? - // It's year 2013 - double m = pow(10.0, digits); - double rv = double(qRound(value * m) / m); - return rv; -} - /* * Epsilon rounding */ -double CMeasurementUnit::epsilonRounding(double value) const +double CMeasurementUnit::epsilonUpRounding(double value) const { - // does notwork reliable with qRound for some reason - double v = floor((value + this->m_epsilon) / this->m_epsilon); + // Rounds a little up in order to avoid fractions + double eps = value > 0 ? this->m_epsilon : -1.0 * this->m_epsilon; + double v = floor((value + eps) / this->m_epsilon); v *= this->m_epsilon; return v; } diff --git a/src/blackmisc/pqbase.h b/src/blackmisc/pqbase.h index 57f96c36e..9c0f70d25 100644 --- a/src/blackmisc/pqbase.h +++ b/src/blackmisc/pqbase.h @@ -93,10 +93,6 @@ public: /*! * \brief Cast as double */ - operator double() const - { - return this->m_factor; - } /*! * \brief Factor, e.g.1000 for "kilo" @@ -106,6 +102,16 @@ public: { return this->m_factor; } + + /*! + * \brief Factor + * \return + */ + double toDouble() const + { + return this->getFactor(); + } + /*! * \brief Name, e.g. "kilo" * \return @@ -114,6 +120,7 @@ public: { return this->m_name; } + /*! * \brief Prefix, e.g. "k" for "kilo" * \return @@ -464,25 +471,17 @@ public: */ double conversionToUnit(double value, const CMeasurementUnit &to) const; - // -------------------------------------------------------------------- - // -- static - // -------------------------------------------------------------------- - - /*! - * \brief Utility round method - * \param value - * \param digits - * \return - */ - static double round(double value, int digits); - /*! * Epsilon rounding. In some conversion rouding is required to avoid * periodical numbers. * \param value * \return */ - double epsilonRounding(double value) const; + double epsilonUpRounding(double value) const; + + // -------------------------------------------------------------------- + // -- static + // -------------------------------------------------------------------- /*! * \brief Unit is not specified diff --git a/src/blackmisc/pqphysicalquantity.h b/src/blackmisc/pqphysicalquantity.h index fa58e6373..8ae257476 100644 --- a/src/blackmisc/pqphysicalquantity.h +++ b/src/blackmisc/pqphysicalquantity.h @@ -6,6 +6,7 @@ #ifndef BLACKMISC_PQPHYSICALQUANTITY_H #define BLACKMISC_PQPHYSICALQUANTITY_H +#include "blackmisc/mathematics.h" #include "blackmisc/pqbase.h" #include "blackmisc/pqunits.h" #include "blackmisc/debug.h" @@ -217,7 +218,8 @@ public: */ qint32 convertedSiValueToInteger() const { - return static_cast(CMeasurementUnit::round(this->m_convertedSiUnitValueD, 0)); + return static_cast( + BlackMisc::Math::CMath::round(this->m_convertedSiUnitValueD, 0)); } /*! @@ -255,15 +257,7 @@ public: */ void substractUnitValue(double value); - /*! - * \brief Cast as QString - */ - operator QString() const - { - return this->unitValueRoundedWithUnit(); - } - - /*! + /*! * \brief Multiply operator *= * \param multiply * \return diff --git a/src/blackmisc/pqunits.cpp b/src/blackmisc/pqunits.cpp index 33dbc1520..3676f36ec 100644 --- a/src/blackmisc/pqunits.cpp +++ b/src/blackmisc/pqunits.cpp @@ -37,7 +37,7 @@ double CAngleUnit::conversionSexagesimalFromSi(const CMeasurementUnit &angleUnit { // using rounding here, since fractions can lead to ugly sexagesimal conversion // e.g. 185.499999 gives 185 29' 59.9999" - value = angleUnit.epsilonRounding(value * 180 / M_PI); // degree + value = angleUnit.epsilonUpRounding(value * 180 / M_PI); // degree double v = floor(value); double c = value - v; double mr = c * 60.0; diff --git a/src/blackmisc/pqunits.h b/src/blackmisc/pqunits.h index 77a67953c..b8c6f29ae 100644 --- a/src/blackmisc/pqunits.h +++ b/src/blackmisc/pqunits.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 VATSIM Community / authors +/* Copyright (C) 2013 VATSIM Community / contributors * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -649,4 +649,4 @@ public: } // namespace } // namespace -#endif // BLACKMISC_PQUNITS_H +#endif // guard diff --git a/tests/blackmisc/testphysicalquantitiesbase.cpp b/tests/blackmisc/testphysicalquantitiesbase.cpp index 0d4e36ae7..48d53e2b5 100644 --- a/tests/blackmisc/testphysicalquantitiesbase.cpp +++ b/tests/blackmisc/testphysicalquantitiesbase.cpp @@ -90,7 +90,7 @@ void CTestPhysicalQuantitiesBase::frequencyTests() QVERIFY2(f1.valueRounded(CFrequencyUnit::kHz(), 2) == 1000, "Mega is 1000kHz"); QVERIFY2(f1.unitValueToDouble() == 1 , "1MHz"); QVERIFY2(f1.convertedSiValueToDouble() == 1000000 , "1E6 Hz"); - CFrequency f2(CMeasurementPrefix::M(), CFrequencyUnit::Hz()) ; // 1 Megahertz + CFrequency f2(CMeasurementPrefix::M().toDouble(), CFrequencyUnit::Hz()) ; // 1 Megahertz QVERIFY2(f1 == f2 , "MHz is 1E6 Hz"); } diff --git a/tests/blackmisc/testvectormatrixbase.cpp b/tests/blackmisc/testvectormatrixbase.cpp index 144edac3e..7c25275b0 100644 --- a/tests/blackmisc/testvectormatrixbase.cpp +++ b/tests/blackmisc/testvectormatrixbase.cpp @@ -18,7 +18,15 @@ void CTestVectorMatrixBase::vectorBasics() CVector3D v1(1); v1 *= 2.0; CVector3D v2(2); - QVERIFY2(v1 == v2, "Matrix should be zero"); + QVERIFY2(v1 == v2, "Vectors should be equal"); + CVector3D v3(1, 1, 1); + CVector3D v4(2, 2, 2); + CVector3D v5 = v3.crossProduct(v4); + QVERIFY2(v5.isZeroEpsilon(), "Cross product shall be {0, 0, 0}"); + CVector3D v6(1, 2, 3); + CVector3D v7(3, 4, 5); + QVERIFY2(v6.crossProduct(v7) == CVector3D(-2, 4, -2), "Cross product is wrong"); + QVERIFY2(v6.dotProduct(v7) == 26, "Dot product is wrong, 26 expected"); } /* @@ -58,7 +66,8 @@ void CTestVectorMatrixBase::matrixBasics() QVERIFY2(m1(0, 0) == 1 && m1(1, 0) == 2 && m1(0, 2) == 3, "Index error"); CMatrix3x3 mi = m1.inverse(invertible); CMatrix3x3 mid = m1 * mi; - QVERIFY2(mid.isIdentity(), qPrintable(QString("Multiply with inverse should be identity: %1").arg(mid))); + mid.round(); + QVERIFY2(mid.isIdentity(), qPrintable(QString("Multiply with inverse should be identity: %1").arg(mid.toQString()))); } } // namespace