diff --git a/source/Quantities.Benchmark/Compare/Creation.cs b/source/Quantities.Benchmark/Compare/Creation.cs new file mode 100644 index 00000000..4c7dd293 --- /dev/null +++ b/source/Quantities.Benchmark/Compare/Creation.cs @@ -0,0 +1,82 @@ +using BenchmarkDotNet.Configs; +using Quantities.Prefixes; +using Quantities.Units.Si; +using Quantities.Units.Si.Metric; + +using static Towel.Measurements.MeasurementsSyntax; + +namespace Quantities.Benchmark.Compare; + +[MemoryDiagnoser] +[CategoriesColumn] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public class Creation +{ + private const String Scalar = nameof(Scalar); + private const String Cubed = nameof(Cubed); + private const String Aliasing = nameof(Aliasing); + private static readonly Double value = Math.E; + + [BenchmarkCategory(Scalar), Benchmark(Baseline = true)] + public Length CreateScalarQuantity() => Length.Of(value, Si()); + + [BenchmarkCategory(Scalar), Benchmark] + public UnitsNet.Length CreateScalarUnitsNet() => UnitsNet.Length.FromCentimeters(value); + + [BenchmarkCategory(Scalar), Benchmark] + public QuantityTypes.Length CreateScalarQuantityTypes() => value * QuantityTypes.Length.Centimetre; + + [BenchmarkCategory(Scalar), Benchmark] + public Towel.Measurements.Length CreateScalarTowelMeasurements() => (value, Centimeters); + + [BenchmarkCategory(Cubed), Benchmark(Baseline = true)] + public Volume CreateCubedQuantity() => Volume.Of(value, Cubic(Si())); + + [BenchmarkCategory(Cubed), Benchmark] + public UnitsNet.Volume CreateCubedUnitsNet() => UnitsNet.Volume.FromCubicCentimeters(value); + + [BenchmarkCategory(Cubed), Benchmark] + public QuantityTypes.Volume CreateCubedQuantityTypes() => value * QuantityTypes.Volume.CubicCentimetre; + + [BenchmarkCategory(Cubed), Benchmark] + public Towel.Measurements.Volume CreateCubedTowelMeasurement() => (value, Centimeters * Centimeters * Centimeters); + + [BenchmarkCategory(Aliasing), Benchmark(Baseline = true)] + public Volume CreateAliasedQuantity() => Volume.Of(value, Metric()); + + [BenchmarkCategory(Aliasing), Benchmark] + public UnitsNet.Volume CreateAliasedUnitsNet() => UnitsNet.Volume.FromCentiliters(value); + + [BenchmarkCategory(Aliasing), Benchmark] + public QuantityTypes.Volume CreateAliasedQuantityTypes() => value * QuantityTypes.Volume.Litre / 100; // cannot create centilitres... + + [BenchmarkCategory(Aliasing), Benchmark] + public Towel.Measurements.Volume CreateAliasedTowelMeasurements() => (1000 * value, Millimeters * Millimeters * Millimeters); // cannot create litres... +} + +/* Summary * + +BenchmarkDotNet v0.13.12, Arch Linux +Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores +.NET SDK 8.0.103 + [Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + + +| Method | Categories | Mean | Error | Ratio | Allocated | Alloc Ratio | +|------------------------------- |----------- |-----------:|----------:|-------:|----------:|------------:| +| CreateAliasedQuantity | Aliasing | 13.0551 ns | 0.1992 ns | 1.000 | - | NA | +| CreateAliasedUnitsNet | Aliasing | 11.8207 ns | 0.0689 ns | 0.906 | - | NA | +| CreateAliasedQuantityTypes | Aliasing | 0.0018 ns | 0.0059 ns | 0.000 | - | NA | +| CreateAliasedTowelMeasurements | Aliasing | 0.0728 ns | 0.0298 ns | 0.006 | - | NA | +| | | | | | | | +| CreateCubedQuantity | Cubed | 0.7939 ns | 0.0129 ns | 1.000 | - | NA | +| CreateCubedUnitsNet | Cubed | 11.8563 ns | 0.0491 ns | 14.906 | - | NA | +| CreateCubedQuantityTypes | Cubed | 0.0000 ns | 0.0000 ns | 0.000 | - | NA | +| CreateCubedTowelMeasurement | Cubed | 0.0975 ns | 0.0216 ns | 0.123 | - | NA | +| | | | | | | | +| CreateScalarQuantity | Scalar | 0.7578 ns | 0.0110 ns | 1.000 | - | NA | +| CreateScalarUnitsNet | Scalar | 11.7537 ns | 0.0513 ns | 15.513 | - | NA | +| CreateScalarQuantityTypes | Scalar | 0.0038 ns | 0.0061 ns | 0.005 | - | NA | +| CreateScalarTowelMeasurements | Scalar | 0.0634 ns | 0.0092 ns | 0.085 | - | NA | +*/ diff --git a/source/Quantities.Benchmark/Compare/Division.cs b/source/Quantities.Benchmark/Compare/Division.cs new file mode 100644 index 00000000..ff9aedf2 --- /dev/null +++ b/source/Quantities.Benchmark/Compare/Division.cs @@ -0,0 +1,56 @@ +using Quantities.Prefixes; +using Quantities.Units.Imperial.Length; +using Quantities.Units.Si.Metric; + +using nLength = UnitsNet.Length; +using nVolume = UnitsNet.Volume; +using qtArea = QuantityTypes.Area; +using qtVolume = QuantityTypes.Volume; +using tLength = Towel.Measurements.Length; +using tVolume = Towel.Measurements.Volume; + +using static Towel.Measurements.MeasurementsSyntax; + +namespace Quantities.Benchmark.Compare; + +[MemoryDiagnoser] +public class Division +{ + private static readonly Volume left = Volume.Of(32, Metric()); + private static readonly Length right = Length.Of(4, Imperial()); + private static readonly nVolume nLeft = nVolume.FromCentiliters(32); + private static readonly nLength nRight = nLength.FromFeet(4); + private static readonly qtVolume qtLeft = 32 * qtVolume.Litre / 100; + private static readonly qtArea qtRight = 4 * qtArea.Acre; + private static readonly tVolume tLeft = (32 * 10, Centimeters * Centimeters * Centimeters); + private static readonly tLength tRight = (4, Feet); + + [Benchmark(Baseline = true)] + public Area Quantity() => left / right; + + [Benchmark] + public UnitsNet.Area UnitsNet() => nLeft / nRight; + + [Benchmark] + public QuantityTypes.Length QuantityTypes() => qtLeft / qtRight; // cannot divide volume by area :-/ + + [Benchmark] + public Towel.Measurements.Area TowelMeasurements() => tLeft / tRight; +} + +/* Summary * + +BenchmarkDotNet v0.13.12, Arch Linux +Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores +.NET SDK 8.0.103 + [Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + + +| Method | Mean | Error | Ratio | Allocated | Alloc Ratio | +|------------------ |----------:|----------:|------:|----------:|------------:| +| Quantity | 8.259 ns | 0.0330 ns | 1.00 | - | NA | +| UnitsNet | 47.356 ns | 0.6768 ns | 5.75 | - | NA | +| QuantityTypes | 1.734 ns | 0.0055 ns | 0.21 | - | NA | +| TowelMeasurements | 8.630 ns | 0.0247 ns | 1.04 | - | NA | +*/ diff --git a/source/Quantities.Benchmark/Compare/Multiplication.cs b/source/Quantities.Benchmark/Compare/Multiplication.cs new file mode 100644 index 00000000..1cd1bb70 --- /dev/null +++ b/source/Quantities.Benchmark/Compare/Multiplication.cs @@ -0,0 +1,54 @@ +using Quantities.Prefixes; +using Quantities.Units.Imperial.Length; +using Quantities.Units.Si; + +using nLength = UnitsNet.Length; +using qtLength = QuantityTypes.Length; +using tLength = Towel.Measurements.Length; + +using static Towel.Measurements.MeasurementsSyntax; + +namespace Quantities.Benchmark.Compare; + +[MemoryDiagnoser(displayGenColumns: false)] +public class Multiplication +{ + private static readonly Length left = Length.Of(3, Si()); + private static readonly Length right = Length.Of(4, Imperial()); + private static readonly nLength nLeft = nLength.FromMillimeters(3); + private static readonly nLength nRight = nLength.FromMillimeters(4); + private static readonly qtLength qtLeft = 3 * qtLength.Millimetre; + private static readonly qtLength qtRight = 4 * qtLength.Foot; + private static readonly tLength tLeft = (3d, Millimeters); + private static readonly tLength tRight = (4d, Feet); + + + [Benchmark(Baseline = true)] + public Area Quantity() => left * right; + + [Benchmark] + public UnitsNet.Area UnitsNet() => nLeft * nRight; + + [Benchmark] + public QuantityTypes.Area QuantityTypes() => qtLeft * qtRight; + + [Benchmark] + public Towel.Measurements.Area TowelMeasurements() => tLeft * tRight; +} + +/* Summary * + +BenchmarkDotNet v0.13.12, Arch Linux +Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores +.NET SDK 8.0.103 + [Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + + +| Method | Mean | Error | Ratio | Allocated | Alloc Ratio | +|------------------ |-----------:|----------:|------:|----------:|------------:| +| Quantity | 6.8138 ns | 0.0566 ns | 1.000 | - | NA | +| UnitsNet | 42.8554 ns | 0.3723 ns | 6.290 | - | NA | +| QuantityTypes | 0.0006 ns | 0.0024 ns | 0.000 | - | NA | +| TowelMeasurements | 6.2112 ns | 0.0824 ns | 0.912 | - | NA | +*/ diff --git a/source/Quantities.Benchmark/Compare/QuantityConversionComparison.cs b/source/Quantities.Benchmark/Compare/QuantityConversionComparison.cs new file mode 100644 index 00000000..d91de2f7 --- /dev/null +++ b/source/Quantities.Benchmark/Compare/QuantityConversionComparison.cs @@ -0,0 +1,84 @@ +using BenchmarkDotNet.Configs; +using Quantities.Prefixes; +using Quantities.Units.Imperial.Length; +using Quantities.Units.Si; +using UnitsNet.Units; + +using nLength = UnitsNet.Length; +using qtLength = QuantityTypes.Length; +using tLength = Towel.Measurements.Length; + +using static Towel.Measurements.MeasurementsSyntax; + +namespace Quantities.Benchmark.Compare; + +[MemoryDiagnoser(displayGenColumns: false)] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public class QuantityConversionComparison +{ + private const String ToSi = nameof(ToSi); + private const String ToImperial = nameof(ToImperial); + private const String ToSame = nameof(ToSame); + private static readonly Length si = Length.Of(3, Si()); + private static readonly Length imperial = Length.Of(4, Imperial()); + private static readonly nLength nSi = nLength.FromMillimeters(3); + private static readonly nLength nImperial = nLength.FromFeet(4); + private static readonly qtLength qtSi = 3 * qtLength.Millimetre; + private static readonly qtLength qtImperial = 4 * qtLength.Foot; + private static readonly tLength tSi = (3d, Millimeters); + private static readonly tLength tImperial = (4d, Feet); + + [BenchmarkCategory(ToSi), Benchmark(Baseline = true)] + public Length QuantityToSi() => imperial.To(Si()); + [BenchmarkCategory(ToSi), Benchmark] + public nLength UnitsNetToSi() => nImperial.ToUnit(LengthUnit.Millimeter); + [BenchmarkCategory(ToSi), Benchmark] + public qtLength QuantityTypesToSi() => qtImperial.ConvertTo(qtLength.Millimetre) * qtLength.Millimetre; + [BenchmarkCategory(ToSi), Benchmark] + public tLength TowelMeasurementsToSi() => (tImperial[Millimeters], Millimeters); + + [BenchmarkCategory(ToImperial), Benchmark(Baseline = true)] + public Length QuantityToImperial() => si.To(Imperial()); + [BenchmarkCategory(ToImperial), Benchmark] + public nLength UnitsNetToImperial() => nSi.ToUnit(LengthUnit.Foot); + [BenchmarkCategory(ToImperial), Benchmark] + public qtLength QuantityTypesToImperial() => qtSi.ConvertTo(qtLength.Foot) * qtLength.Foot; + [BenchmarkCategory(ToImperial), Benchmark] + public tLength TowelMeasurementsToImperial() => (tSi[Feet], Feet); + + [BenchmarkCategory(ToSame), Benchmark(Baseline = true)] + public Length QuantityToSame() => imperial.To(Imperial()); + [BenchmarkCategory(ToSame), Benchmark] + public nLength UnitsNetToSame() => nImperial.ToUnit(LengthUnit.Foot); + [BenchmarkCategory(ToSame), Benchmark] + public qtLength QuantityTypesToSame() => qtImperial.ConvertTo(qtLength.Foot) * qtLength.Foot; + [BenchmarkCategory(ToSame), Benchmark] + public tLength TowelMeasurementsToSame() => (tImperial[Feet], Feet); +} + +/* Summary * + +BenchmarkDotNet v0.13.12, Arch Linux +Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores +.NET SDK 8.0.103 + [Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + + +| Method | Mean | Error | Ratio | Allocated | Alloc Ratio | +|---------------------------- |-----------:|----------:|-------:|----------:|------------:| +| QuantityToImperial | 2.7528 ns | 0.0701 ns | 1.000 | - | NA | +| UnitsNetToImperial | 76.2580 ns | 0.5021 ns | 27.712 | 48 B | NA | +| QuantityTypesToImperial | 0.0049 ns | 0.0084 ns | 0.002 | - | NA | +| TowelMeasurementsToImperial | 3.9093 ns | 0.0157 ns | 1.421 | - | NA | +| | | | | | | +| QuantityToSame | 2.5044 ns | 0.0480 ns | 1.000 | - | NA | +| UnitsNetToSame | 16.1036 ns | 0.0959 ns | 6.432 | - | NA | +| QuantityTypesToSame | 0.0028 ns | 0.0079 ns | 0.001 | - | NA | +| TowelMeasurementsToSame | 0.0340 ns | 0.0060 ns | 0.013 | - | NA | +| | | | | | | +| QuantityToSi | 3.6184 ns | 0.0270 ns | 1.000 | - | NA | +| UnitsNetToSi | 75.7462 ns | 0.2194 ns | 20.934 | 48 B | NA | +| QuantityTypesToSi | 0.0038 ns | 0.0056 ns | 0.001 | - | NA | +| TowelMeasurementsToSi | 3.4991 ns | 0.0125 ns | 0.967 | - | NA | +*/ diff --git a/source/Quantities.Benchmark/Compare/ValueConversionComparison.cs b/source/Quantities.Benchmark/Compare/ValueConversionComparison.cs new file mode 100644 index 00000000..c9b58151 --- /dev/null +++ b/source/Quantities.Benchmark/Compare/ValueConversionComparison.cs @@ -0,0 +1,83 @@ +using BenchmarkDotNet.Configs; +using Quantities.Prefixes; +using Quantities.Units.Imperial.Length; +using Quantities.Units.Si; + +using nLength = UnitsNet.Length; +using qtLength = QuantityTypes.Length; +using tLength = Towel.Measurements.Length; + +using static Towel.Measurements.MeasurementsSyntax; + +namespace Quantities.Benchmark.Compare; + +[MemoryDiagnoser(displayGenColumns: false)] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public class ValueConversionComparison +{ + private const String ToSi = nameof(ToSi); + private const String ToImperial = nameof(ToImperial); + private const String ToSame = nameof(ToSame); + private static readonly Length si = Length.Of(3, Si()); + private static readonly Length imperial = Length.Of(4, Imperial()); + private static readonly nLength nSi = nLength.FromMillimeters(3); + private static readonly nLength nImperial = nLength.FromFeet(4); + private static readonly qtLength qtSi = 3 * qtLength.Millimetre; + private static readonly qtLength qtImperial = 4 * qtLength.Foot; + private static readonly tLength tSi = (3d, Millimeters); + private static readonly tLength tImperial = (4d, Feet); + + [BenchmarkCategory(ToSi), Benchmark(Baseline = true)] + public Double QuantityToSi() => imperial.To(Si()); + [BenchmarkCategory(ToSi), Benchmark] + public Double UnitsNetToSi() => nImperial.Millimeters; + [BenchmarkCategory(ToSi), Benchmark] + public Double QuantityTypesToSi() => qtImperial.ConvertTo(qtLength.Millimetre); + [BenchmarkCategory(ToSi), Benchmark] + public Double TowelMeasurementsToSi() => tImperial[Millimeters]; + + [BenchmarkCategory(ToImperial), Benchmark(Baseline = true)] + public Double QuantityToImperial() => si.To(Imperial()); + [BenchmarkCategory(ToImperial), Benchmark] + public Double UnitsNetToImperial() => nSi.Feet; + [BenchmarkCategory(ToImperial), Benchmark] + public Double QuantityTypesToImperial() => qtSi.ConvertTo(qtLength.Foot); + [BenchmarkCategory(ToImperial), Benchmark] + public Double TowelMeasurementsToImperial() => tSi[Feet]; + + [BenchmarkCategory(ToSame), Benchmark(Baseline = true)] + public Double QuantityToSame() => imperial.To(Imperial()); + [BenchmarkCategory(ToSame), Benchmark] + public Double UnitsNetToSame() => nImperial.Feet; + [BenchmarkCategory(ToSame), Benchmark] + public Double QuantityTypesToSame() => qtImperial.ConvertTo(qtLength.Foot); + [BenchmarkCategory(ToSame), Benchmark] + public Double TowelMeasurementsToSame() => tImperial[Feet]; +} + +/* Summary * + +BenchmarkDotNet v0.13.12, Arch Linux +Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores +.NET SDK 8.0.103 + [Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + + +| Method | Mean | Error | Ratio | Allocated | Alloc Ratio | +|---------------------------- |-----------:|----------:|-------:|----------:|------------:| +| QuantityToImperial | 3.5543 ns | 0.0593 ns | 1.00 | - | NA | +| UnitsNetToImperial | 80.8526 ns | 1.5024 ns | 22.78 | 48 B | NA | +| QuantityTypesToImperial | 0.0746 ns | 0.0038 ns | 0.02 | - | NA | +| TowelMeasurementsToImperial | 3.6869 ns | 0.0139 ns | 1.04 | - | NA | +| | | | | | | +| QuantityToSame | 1.2234 ns | 0.0062 ns | 1.000 | - | NA | +| UnitsNetToSame | 0.4822 ns | 0.0077 ns | 0.394 | - | NA | +| QuantityTypesToSame | 0.0184 ns | 0.0041 ns | 0.015 | - | NA | +| TowelMeasurementsToSame | 0.0004 ns | 0.0009 ns | 0.000 | - | NA | +| | | | | | | +| QuantityToSi | 3.5698 ns | 0.0140 ns | 1.000 | - | NA | +| UnitsNetToSi | 82.4369 ns | 0.4410 ns | 23.109 | 48 B | NA | +| QuantityTypesToSi | 0.0000 ns | 0.0000 ns | 0.000 | - | NA | +| TowelMeasurementsToSi | 4.3121 ns | 0.0674 ns | 1.216 | - | NA | +*/ diff --git a/source/Quantities.Benchmark/Quantities.Benchmark.csproj b/source/Quantities.Benchmark/Quantities.Benchmark.csproj index 8bd62ce5..5f2b7a90 100644 --- a/source/Quantities.Benchmark/Quantities.Benchmark.csproj +++ b/source/Quantities.Benchmark/Quantities.Benchmark.csproj @@ -20,6 +20,9 @@ + + + diff --git a/source/Quantities.Test/Compare/QuantitiesAndUnitsNetAreEquallyAccurate.cs b/source/Quantities.Test/Compare/QuantitiesAndUnitsNetAreEquallyAccurate.cs new file mode 100644 index 00000000..412fd7b2 --- /dev/null +++ b/source/Quantities.Test/Compare/QuantitiesAndUnitsNetAreEquallyAccurate.cs @@ -0,0 +1,87 @@ +using UnitsNet; +using Quantities.Units.Si.Metric; +using Quantities.Units.Si.Metric.UnitsOfInformation; +using Quantities.Units.Si.Derived; +using Quantities.Units.Imperial.Temperature; +using Quantities.Units.NonStandard.Temperature; + +using nLength = UnitsNet.Length; +using nTemperature = UnitsNet.Temperature; +using Bytes = Quantities.Units.Si.Metric.UnitsOfInformation.Byte; + +namespace Quantities.Test.Compare; + +public class QuantitiesAndUnitsNetAreEquallyAccurate +{ + [Fact] + public void ÅngströmToNanoMetre() + { + const Double expectedNanoMetre = 1d; + + // Quantities + Length ångström = Length.Of(10, Metric<Ångström>()); + Length nanoMetres = ångström.To(Si()); + + // UnitsNet + nLength nÅngström = nLength.FromAngstroms(10); + nLength nNanoMetre = nÅngström.ToUnit(UnitsNet.Units.LengthUnit.Nanometer); + + // Equally precise + Assert.Equal(expectedNanoMetre, nanoMetres); + Assert.Equal(expectedNanoMetre, nNanoMetre.Value); + } + + [Fact] + public void GibiBitPerHourToKiloBytePerMinute() + { + const Double expectedRate = 1024d * 1024d * 1024d / 1000d; + + // Quantities + DataRate speed = DataRate.Of(8, Binary().Per(Si())); + DataRate actual = speed.To(Metric().Per(Si())); + + // UnitsNet + BitRate nSpeed = BitRate.FromGibibitsPerSecond(8); + BitRate nActual = nSpeed.ToUnit(UnitsNet.Units.BitRateUnit.KilobytePerSecond); + + // Equally precise + Assert.Equal(expectedRate, actual); + Assert.Equal(expectedRate, (Double)nActual.Value); // but Value is a Decimal here :-/ + } + + [Fact] + public void CelsiusToFahrenheit() + { + const Double expectedFahrenheit = 98.6; + + // Quantities + Temperature temperature = Temperature.Of(37.0, Metric()); + Temperature actual = temperature.To(Imperial()); + + // UnitsNet + nTemperature nTemperature = nTemperature.FromDegreesCelsius(37.0); + nTemperature nActual = nTemperature.ToUnit(UnitsNet.Units.TemperatureUnit.DegreeFahrenheit); + + // Equally precise :-) + Assert.Equal(expectedFahrenheit, actual); + Assert.Equal(expectedFahrenheit, nActual.Value); + } + + [Fact] + public void KelvinToRømer() + { + const Double expectedRømer = -1.8345; // calculated using full precision math. + + // Quantities + Temperature temperature = Temperature.Of(255.37, Si()); + Temperature actual = temperature.To(NonStandard()); + + // UnitsNet + nTemperature nTemperature = nTemperature.FromKelvins(255.37); + nTemperature nActual = nTemperature.ToUnit(UnitsNet.Units.TemperatureUnit.DegreeRoemer); + + // Quantities and UnitsNet are equally bad (measured by precision parameter only) + Assert.Equal(expectedRømer, actual, 13); // -1.8344999999999885 + Assert.Equal(expectedRømer, nActual.Value, 13); // -1.83449999999999 + } +} diff --git a/source/Quantities.Test/Compare/QuantitiesIsMoreAccurateThanUnitsNet.cs b/source/Quantities.Test/Compare/QuantitiesIsMoreAccurateThanUnitsNet.cs new file mode 100644 index 00000000..37fbd28d --- /dev/null +++ b/source/Quantities.Test/Compare/QuantitiesIsMoreAccurateThanUnitsNet.cs @@ -0,0 +1,116 @@ +using Quantities.Units.Si.Metric; +using Quantities.Units.Imperial.Area; +using Quantities.Units.Imperial.Mass; + +using nArea = UnitsNet.Area; +using nLength = UnitsNet.Length; +using nMass = UnitsNet.Mass; +using nVolume = UnitsNet.Volume; + +namespace Quantities.Test.Compare; + +public class QuantitiesIsMoreAccurateThanUnitsNet +{ + [Fact] + public void SquareMilesToSquareKilometres() + { + Double expectedSqKiloMetre = 5.179976220672; // 2×1.609344×1.609344 using full precision math. + + // Quantities + Area squareMiles = Area.Of(2, Square(Imperial())); + Area actual = squareMiles.To(Square(Si())); + + // UnitsNet + nArea nSquareMiles = nArea.FromSquareMiles(2); + nArea nActual = nSquareMiles.ToUnit(UnitsNet.Units.AreaUnit.SquareKilometer); + + // Quantities is marginally more accurate that UnitsNet + Assert.Equal(expectedSqKiloMetre, actual); + Assert.Equal(expectedSqKiloMetre, nActual.Value, 14); // two digits less accurate. + + // Sanity check + Double roundRobinSqMiles = actual.To(Square(Imperial())); + Double nRoundRobinSqMiles = nActual.ToUnit(UnitsNet.Units.AreaUnit.SquareMile).Value; + Assert.Equal(2d, roundRobinSqMiles); + Assert.Equal(2d, nRoundRobinSqMiles); + } + + [Fact] + public void SquareFeetTimesYards() + { + const Double expectedCubicFeet = 162; + + // Quantities + Area area = Area.Of(27, Square(Imperial())); + Length length = Length.Of(2, Imperial()); + + Volume actual = area * length; + + // UnitsNet + nArea nA = nArea.FromSquareFeet(27); + nLength nL = nLength.FromYards(2); + + nVolume nActualInCubicMetres = nA * nL; // results in m³ + // need to convert to cubic feet first... + nVolume nActual = nActualInCubicMetres.ToUnit(UnitsNet.Units.VolumeUnit.CubicFoot); + + // Quantities is marginally more accurate that UnitsNet + Assert.Equal(expectedCubicFeet, actual); + Assert.Equal(expectedCubicFeet, nActual.Value, 13); + + // Benefit of the doubt, let's check UnitsNet default answer: m³ + const Double expectedCubicMetre = 4.587329147904; // 162×(0.3048×0.3048×0.3048) using full precision math + Volume cubicMetre = actual.To(Cubic(Si())); + Assert.Equal(expectedCubicMetre, cubicMetre); // is accurate + Assert.Equal(expectedCubicMetre, nActualInCubicMetres.Value); // is also accurate + } + + [Fact] + public void PureArealDimensionDividedByLength() + { + const Double expectedYards = 16; + + // Quantities + Area area = Area.Of(2, Imperial()); + Length length = Length.Of(1815, Imperial()); + + Length actual = area / length; + + // UnitsNet + nArea nA = nArea.FromAcres(2); + nLength nL = nLength.FromFeet(1815); + + nLength nActualInMeters = nA / nL; // is in m + // need to convert to yards first + nLength nActual = nActualInMeters.ToUnit(UnitsNet.Units.LengthUnit.Yard); + + // Quantities is marginally more accurate that UnitsNet + Assert.Equal(expectedYards, actual); + Assert.Equal(expectedYards, nActual.Value, 14); + + // Benefit of the doubt, let's check UnitsNet default answer: m + const Double expectedMetre = 14.6304; // 16×0.9144 using full precision math + Length metres = actual.To(Si()); + Assert.Equal(expectedMetre, metres); // is accurate + Assert.Equal(expectedMetre, nActualInMeters.Value, 14); // is less accurate + } + + [Fact] + public void GramToPound() + { + const Double expectedPounds = 3d; + const Double gramsInPound = 453.59237; // this is the definition of the pound in grams... + + // Quantities + Mass mass = Mass.Of(expectedPounds * gramsInPound, Metric()); + Mass actual = mass.To(Imperial()); + + // UnitsNet + nMass nMass = nMass.FromGrams(expectedPounds * gramsInPound); + nMass nActual = nMass.ToUnit(UnitsNet.Units.MassUnit.Pound); + + // Quantities is marginally more accurate that UnitsNet + Assert.Equal(expectedPounds, actual); + Assert.Equal(expectedPounds, nActual.Value, 15); + } +} diff --git a/source/Quantities.Test/Compare/UnitsNetIsMoreAccurateThanQuantities.cs b/source/Quantities.Test/Compare/UnitsNetIsMoreAccurateThanQuantities.cs new file mode 100644 index 00000000..8e1522e7 --- /dev/null +++ b/source/Quantities.Test/Compare/UnitsNetIsMoreAccurateThanQuantities.cs @@ -0,0 +1,7 @@ +namespace Quantities.Test.Compare; + +public class UnitsNetIsMoreAccurateThanQuantities +{ + [Fact(Skip = "No known examples so far")] + public void NoKnownExamplesSoFar() { } +} diff --git a/source/Quantities.Test/Quantities.Test.csproj b/source/Quantities.Test/Quantities.Test.csproj index d319eb9b..114c797b 100644 --- a/source/Quantities.Test/Quantities.Test.csproj +++ b/source/Quantities.Test/Quantities.Test.csproj @@ -12,4 +12,8 @@ + + + +