[poincare/unit] Add Celsius and Fahrenheit

Temperatures can be converted to and from degree Celsius and Fahrenheit.
When used in non-trivial calculations, they are always reduced to undef,
as the rules for manipulating relative scales are not well defined.

Change-Id: If59e224a0e7f940b421bc894bbe2279c90f38d04
This commit is contained in:
Gabriel Ozouf
2020-08-19 15:25:06 +02:00
committed by Émilie Feral
parent 4274b558b6
commit d37540f032
14 changed files with 201 additions and 14 deletions

View File

@@ -160,7 +160,10 @@ const ToolboxMessageTree unitCurrentAmpereChildren[] = {
};
const ToolboxMessageTree unitTemperatureChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::UnitTemperatureKelvinSymbol, I18n::Message::UnitTemperatureKelvin)};
ToolboxMessageTree::Leaf(I18n::Message::UnitTemperatureKelvinSymbol, I18n::Message::UnitTemperatureKelvin),
ToolboxMessageTree::Leaf(I18n::Message::UnitTemperatureCelsiusSymbol, I18n::Message::UnitTemperatureCelsius),
ToolboxMessageTree::Leaf(I18n::Message::UnitTemperatureFahrenheitSymbol, I18n::Message::UnitTemperatureFahrenheit),
};
const ToolboxMessageTree unitAmountMoleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::UnitAmountMoleSymbol, I18n::Message::UnitAmountMole),

View File

@@ -35,6 +35,8 @@ UnitCurrentAmpereSymbol = "_A"
UnitCurrentAmpereMilliSymbol = "_mA"
UnitCurrentAmpereMicroSymbol = "_μA"
UnitTemperatureKelvinSymbol = "_K"
UnitTemperatureCelsiusSymbol = "_Cel"
UnitTemperatureFahrenheitSymbol = "_Fah"
UnitAmountMoleSymbol = "_mol"
UnitAmountMoleMilliSymbol = "_mmol"
UnitAmountMoleMicroSymbol = "_μmol"

View File

@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
UnitCurrentAmpereMicro = "Mikroampere"
UnitTemperatureMenu = "Temperatur"
UnitTemperatureKelvin = "Kelvin"
UnitTemperatureCelsius = "Celsius"
UnitTemperatureFahrenheit = "Fahrenheit"
UnitAmountMenu = "Stoffmenge"
UnitAmountMole = "Mol"
UnitAmountMoleMilli = "Millimol"

View File

@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
UnitCurrentAmpereMicro = "Microampere"
UnitTemperatureMenu = "Temperature"
UnitTemperatureKelvin = "Kelvin"
UnitTemperatureCelsius = "Celsius"
UnitTemperatureFahrenheit = "Fahrenheit"
UnitAmountMenu = "Amount of substance"
UnitAmountMole = "Mole"
UnitAmountMoleMilli = "Millimole"

View File

@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
UnitCurrentAmpereMicro = "Microampere"
UnitTemperatureMenu = "Temperature"
UnitTemperatureKelvin = "Kelvin"
UnitTemperatureCelsius = "Celsius"
UnitTemperatureFahrenheit = "Fahrenheit"
UnitAmountMenu = "Amount of substance"
UnitAmountMole = "Mole"
UnitAmountMoleMilli = "Millimole"

View File

@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampère"
UnitCurrentAmpereMicro = "Microampère"
UnitTemperatureMenu = "Température"
UnitTemperatureKelvin = "Kelvin"
UnitTemperatureCelsius = "Celsius"
UnitTemperatureFahrenheit = "Fahrenheit"
UnitAmountMenu = "Quantité de matière"
UnitAmountMole = "Mole"
UnitAmountMoleMilli = "Millimole"

View File

@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
UnitCurrentAmpereMicro = "Microampere"
UnitTemperatureMenu = "Temperatura"
UnitTemperatureKelvin = "Kelvin"
UnitTemperatureCelsius = "Celsius"
UnitTemperatureFahrenheit = "Fahrenheit"
UnitAmountMenu = "Quantità de materia"
UnitAmountMole = "Mole"
UnitAmountMoleMilli = "Millimole"

View File

@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampère"
UnitCurrentAmpereMicro = "Microampère"
UnitTemperatureMenu = "Temperatuur"
UnitTemperatureKelvin = "Kelvin"
UnitTemperatureCelsius = "Celsius"
UnitTemperatureFahrenheit = "Fahrenheit"
UnitAmountMenu = "Hoeveelheid stof"
UnitAmountMole = "Mol"
UnitAmountMoleMilli = "Millimol"

View File

@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Miliampere"
UnitCurrentAmpereMicro = "Microampere"
UnitTemperatureMenu = "Temperatura"
UnitTemperatureKelvin = "Kelvin"
UnitTemperatureCelsius = "Celsius"
UnitTemperatureFahrenheit = "Fahrenheit"
UnitAmountMenu = "Quantidade da substância"
UnitAmountMole = "Mole"
UnitAmountMoleMilli = "Milimole"

View File

@@ -69,6 +69,7 @@ public:
*(coefficientsAddresses[i]) = c;
}
bool operator==(const Vector &rhs) const { return time == rhs.time && distance == rhs.distance && mass == rhs.mass && current == rhs.current && temperature == rhs.temperature && amountOfSubstance == rhs.amountOfSubstance && luminuousIntensity == rhs.luminuousIntensity; }
bool operator!=(const Vector &rhs) const { return !(*this == rhs); }
void addAllCoefficients(const Vector other, int factor);
Expression toBaseUnits() const;
T time;
@@ -102,6 +103,7 @@ public:
m_inputPrefixable(inputPrefixable),
m_outputPrefixable(outputPrefixable)
{}
virtual const Vector<int> dimensionVector() const { return Vector<int>{.time = 0, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; };
virtual int numberOfRepresentatives() const { return 0; };
/* representativesOfSameDimension returns a pointer to the array containing
@@ -110,18 +112,20 @@ public:
virtual const Prefix * basePrefix() const { return Prefix::EmptyPrefix(); }
virtual bool isBaseUnit() const { return false; }
virtual const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const { return DefaultFindBestRepresentative(value, exponent, representativesOfSameDimension(), numberOfRepresentatives(), prefix); }
virtual bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const { return true; }
virtual bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const { return false; }
virtual int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { return 0; }
const char * rootSymbol() const { return m_rootSymbol; }
double ratio() const { return m_ratio; }
bool isInputPrefixable() const { return m_inputPrefixable != Prefixable::None; }
bool isOutputPrefixable() const { return m_outputPrefixable != Prefixable::None; }
bool canPrefix(const Prefix * prefix, bool input) const;
int serialize(char * buffer, int bufferSize, const Prefix * prefix) const;
bool canParseWithEquivalents(const char * symbol, size_t length, const Representative * * representative, const Prefix * * prefix) const;
bool canParse(const char * symbol, size_t length, const Prefix * * prefix) const;
Expression toBaseUnits() const;
bool canPrefix(const Prefix * prefix, bool input) const;
const Prefix * findBestPrefix(double value, double exponent) const;
protected:
static const Representative * DefaultFindBestRepresentative(double value, double exponent, const Representative * representatives, int length, const Prefix * * prefix);
const char * m_rootSymbol;
@@ -187,7 +191,6 @@ public:
int numberOfRepresentatives() const override { return 1; }
const Representative * representativesOfSameDimension() const override;
bool isBaseUnit() const override { return this == representativesOfSameDimension(); }
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return false; }
private:
using Representative::Representative;
};
@@ -195,13 +198,18 @@ public:
class TemperatureRepresentative : public Representative {
friend class Unit;
public:
static double ConvertTemperatures(double value, const Representative * source, const Representative * target);
constexpr static TemperatureRepresentative Default() { return TemperatureRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); }
const Vector<int> dimensionVector() const override { return Vector<int>{.time = 0, .distance = 0, .mass = 0, .current = 0, .temperature = 1, .amountOfSubstance = 0, .luminuousIntensity = 0}; }
int numberOfRepresentatives() const override { return 1; }
int numberOfRepresentatives() const override { return 3; }
const Representative * representativesOfSameDimension() const override;
bool isBaseUnit() const override { return this == representativesOfSameDimension(); }
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return false; }
const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override { return this; }
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return true; }
int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override;
private:
static constexpr double k_celsiusOrigin = 273.15;
static constexpr double k_fahrenheitOrigin = 459.67;
using Representative::Representative;
};
@@ -213,7 +221,6 @@ public:
int numberOfRepresentatives() const override { return 1; }
const Representative * representativesOfSameDimension() const override;
bool isBaseUnit() const override { return this == representativesOfSameDimension(); }
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return false; }
private:
using Representative::Representative;
};
@@ -226,7 +233,6 @@ public:
int numberOfRepresentatives() const override { return 1; }
const Representative * representativesOfSameDimension() const override;
bool isBaseUnit() const override { return this == representativesOfSameDimension(); }
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return false; }
private:
using Representative::Representative;
};
@@ -238,7 +244,6 @@ public:
const Vector<int> dimensionVector() const override { return Vector<int>{.time = -1, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; }
int numberOfRepresentatives() const override { return 1; }
const Representative * representativesOfSameDimension() const override;
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return false; }
private:
using Representative::Representative;
};
@@ -272,6 +277,7 @@ public:
const Vector<int> dimensionVector() const override { return Vector<int>{.time = -2, .distance = 2, .mass = 1, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; }
int numberOfRepresentatives() const override { return 2; }
const Representative * representativesOfSameDimension() const override;
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return true; }
int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override;
private:
using Representative::Representative;
@@ -394,6 +400,7 @@ public:
int numberOfRepresentatives() const override { return 2; }
const Representative * representativesOfSameDimension() const override;
const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override;
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return true; }
int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override;
private:
using Representative::Representative;
@@ -407,6 +414,7 @@ public:
int numberOfRepresentatives() const override { return 8; }
const Representative * representativesOfSameDimension() const override;
const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override;
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return true; }
int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override;
private:
using Representative::Representative;
@@ -418,6 +426,7 @@ public:
constexpr static SpeedRepresentative Default() { return SpeedRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); }
const Vector<int>dimensionVector() const override { return Vector<int>{.time = -1, .distance = 1, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; }
const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override { return nullptr; }
bool hasAdditionalExpressions(double value, Preferences::UnitFormat unitFormat) const override { return true; }
int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override;
private:
using Representative::Representative;
@@ -539,7 +548,11 @@ public:
typedef UnitNode::CurrentRepresentative CurrentRepresentative;
static constexpr const CurrentRepresentative k_currentRepresentatives[] = { CurrentRepresentative("A", 1., Prefixable::All, Prefixable::LongScale) };
typedef UnitNode::TemperatureRepresentative TemperatureRepresentative;
static constexpr const TemperatureRepresentative k_temperatureRepresentatives[] = { TemperatureRepresentative("K", 1., Prefixable::All, Prefixable::LongScale) };
static constexpr const TemperatureRepresentative k_temperatureRepresentatives[] = {
TemperatureRepresentative("K", 1., Prefixable::All, Prefixable::None),
TemperatureRepresentative("Cel", 1., Prefixable::None, Prefixable::None),
TemperatureRepresentative("Fah", 5./9., Prefixable::None, Prefixable::None),
};
typedef UnitNode::AmountOfSubstanceRepresentative AmountOfSubstanceRepresentative;
static constexpr const AmountOfSubstanceRepresentative k_amountOfSubstanceRepresentatives[] = { AmountOfSubstanceRepresentative("mol", 1., Prefixable::All, Prefixable::LongScale) };
typedef UnitNode::LuminousIntensityRepresentative LuminousIntensityRepresentative;
@@ -631,6 +644,12 @@ public:
static_assert(strings_equal(k_massRepresentatives[k_poundRepresentativeIndex].m_rootSymbol, "lb"), "Index for the Pound Representative is incorrect.");
static constexpr int k_shortTonRepresentativeIndex = 5;
static_assert(strings_equal(k_massRepresentatives[k_shortTonRepresentativeIndex].m_rootSymbol, "shtn"), "Index for the Short Ton Representative is incorrect.");
static constexpr int k_kelvinRepresentativeIndex = 0;
static_assert(strings_equal(k_temperatureRepresentatives[k_kelvinRepresentativeIndex].m_rootSymbol, "K"), "Index for the Kelvin Representative is incorrect.");
static constexpr int k_celsiusRepresentativeIndex = 1;
static_assert(strings_equal(k_temperatureRepresentatives[k_celsiusRepresentativeIndex].m_rootSymbol, "Cel"), "Index for the Celsius Representative is incorrect.");
static constexpr int k_fahrenheitRepresentativeIndex = 2;
static_assert(strings_equal(k_temperatureRepresentatives[k_fahrenheitRepresentativeIndex].m_rootSymbol, "Fah"), "Index for the Fahrenheit Representative is incorrect.");
static constexpr int k_electronVoltRepresentativeIndex = 1;
static_assert(strings_equal(k_energyRepresentatives[k_electronVoltRepresentativeIndex].m_rootSymbol, "eV"), "Index for the Electron Volt Representative is incorrect.");
static constexpr int k_wattRepresentativeIndex = 0;
@@ -657,6 +676,7 @@ public:
static bool ShouldDisplayAdditionalOutputs(double value, Expression unit, Preferences::UnitFormat unitFormat);
static int SetAdditionalExpressions(Expression units, double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext);
static Expression BuildSplit(double value, const Unit * units, int length, ExpressionNode::ReductionContext reductionContext);
static Expression ConvertTemperatureUnits(Expression e, Unit unit, ExpressionNode::ReductionContext reductionContext);
// Simplification
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
@@ -665,6 +685,8 @@ public:
bool isBaseUnit() const { return node()->representative()->isBaseUnit() && node()->prefix() == node()->representative()->basePrefix(); }
void chooseBestRepresentativeAndPrefix(double * value, double exponent, ExpressionNode::ReductionContext reductionContext, bool optimizePrefix);
const Representative * representative() const { return node()->representative(); }
private:
UnitNode * node() const { return static_cast<UnitNode *>(Expression::node()); }
Expression removeUnit(Expression * unit);

View File

@@ -5,6 +5,7 @@
#include <poincare/multiplication.h>
#include <poincare/power.h>
#include <poincare/rational.h>
#include <poincare/undefined.h>
#include <algorithm>
#include <assert.h>
#include <limits.h>
@@ -54,6 +55,9 @@ constexpr const int
Unit::k_ounceRepresentativeIndex,
Unit::k_poundRepresentativeIndex,
Unit::k_shortTonRepresentativeIndex,
Unit::k_kelvinRepresentativeIndex,
Unit::k_celsiusRepresentativeIndex,
Unit::k_fahrenheitRepresentativeIndex,
Unit::k_electronVoltRepresentativeIndex,
Unit::k_wattRepresentativeIndex,
Unit::k_hectareRepresentativeIndex,
@@ -476,6 +480,44 @@ int UnitNode::MassRepresentative::setAdditionalExpressions(double value, Express
return 1;
}
double UnitNode::TemperatureRepresentative::ConvertTemperatures(double value, const Representative * source, const Representative * target) {
assert(source->dimensionVector() == TemperatureRepresentative::Default().dimensionVector());
assert(target->dimensionVector() == TemperatureRepresentative::Default().dimensionVector());
if (source == target) {
return value;
}
constexpr double origin[] = {0, k_celsiusOrigin, k_fahrenheitOrigin};
assert(sizeof(origin) == source->numberOfRepresentatives() * sizeof(double));
double sourceOrigin = origin[source - source->representativesOfSameDimension()];
double targetOrigin = origin[target - target->representativesOfSameDimension()];
/* (T + origin) * ration converts T to Kelvin.
* T/ratio - origin converts T from Kelvin. */
return (value + sourceOrigin) * source->ratio() / target->ratio() - targetOrigin;
}
int UnitNode::TemperatureRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const {
assert(availableLength >= 2);
const Representative * celsius = TemperatureRepresentative::Default().representativesOfSameDimension() + Unit::k_celsiusRepresentativeIndex;
const Representative * fahrenheit = TemperatureRepresentative::Default().representativesOfSameDimension() + Unit::k_fahrenheitRepresentativeIndex;
const Representative * kelvin = TemperatureRepresentative::Default().representativesOfSameDimension() + Unit::k_kelvinRepresentativeIndex;
const Representative * targets[] = {
reductionContext.unitFormat() == Preferences::UnitFormat::Metric ? celsius : fahrenheit,
reductionContext.unitFormat() == Preferences::UnitFormat::Metric ? fahrenheit : celsius,
kelvin};
int numberOfExpressionsSet = 0;
int numberOfTargets = sizeof(targets) / sizeof(Representative *);
for (int i = 0; i < numberOfTargets; i++) {
if (targets[i] == this) {
continue;
}
dest[numberOfExpressionsSet++] = Multiplication::Builder(
Float<double>::Builder(TemperatureRepresentative::ConvertTemperatures(value, this, targets[i])),
Unit::Builder(targets[i], Prefix::EmptyPrefix()));
}
assert(numberOfExpressionsSet == 2);
return numberOfExpressionsSet;
}
int UnitNode::EnergyRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const {
assert(availableLength >= 2);
/* 1. Convert into Wh
@@ -717,12 +759,12 @@ bool Unit::ShouldDisplayAdditionalOutputs(double value, Expression unit, Prefere
UnitNode::Vector<int> vector = UnitNode::Vector<int>::FromBaseUnits(unit);
const Representative * representative = Representative::RepresentativeForDimension(vector);
return representative != nullptr
&& ((unit.type() == ExpressionNode::Type::Unit && !unit.convert<Unit>().isBaseUnit())
&& ((unit.type() == ExpressionNode::Type::Unit && !static_cast<Unit &>(unit).isBaseUnit())
|| representative->hasAdditionalExpressions(value, unitFormat));
}
int Unit::SetAdditionalExpressions(Expression units, double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) {
const Representative * representative = UnitNode::Representative::RepresentativeForDimension(UnitNode::Vector<int>::FromBaseUnits(units));
const Representative * representative = units.type() == ExpressionNode::Type::Unit ? static_cast<Unit &>(units).node()->representative() : UnitNode::Representative::RepresentativeForDimension(UnitNode::Vector<int>::FromBaseUnits(units));
assert(representative);
return representative->setAdditionalExpressions(value, dest, availableLength, reductionContext);
}
@@ -764,6 +806,28 @@ Expression Unit::BuildSplit(double value, const Unit * units, int length, Expres
return res.squashUnaryHierarchyInPlace().shallowBeautify(keepUnitsContext);
}
Expression Unit::ConvertTemperatureUnits(Expression e, Unit unit, ExpressionNode::ReductionContext reductionContext) {
const Representative * targetRepr = unit.representative();
const Prefix * targetPrefix = unit.node()->prefix();
assert(unit.representative()->dimensionVector() == TemperatureRepresentative::Default().dimensionVector());
Expression startUnit;
e = e.removeUnit(&startUnit);
if (startUnit.type() != ExpressionNode::Type::Unit) {
return Undefined::Builder();
}
const Representative * startRepr = static_cast<Unit &>(startUnit).representative();
if (startRepr->dimensionVector() != TemperatureRepresentative::Default().dimensionVector()) {
return Undefined::Builder();
}
const Prefix * startPrefix = static_cast<Unit &>(startUnit).node()->prefix();
double value = e.approximateToScalar<double>(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
return Multiplication::Builder(
Float<double>::Builder(TemperatureRepresentative::ConvertTemperatures(value * std::pow(10., startPrefix->exponent()), startRepr, targetRepr) * std::pow(10., - targetPrefix->exponent())),
unit.clone());
}
Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
if (reductionContext.unitConversion() == ExpressionNode::UnitConversion::None
|| isBaseUnit()) {
@@ -772,6 +836,39 @@ Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext
* here but not g */
return *this;
}
/* Handle temperatures : Celsius and Fahrenheit should not be used in
* calculations, only in conversions and results.
* These are the seven legal forms for writing non-kelvin temperatures :
* (1) _°C
* (2) _°C->_?
* (3) 123_°C
* (4) -123_°C
* (5) 123_°C->_K
* (6) -123_°C->_K
* (7) Right member of a unit convert - this is handled above, as
* UnitConversion is set to None in this case. */
if (node()->representative()->dimensionVector() == TemperatureRepresentative::Default().dimensionVector()) {
Expression p = parent();
if (p.isUninitialized() || p.type() == ExpressionNode::Type::UnitConvert) {
// Form (1) and (2)
return *this;
}
if (p.type() == ExpressionNode::Type::Multiplication && p.numberOfChildren() == 2) {
Expression pp = p.parent();
if (pp.isUninitialized() || pp.type() == UnitNode::Type::UnitConvert) {
// Form (3) and (5)
return *this;
}
Expression ppp = pp.parent();
if (pp.type() == UnitNode::Type::Opposite && (ppp.isUninitialized() || ppp.type() == UnitNode::Type::UnitConvert)) {
// Form (4) and (6)
return *this;
}
}
return replaceWithUndefinedInPlace();
}
UnitNode * unitNode = node();
const Representative * representative = unitNode->representative();
const Prefix * prefix = unitNode->prefix();
@@ -789,7 +886,7 @@ Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext
Expression Unit::shallowBeautify(ExpressionNode::ReductionContext reductionContext) {
// Force Float(1) in front of an orphan Unit
if (parent().isUninitialized() || parent().type() == ExpressionNode::Type::Opposite) {
Multiplication m = Multiplication::Builder(Float<double>::Builder(1.0));
Multiplication m = Multiplication::Builder(Float<double>::Builder(1.));
replaceWithInPlace(m);
m.addChildAtIndexInPlace(*this, 1, 1);
return std::move(m);
@@ -806,7 +903,11 @@ Expression Unit::removeUnit(Expression * unit) {
void Unit::chooseBestRepresentativeAndPrefix(double * value, double exponent, ExpressionNode::ReductionContext reductionContext, bool optimizePrefix) {
assert(exponent != 0.f);
if (std::isinf(*value) || *value == 0.0) {
if ((std::isinf(*value) || (*value == 0.0 && node()->representative()->dimensionVector() != TemperatureRepresentative::Default().dimensionVector()))) {
/* Use the base unit to represent an infinite or null value, as all units
* are equivalent.
* This is not true for temperatures (0 K != 0°C != 0°F). */
node()->setRepresentative(node()->representative()->representativesOfSameDimension());
node()->setPrefix(node()->representative()->basePrefix());
return;

View File

@@ -83,6 +83,17 @@ Expression UnitConvert::shallowBeautify(ExpressionNode::ReductionContext reducti
return replaceWithUndefinedInPlace();
}
/* Handle temperatures, as converting between Kelvin, Celsius and Fahrenheit
* cannot be done with a division. */
if (unit.type() == ExpressionNode::Type::Unit) {
Unit unitRef = static_cast<Unit &>(unit);
if (unitRef.representative()->dimensionVector() == Unit::TemperatureRepresentative::Default().dimensionVector()) {
Expression result = Unit::ConvertTemperatureUnits(childAtIndex(0), unitRef, reductionContext);
replaceWithInPlace(result);
return result;
}
}
// Divide the left member by the new unit
Expression division = Division::Builder(childAtIndex(0), unit.clone());
division = division.deepReduce(reductionContext);

View File

@@ -402,6 +402,16 @@ QUIZ_CASE(poincare_expression_additional_results) {
assert_additional_results_compute_to("1×_kg", array7, 1, Imperial);
assert_additional_results_compute_to("1×_kg", nullptr, 0, Metric);
// Temperatures
const char * array14[2] = {"-273.15×_Cel", "-459.67×_Fah"};
assert_additional_results_compute_to("0×_K", array14, 2, Metric);
const char * array15[2] = {"-279.67×_Fah", "-173.15×_Cel"};
assert_additional_results_compute_to("100×_K", array15, 2, Imperial);
const char * array16[2] = {"12.02×_Fah", "262.05×_K"};
assert_additional_results_compute_to("-11.1×_Cel", array16, 2);
const char * array17[2] = {"-20×_Cel", "253.15×_K"};
assert_additional_results_compute_to("-4×_Fah", array17, 2);
// Energy
const char * array8[2] = {"1×_kW×_h", "2.246943ᴇ13×_TeV"};
assert_additional_results_compute_to("3.6×_MN_m", array8, 2);

View File

@@ -309,6 +309,8 @@ QUIZ_CASE(poincare_simplification_units) {
assert_parsed_expression_simplify_to("_shtn", "1×_shtn", User, Radian, Imperial);
assert_parsed_unit_simplify_to_with_prefixes(Unit::k_currentRepresentatives);
assert_parsed_unit_simplify_to_with_prefixes(Unit::k_temperatureRepresentatives);
assert_parsed_expression_simplify_to("_Cel", "1×_Cel");
assert_parsed_expression_simplify_to("_Fah", "1×_Fah");
assert_parsed_unit_simplify_to_with_prefixes(Unit::k_amountOfSubstanceRepresentatives);
assert_parsed_unit_simplify_to_with_prefixes(Unit::k_luminousIntensityRepresentatives);
assert_parsed_unit_simplify_to_with_prefixes(Unit::k_forceRepresentatives);
@@ -344,6 +346,21 @@ QUIZ_CASE(poincare_simplification_units) {
assert_parsed_expression_simplify_to("1_qt", "1×_qt", User, Radian, Imperial);
assert_parsed_expression_simplify_to("1_qt", "946.352946×_cm^3");
/* Tests for non-absolute units */
assert_parsed_expression_simplify_to("273.15×_K→_Cel", "0×_Cel");
assert_parsed_expression_simplify_to("0×_Cel", "0×_Cel");
assert_parsed_expression_simplify_to("-32×_Fah", "-32×_Fah");
assert_parsed_expression_simplify_to("273.16×_K", "273.16×_K");
assert_parsed_expression_simplify_to("100×_Cel→_K", "373.15×_K");
assert_parsed_expression_simplify_to("-100×_Cel→_K", "173.15×_K");
assert_parsed_expression_simplify_to("_Cel+_Cel", Undefined::Name());
assert_parsed_expression_simplify_to("_Cel+_Fah", Undefined::Name());
assert_parsed_expression_simplify_to("_K+_Fah", Undefined::Name());
assert_parsed_expression_simplify_to("2*20_Fah", Undefined::Name());
assert_parsed_expression_simplify_to("_Cel^2", Undefined::Name());
assert_parsed_expression_simplify_to("1/(-3_Cel)", Undefined::Name());
assert_parsed_expression_simplify_to("-1×100×_Cel→_K", Undefined::Name());
/* Unit sum/subtract */
assert_parsed_expression_simplify_to("_m+_m", "2×_m");
assert_parsed_expression_simplify_to("_m-_m", "0×_m");
@@ -1205,6 +1222,13 @@ QUIZ_CASE(poincare_simplification_unit_convert) {
assert_parsed_expression_simplify_to("4→_km/_m", Undefined::Name());
assert_parsed_expression_simplify_to("3×_min→_s+1-1", Undefined::Name());
assert_parsed_expression_simplify_to("0_K→_Cel", "-273.15×_Cel");
assert_parsed_expression_simplify_to("0_Cel→_K", "273.15×_K");
assert_parsed_expression_simplify_to("_Cel→_K", "274.15×_K");
assert_parsed_expression_simplify_to("0_K→_Fah", "-459.67×_Fah");
assert_parsed_expression_simplify_to("0_Fah→_K", "255.37222222222×_K");
assert_parsed_expression_simplify_to("_Fah→_K", "255.92777777778×_K");
assert_reduce("_m→a", Radian, Metric, Real);
assert_reduce("_m→b", Radian, Metric, Real);
assert_parsed_expression_simplify_to("1_km→a×b", Undefined::Name());