Skip to content

Commit 9bafef0

Browse files
pgrawehrangularsen
andauthored
Return decimal for decimal-based quantities (#1074)
Fixes #1058 Return the correct value type for quantities that use `decimal` internally; `Power, Information, BitRate`. - Quantity properties return `decimal` or `double` based on internal value type. - `IQuantity.Value` returns `QuantityValue`, which supports both double and decimal. - `QuantityValue`: Implement IEquality<QuantityValue>, IComparable<QuantityValue>, IComparable. Co-authored-by: Andreas Gullberg Larsen <[email protected]>
1 parent 142a1fb commit 9bafef0

File tree

261 files changed

+2160
-1526
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

261 files changed

+2160
-1526
lines changed

CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,11 @@ private void GenerateProperties()
262262
public {_valueType} Value => _value;
263263
");
264264

265-
// Need to provide explicit interface implementation for decimal quantities like Information
266-
if (_quantity.ValueType != "double")
267-
Writer.WL(@"
268-
double IQuantity.Value => (double) _value;
265+
Writer.WL(@"
266+
/// <inheritdoc />
267+
QuantityValue IQuantity.Value => _value;
269268
");
269+
// Need to provide explicit interface implementation for decimal quantities like Information
270270
if (_quantity.ValueType == "decimal")
271271
Writer.WL(@"
272272
/// <inheritdoc cref=""IDecimalQuantity.Value""/>
@@ -306,11 +306,11 @@ private void GenerateConversionProperties()
306306

307307
Writer.WL($@"
308308
/// <summary>
309-
/// Gets a <see cref=""double""/> value of this quantity converted into <see cref=""{_unitEnumName}.{unit.SingularName}""/>
309+
/// Gets a <see cref=""{_quantity.ValueType}""/> value of this quantity converted into <see cref=""{_unitEnumName}.{unit.SingularName}""/>
310310
/// </summary>");
311311
Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit));
312312
Writer.WL($@"
313-
public double {unit.PluralName} => As({_unitEnumName}.{unit.SingularName});
313+
public {_quantity.ValueType} {unit.PluralName} => As({_unitEnumName}.{unit.SingularName});
314314
");
315315
}
316316

@@ -651,7 +651,7 @@ private void GenerateArithmeticOperators()
651651
}}
652652
653653
/// <summary>Get ratio value from dividing <see cref=""{_quantity.Name}""/> by <see cref=""{_quantity.Name}""/>.</summary>
654-
public static double operator /({_quantity.Name} left, {_quantity.Name} right)
654+
public static {_quantity.ValueType} operator /({_quantity.Name} left, {_quantity.Name} right)
655655
{{
656656
return left.{_baseUnit.PluralName} / right.{_baseUnit.PluralName};
657657
}}
@@ -806,13 +806,13 @@ public int CompareTo({_quantity.Name} other)
806806
/// <param name=""tolerance"">The absolute or relative tolerance value. Must be greater than or equal to 0.</param>
807807
/// <param name=""comparisonType"">The comparison type: either relative or absolute.</param>
808808
/// <returns>True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance.</returns>
809-
public bool Equals({_quantity.Name} other, double tolerance, ComparisonType comparisonType)
809+
public bool Equals({_quantity.Name} other, {_quantity.ValueType} tolerance, ComparisonType comparisonType)
810810
{{
811811
if (tolerance < 0)
812812
throw new ArgumentOutOfRangeException(""tolerance"", ""Tolerance must be greater than or equal to 0."");
813813
814-
double thisValue = (double)this.Value;
815-
double otherValueInThisUnits = other.As(this.Unit);
814+
{_quantity.ValueType} thisValue = this.Value;
815+
{_quantity.ValueType} otherValueInThisUnits = other.As(this.Unit);
816816
817817
return UnitsNet.Comparison.Equals(thisValue, otherValueInThisUnits, tolerance, comparisonType);
818818
}}
@@ -839,17 +839,30 @@ private void GenerateConversionMethods()
839839
/// Convert to the unit representation <paramref name=""unit"" />.
840840
/// </summary>
841841
/// <returns>Value converted to the specified unit.</returns>
842-
public double As({_unitEnumName} unit)
842+
public {_quantity.ValueType} As({_unitEnumName} unit)
843843
{{
844844
if (Unit == unit)
845-
return Convert.ToDouble(Value);
845+
return Value;
846+
847+
return GetValueAs(unit);
848+
}}
849+
");
846850

847-
var converted = GetValueAs(unit);
848-
return Convert.ToDouble(converted);
851+
if (_quantity.ValueType == "decimal")
852+
{
853+
Writer.WL($@"
854+
855+
double IQuantity<{_unitEnumName}>.As({_unitEnumName} unit)
856+
{{
857+
return (double)As(unit);
849858
}}
859+
");
860+
}
861+
862+
Writer.WL($@"
850863
851864
/// <inheritdoc cref=""IQuantity.As(UnitSystem)""/>
852-
public double As(UnitSystem unitSystem)
865+
public {_quantity.ValueType} As(UnitSystem unitSystem)
853866
{{
854867
if (unitSystem is null)
855868
throw new ArgumentNullException(nameof(unitSystem));
@@ -862,14 +875,27 @@ public double As(UnitSystem unitSystem)
862875
863876
return As(firstUnitInfo.Value);
864877
}}
878+
");
879+
880+
if (_quantity.ValueType == "decimal")
881+
{
882+
Writer.WL($@"
883+
/// <inheritdoc cref=""IQuantity.As(UnitSystem)""/>
884+
double IQuantity.As(UnitSystem unitSystem)
885+
{{
886+
return (double)As(unitSystem);
887+
}}
888+
");
889+
}
865890

891+
Writer.WL($@"
866892
/// <inheritdoc />
867893
double IQuantity.As(Enum unit)
868894
{{
869-
if (!(unit is {_unitEnumName} unitAs{_unitEnumName}))
895+
if (!(unit is {_unitEnumName} typedUnit))
870896
throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit));
871897
872-
return As(unitAs{_unitEnumName});
898+
return (double)As(typedUnit);
873899
}}
874900
875901
/// <summary>
@@ -916,10 +942,10 @@ double IQuantity.As(Enum unit)
916942
/// <inheritdoc />
917943
IQuantity IQuantity.ToUnit(Enum unit)
918944
{{
919-
if (!(unit is {_unitEnumName} unitAs{_unitEnumName}))
945+
if (!(unit is {_unitEnumName} typedUnit))
920946
throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit));
921947
922-
return ToUnit(unitAs{_unitEnumName}, DefaultConversionFunctions);
948+
return ToUnit(typedUnit, DefaultConversionFunctions);
923949
}}
924950
925951
/// <inheritdoc cref=""IQuantity.ToUnit(UnitSystem)""/>

CodeGen/Generators/UnitsNetGen/UnitTestBaseClassGenerator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public abstract partial class {_quantity.Name}TestsBase : QuantityTestsBase
102102
continue;
103103

104104
Writer.WL($@"
105-
protected abstract double {unit.PluralName}InOne{_baseUnit.SingularName} {{ get; }}");
105+
protected abstract {_quantity.ValueType} {unit.PluralName}InOne{_baseUnit.SingularName} {{ get; }}");
106106
}
107107

108108
Writer.WL("");
@@ -114,12 +114,12 @@ public abstract partial class {_quantity.Name}TestsBase : QuantityTestsBase
114114
continue;
115115

116116
Writer.WL($@"
117-
protected virtual double {unit.PluralName}Tolerance {{ get {{ return 1e-5; }} }}");
117+
protected virtual {_quantity.ValueType} {unit.PluralName}Tolerance {{ get {{ return { (_quantity.ValueType == "decimal" ? "1e-9m" : "1e-5") }; }} }}");
118118
}
119119
Writer.WL($@"
120120
// ReSharper restore VirtualMemberNeverOverriden.Global
121121
122-
protected (double UnitsInBaseUnit, double Tolerence) GetConversionFactor({_unitEnumName} unit)
122+
protected ({_quantity.ValueType} UnitsInBaseUnit, {_quantity.ValueType} Tolerence) GetConversionFactor({_unitEnumName} unit)
123123
{{
124124
return unit switch
125125
{{");
@@ -355,7 +355,7 @@ public void ToUnit({_unitEnumName} unit)
355355
var converted = inBaseUnits.ToUnit(unit);
356356
357357
var conversionFactor = GetConversionFactor(unit);
358-
AssertEx.EqualTolerance(conversionFactor.UnitsInBaseUnit, (double)converted.Value, conversionFactor.Tolerence);
358+
AssertEx.EqualTolerance(conversionFactor.UnitsInBaseUnit, converted.Value, conversionFactor.Tolerence);
359359
Assert.Equal(unit, converted.Unit);
360360
}}
361361

Common/UnitDefinitions/Power.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
{
8686
"SingularName": "BritishThermalUnitPerHour",
8787
"PluralName": "BritishThermalUnitsPerHour",
88-
"FromUnitToBaseFunc": "{x} * 0.293071m",
89-
"FromBaseToUnitFunc": "{x} / 0.293071m",
88+
"FromUnitToBaseFunc": "{x} * 0.29307107017m",
89+
"FromBaseToUnitFunc": "{x} / 0.29307107017m",
9090
"Prefixes": [ "Kilo", "Mega" ],
9191
"Localization": [
9292
{

UnitsNet.NanoFramework/GeneratedCode/Quantities/Power.g.cs

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void UnitsNetBaseJsonConverter_ConvertValueUnit_works_as_expected()
5454

5555
Assert.NotNull(result);
5656
Assert.IsType<Power>(result);
57-
Assert.True(Power.FromWatts(10.2365m).Equals((Power)result, 1E-5, ComparisonType.Absolute));
57+
Assert.True(Power.FromWatts(10.2365m).Equals((Power)result, 1E-5m, ComparisonType.Absolute));
5858

5959
}
6060

UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public void UnitsNetIComparableJsonConverter_ReadJson_works_as_expected()
117117

118118
Assert.NotNull(result);
119119
Assert.IsType<Power>(result);
120-
Assert.Equal(120D, ((Power)result).Watts);
120+
Assert.Equal(120M, ((Power)result).Watts);
121121
}
122122
}
123123
}

UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public void UnitsNetIQuantityJsonConverter_ReadJson_works_as_expected()
135135

136136
Assert.NotNull(result);
137137
Assert.IsType<Power>(result);
138-
Assert.Equal(10.3654D, ((Power)result).Watts);
138+
Assert.Equal(10.3654M, ((Power)result).Watts);
139139
}
140140
}
141141
}

UnitsNet.Serialization.JsonNet/AbbreviatedUnitsConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public override void WriteJson(JsonWriter writer, IQuantity? quantity, JsonSeria
8585
}
8686
else
8787
{
88-
writer.WriteValue(quantity.Value);
88+
writer.WriteValue((double)quantity.Value);
8989
}
9090

9191
// write the 'Unit' abbreviation

UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,14 @@ protected ValueUnit ConvertIQuantity(IQuantity quantity)
186186
return new ExtendedValueUnit
187187
{
188188
Unit = $"{quantity.QuantityInfo.UnitType.Name}.{quantity.Unit}",
189-
Value = quantity.Value,
189+
// The type of "Value" is still double
190+
Value = (double)quantity.Value,
190191
ValueString = d.Value.ToString(CultureInfo.InvariantCulture),
191192
ValueType = "decimal"
192193
};
193194
}
194195

195-
return new ValueUnit {Value = quantity.Value, Unit = $"{quantity.QuantityInfo.UnitType.Name}.{quantity.Unit}"};
196+
return new ValueUnit {Value = (double)quantity.Value, Unit = $"{quantity.QuantityInfo.UnitType.Name}.{quantity.Unit}"};
196197
}
197198

198199
/// <summary>

UnitsNet.Tests/AssertEx.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,23 @@ public static void EqualTolerance(double expected, double actual, double toleran
2525
Assert.True( areEqual, $"Values are not equal within absolute tolerance: {tolerance}\nExpected: {expected}\nActual: {actual}\nDiff: {actual - expected:e}" );
2626
}
2727
}
28+
29+
public static void EqualTolerance(decimal expected, decimal actual, decimal tolerance, ComparisonType comparisonType = ComparisonType.Relative)
30+
{
31+
if (comparisonType == ComparisonType.Relative)
32+
{
33+
bool areEqual = Comparison.EqualsRelative(expected, actual, tolerance);
34+
35+
decimal difference = Math.Abs(expected - actual);
36+
decimal relativeDifference = difference / expected;
37+
38+
Assert.True(areEqual, $"Values are not equal within relative tolerance: {tolerance:P4}\nExpected: {expected}\nActual: {actual}\nDiff: {relativeDifference:P4}");
39+
}
40+
else if (comparisonType == ComparisonType.Absolute)
41+
{
42+
bool areEqual = Comparison.EqualsAbsolute(expected, actual, tolerance);
43+
Assert.True(areEqual, $"Values are not equal within absolute tolerance: {tolerance}\nExpected: {expected}\nActual: {actual}\nDiff: {actual - expected:e}");
44+
}
45+
}
2846
}
2947
}

UnitsNet.Tests/CustomCode/BitRateTests.cs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,37 +29,37 @@ public class BitRateTests : BitRateTestsBase
2929
{
3030
protected override bool SupportsSIUnitSystem => false;
3131

32-
protected override double BitsPerSecondInOneBitPerSecond => 1d;
33-
protected override double BytesPerSecondInOneBitPerSecond => 1.25E-1d;
32+
protected override decimal BitsPerSecondInOneBitPerSecond => 1m;
33+
protected override decimal BytesPerSecondInOneBitPerSecond => 1.25E-1m;
3434

35-
protected override double KilobitsPerSecondInOneBitPerSecond => 1E-3d;
36-
protected override double KilobytesPerSecondInOneBitPerSecond => 1.25E-4d;
37-
protected override double KibibitsPerSecondInOneBitPerSecond => 0.0009765625d;
38-
protected override double KibibytesPerSecondInOneBitPerSecond => 0.0001220703125d;
35+
protected override decimal KilobitsPerSecondInOneBitPerSecond => 1E-3m;
36+
protected override decimal KilobytesPerSecondInOneBitPerSecond => 1.25E-4m;
37+
protected override decimal KibibitsPerSecondInOneBitPerSecond => 0.0009765625m;
38+
protected override decimal KibibytesPerSecondInOneBitPerSecond => 0.0001220703125m;
3939

40-
protected override double MegabitsPerSecondInOneBitPerSecond => 1E-6d;
41-
protected override double MegabytesPerSecondInOneBitPerSecond => 1.25E-07d;
42-
protected override double MebibitsPerSecondInOneBitPerSecond => 9.5367431640625E-07d;
43-
protected override double MebibytesPerSecondInOneBitPerSecond => 1.19209289550781E-07d;
40+
protected override decimal MegabitsPerSecondInOneBitPerSecond => 1E-6m;
41+
protected override decimal MegabytesPerSecondInOneBitPerSecond => 1.25E-07m;
42+
protected override decimal MebibitsPerSecondInOneBitPerSecond => 9.5367431640625E-07m;
43+
protected override decimal MebibytesPerSecondInOneBitPerSecond => 1.19209289550781E-07m;
4444

45-
protected override double GigabitsPerSecondInOneBitPerSecond => 1E-9d;
46-
protected override double GigabytesPerSecondInOneBitPerSecond => 1.25E-10d;
47-
protected override double GibibitsPerSecondInOneBitPerSecond => 9.31322574615479E-10d;
48-
protected override double GibibytesPerSecondInOneBitPerSecond => 1.16415321826935E-10d;
45+
protected override decimal GigabitsPerSecondInOneBitPerSecond => 1E-9m;
46+
protected override decimal GigabytesPerSecondInOneBitPerSecond => 1.25E-10m;
47+
protected override decimal GibibitsPerSecondInOneBitPerSecond => 9.31322574615479E-10m;
48+
protected override decimal GibibytesPerSecondInOneBitPerSecond => 1.16415321826935E-10m;
4949

50-
protected override double TerabitsPerSecondInOneBitPerSecond => 1E-12d;
51-
protected override double TerabytesPerSecondInOneBitPerSecond => 1.25E-13d;
52-
protected override double TebibitsPerSecondInOneBitPerSecond => 9.09494701772928E-13d;
53-
protected override double TebibytesPerSecondInOneBitPerSecond => 1.13686837721616E-13d;
50+
protected override decimal TerabitsPerSecondInOneBitPerSecond => 1E-12m;
51+
protected override decimal TerabytesPerSecondInOneBitPerSecond => 1.25E-13m;
52+
protected override decimal TebibitsPerSecondInOneBitPerSecond => 9.09494701772928E-13m;
53+
protected override decimal TebibytesPerSecondInOneBitPerSecond => 1.13686837721616E-13m;
5454

55-
protected override double PetabitsPerSecondInOneBitPerSecond => 1E-15d;
56-
protected override double PetabytesPerSecondInOneBitPerSecond => 1.25E-16d;
57-
protected override double PebibitsPerSecondInOneBitPerSecond => 8.88178419700125E-16d;
58-
protected override double PebibytesPerSecondInOneBitPerSecond => 1.11022302462516E-16d;
55+
protected override decimal PetabitsPerSecondInOneBitPerSecond => 1E-15m;
56+
protected override decimal PetabytesPerSecondInOneBitPerSecond => 1.25E-16m;
57+
protected override decimal PebibitsPerSecondInOneBitPerSecond => 8.88178419700125E-16m;
58+
protected override decimal PebibytesPerSecondInOneBitPerSecond => 1.11022302462516E-16m;
5959

60-
protected override double ExabitsPerSecondInOneBitPerSecond => 1E-18d;
61-
protected override double ExabytesPerSecondInOneBitPerSecond => 1.25E-19d;
62-
protected override double ExbibitsPerSecondInOneBitPerSecond => 8.67361738E-19d;
63-
protected override double ExbibytesPerSecondInOneBitPerSecond => 1.0842021724855E-19d;
60+
protected override decimal ExabitsPerSecondInOneBitPerSecond => 1E-18m;
61+
protected override decimal ExabytesPerSecondInOneBitPerSecond => 1.25E-19m;
62+
protected override decimal ExbibitsPerSecondInOneBitPerSecond => 8.67361738E-19m;
63+
protected override decimal ExbibytesPerSecondInOneBitPerSecond => 1.0842021724855E-19m;
6464
}
6565
}

0 commit comments

Comments
 (0)