[poincare/unit] Fix rational exponent rounding

In metric, sqrt(N) would be simplified to _N_kg^-0.5_m^-0.5_s instead of
_kg^0.5_m^0.5_s^-1.
We solve this issue by preventing simplification using derived units
in presence of rational exponents.

Change-Id: I97118bb32c963809c8d176b7b297d1682965e9af
This commit is contained in:
Gabriel Ozouf
2020-09-24 10:45:39 +02:00
committed by Émilie Feral
parent 4ba0603e0c
commit d862d5503c
3 changed files with 17 additions and 4 deletions

View File

@@ -440,10 +440,9 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
* - Repeat those steps until no more simplification is possible.
*/
Multiplication unitsAccu = Multiplication::Builder();
/* If exponents are not integers, FromBaseUnits will return the closest
* representation of units with base units and integer exponents.
* It cause no problem because once the best derived units are found,
* units is divided then multiplied by them. */
/* If exponents are not integers, FromBaseUnits will return a null
* vector, preventing any attempt at simplification. This protects us
* against undue "simplifications" such as _C^1.3 -> _C*_A^0.3*_s^0.3 */
UnitNode::Vector<int> unitsExponents = UnitNode::Vector<int>::FromBaseUnits(units);
size_t unitsSupportSize = unitsExponents.supportSize();
UnitNode::Vector<int> bestRemainderExponents;

View File

@@ -128,6 +128,12 @@ UnitNode::Vector<int> UnitNode::Vector<int>::FromBaseUnits(const Expression base
assert(exp.type() == ExpressionNode::Type::Rational);
// Using the closest integer to the exponent.
float exponentFloat = static_cast<const Rational &>(exp).node()->templatedApproximate<float>();
if (exponentFloat != std::round(exponentFloat)) {
/* If non-integer exponents are found, we round a null vector so that
* Multiplication::shallowBeautify will not attempt to find derived
* units. */
return vector;
}
/* We limit to INT_MAX / 3 because an exponent might get bigger with
* simplification. As a worst case scenario, (_s²_m²_kg/_A²)^n should be
* simplified to (_s^5_S)^n. If 2*n is under INT_MAX, 5*n might not. */

View File

@@ -361,6 +361,13 @@ QUIZ_CASE(poincare_simplification_units) {
assert_parsed_expression_simplify_to("1/(-3_°C)", Undefined::Name());
assert_parsed_expression_simplify_to("-1×100×_°C→_K", Undefined::Name());
/* Rational exponents */
assert_parsed_expression_simplify_to("√(_m)", "1×_m^\u00121/2\u0013");
assert_parsed_expression_simplify_to("√(_N)", "1×_kg^\u00121/2\u0013×_m^\u00121/2\u0013×_s^\u0012-1\u0013");
assert_parsed_expression_simplify_to("√(_N)", "1.5527410012845×_lb^\u00121/2\u0013×_yd^\u00121/2\u0013×_s^\u0012-1\u0013", User, Radian, Imperial);
assert_parsed_expression_simplify_to("_C^0.3", "1×_A^\u00123/10\u0013×_s^\u00123/10\u0013");
assert_parsed_expression_simplify_to("_kat_kg^-2.8", "1×_mol×_kg^\u0012-14/5\u0013×_s^\u0012-1\u0013");
/* Unit sum/subtract */
assert_parsed_expression_simplify_to("_m+_m", "2×_m");
assert_parsed_expression_simplify_to("_m-_m", "0×_m");
@@ -528,6 +535,7 @@ QUIZ_CASE(poincare_simplification_units) {
assert_parsed_expression_simplify_to("-2×_A", "-2×_A");
assert_parsed_expression_simplify_to("cos(1_s/1_s)", "cos(1)");
assert_parsed_expression_simplify_to("1_m+π_m+√(2)_m-cos(15)_m", "6.3154941288217×_m");
assert_parsed_expression_simplify_to("√(16×_m^2)", "4×_m");
}
QUIZ_CASE(poincare_simplification_power) {