mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[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:
committed by
Émilie Feral
parent
4274b558b6
commit
d37540f032
@@ -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),
|
||||
|
||||
@@ -35,6 +35,8 @@ UnitCurrentAmpereSymbol = "_A"
|
||||
UnitCurrentAmpereMilliSymbol = "_mA"
|
||||
UnitCurrentAmpereMicroSymbol = "_μA"
|
||||
UnitTemperatureKelvinSymbol = "_K"
|
||||
UnitTemperatureCelsiusSymbol = "_Cel"
|
||||
UnitTemperatureFahrenheitSymbol = "_Fah"
|
||||
UnitAmountMoleSymbol = "_mol"
|
||||
UnitAmountMoleMilliSymbol = "_mmol"
|
||||
UnitAmountMoleMicroSymbol = "_μmol"
|
||||
|
||||
@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
|
||||
UnitCurrentAmpereMicro = "Mikroampere"
|
||||
UnitTemperatureMenu = "Temperatur"
|
||||
UnitTemperatureKelvin = "Kelvin"
|
||||
UnitTemperatureCelsius = "Celsius"
|
||||
UnitTemperatureFahrenheit = "Fahrenheit"
|
||||
UnitAmountMenu = "Stoffmenge"
|
||||
UnitAmountMole = "Mol"
|
||||
UnitAmountMoleMilli = "Millimol"
|
||||
|
||||
@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
|
||||
UnitCurrentAmpereMicro = "Microampere"
|
||||
UnitTemperatureMenu = "Temperature"
|
||||
UnitTemperatureKelvin = "Kelvin"
|
||||
UnitTemperatureCelsius = "Celsius"
|
||||
UnitTemperatureFahrenheit = "Fahrenheit"
|
||||
UnitAmountMenu = "Amount of substance"
|
||||
UnitAmountMole = "Mole"
|
||||
UnitAmountMoleMilli = "Millimole"
|
||||
|
||||
@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
|
||||
UnitCurrentAmpereMicro = "Microampere"
|
||||
UnitTemperatureMenu = "Temperature"
|
||||
UnitTemperatureKelvin = "Kelvin"
|
||||
UnitTemperatureCelsius = "Celsius"
|
||||
UnitTemperatureFahrenheit = "Fahrenheit"
|
||||
UnitAmountMenu = "Amount of substance"
|
||||
UnitAmountMole = "Mole"
|
||||
UnitAmountMoleMilli = "Millimole"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -43,6 +43,8 @@ UnitCurrentAmpereMilli = "Milliampere"
|
||||
UnitCurrentAmpereMicro = "Microampere"
|
||||
UnitTemperatureMenu = "Temperatura"
|
||||
UnitTemperatureKelvin = "Kelvin"
|
||||
UnitTemperatureCelsius = "Celsius"
|
||||
UnitTemperatureFahrenheit = "Fahrenheit"
|
||||
UnitAmountMenu = "Quantità de materia"
|
||||
UnitAmountMole = "Mole"
|
||||
UnitAmountMoleMilli = "Millimole"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user