-
Notifications
You must be signed in to change notification settings - Fork 396
Experimental: Generic Math and INumber
Generic math was introduced in .NET 7.
https://learn.microsoft.com/en-us/dotnet/standard/generics/math
We wanted to see what works and what doesn't for Units.NET, so we added some experimental support for the generic math interfaces in
Generic math for UnitsNet in .NET 7 · Pull Request #1164.
Sum and Average, for now.
[Fact]
public void CanCalcSum()
{
Length[] values = { Length.FromCentimeters(100), Length.FromCentimeters(200) };
Assert.Equal(Length.FromCentimeters(300), values.Sum());
}
[Fact]
public void CanCalcAverage_ForQuantitiesWithDoubleValueType()
{
Length[] values = { Length.FromCentimeters(100), Length.FromCentimeters(200) };
Assert.Equal(Length.FromCentimeters(150), values.Average());
}
It seems there are no implementations shipped with .NET yet, so we provide these two extension methods as a proof of concept. We can add more if there is a need for it.
public static T Sum<T>(this IEnumerable<T> source)
where T : IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
{
// Put accumulator on right hand side of the addition operator to construct quantities with the same unit as the values.
// The addition operator implementation picks the unit from the left hand side, and the additive identity (e.g. Length.Zero) is always the base unit.
return source.Aggregate(T.AdditiveIdentity, (acc, item) => item + acc);
}
public static T Average<T>(this IEnumerable<T> source)
where T : IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>, IDivisionOperators<T, double, T>
{
// Put accumulator on right hand side of the addition operator to construct quantities with the same unit as the values.
// The addition operator implementation picks the unit from the left hand side, and the additive identity (e.g. Length.Zero) is always the base unit.
(T value, int count) result = source.Aggregate(
(value: T.AdditiveIdentity, count: 0),
(acc, item) => (value: item + acc.value, count: acc.count + 1));
return result.value / result.count;
}
INumber.Min/Max not well defined
UnitsNet does not provide Min/Max values for quantities, since you quickly run into overflow exceptions when converting to other units. Also the Min/Max could change when introducing bigger/smaller units.
Temperature has its own quirks with arithmetic in general.
0 Celsius != 0 Fahrenheit != 0 Kelvin.
So for example 20 °C + 5 °C is ambiguous. It could mean 25 °C, or it could mean 293.15 K + 278.15 K.
This made it hard to implement IAdditiveIdentity in a way that is intuitive, which is essential to arithmetic like Average()
.