jason-c-daniels - Jcd.Units 0.0.82
Provides types and extensions methods for unit of measure based, quantity comparisons, conversions, and arithmetic, as well as an extensive unit of measure catalog.
PM> Install-Package Jcd.Units -Version 0.0.82 -Source https://www.myget.org/F/jason-c-daniels/api/v3/index.json
> nuget.exe install Jcd.Units -Version 0.0.82 -Source https://www.myget.org/F/jason-c-daniels/api/v3/index.json
> dotnet add package Jcd.Units --version 0.0.82 --source https://www.myget.org/F/jason-c-daniels/api/v3/index.json
source https://www.myget.org/F/jason-c-daniels/api/v3/index.json
nuget Jcd.Units ~> 0.0.82
Copy to clipboard
> choco install Jcd.Units --version 0.0.82 --source https://www.myget.org/F/jason-c-daniels/api/v2
Import-Module PowerShellGet
Register-PSRepository -Name "jason-c-daniels" -SourceLocation "https://www.myget.org/F/jason-c-daniels/api/v2"
Install-Module -Name "Jcd.Units" -RequiredVersion "0.0.82" -Repository "jason-c-daniels"
Copy to clipboard
Jcd.Units
A dotnet 6 library that provides a set of utility classes and compile time safety for managing unit of measure bound quantities.
For example, you cannot meaningfully compare a Duration and an Length. The library prevents this.
Feature Overview
Core Types
The two core classes provided are UnitOfMeasure<TDerived> and Quantity<TUnit>.
In both cases TUnit is the type derived off of UnitOfMeasure.
This idiomatic method of coding, in part, provides the foundation for the arithmetic
and relational type safety. The rest is defined in Quantity<TUnit>, which can perform
arithmetic operations on itself and against doubles. A Quantity<TUnit> is always the result.
Ease of Use Methods and Extensions
A number of extension methods have been provided to easily convert integral data
and common types into instances of Quantity<TUnit>. With the exception of ToTimeStamp
(Which only operates on a Duration) they are all called As.
As well, Quantity<TUnit> provides a unit of measure conversion called To
which converts units within the same domain (e.g. Durations).
For example, the code below creates a Duration quantity of 1 second.
using Jcd.Units.UnitsOfMeasure;
// capture the units for readability
var second = Durations.Second;
var ms = Durations.Millisecond;
var min = Durations.Minute;
var oneSecond = 1.As(second); // oneSecond is of type Quantity<Duration> with a RawValue of 1.00d, and a Unit of Second
var oneSInMs = oneSecond.To(ms); // oneSInMs is of type Quantity<Duration> with a RawValue of 1,000.00d and a Unit of Millisecond
var ts = oneSInMs.ToTimeStamp(); // convert the milliseconds to a TimeStamp
var minDur = ts.As(min); // convert the TimeStamp to its minutes representation.
Feature Limitations
- This library does not auto-select equivalent derived units from multiplication or
division at runtime. So dividing a
Lengthby aDurationdoes not yield a velocity. - In fact, without explicitly casting one of the operands to a
doubleyou cannot perform arithmetic operations on quantities of disparate unit of measure types.- The first reason is that not all units have defined conversions of that sort.
- The second is that for complex and compound units, an algebraic solver would
be required. If you need that to happen automatically, without writing any code,
use MathCAD or Wolfram. This library only supports simple unit of measure
handling. You will still have to put some effort in to get the desired results.
However, my hope is that in using this library, it's a lower effort versus standard
doubles.
- Given the nature of the global hooks used to perform configurable unit of measure
selection and double comparisons, the performance is anywhere from 1 to 2 orders
of magnitude slower than using plain doubles. _(Plain
doublearithmetic sees a throughput of effectively 0.8-1.1 CPU cycles per operation on ,-, and *. Quantity NOTE: On a CPU running at 3.54GHz dividing oneQuantity<TUnit>by another, the slowest operation, is roughly 40ns. This is still suitable for many applications, just not high-throughput number crunching. - The types used for globally registering, and using, unit selectors and double comparers are not thread safe. Do not change these settings while calculations are running. The behavior is undefined and unpredictable. This is true regardless if changed from a background thread, or the current thread (via an async Task, or mid algorithm)
Examples
Basic Conversions, No Comparisons.
In the simplest use case you may not want to do more than convert among existing units. The following example demonstrates that.
using Jcd.Units;
using Jcd.Units.UnitsOfMeasure;
using SI=Jcd.Units.UnitsOfMeasure.SI;
using US=Jcd.Units.UnitsOfMeasure.USCustomary;
// for brevity sake we capture the unit of measures in variables
// and use those instead of referring to the entire namespace and
// containing enumeration-like class.
var C = SI.Temperatures.DegreesCelcius;
var F = US.Temperatures.DegreesFahrenheit;
var tF = 32.As(F);
var tC = tF.To(C);
Console.WriteLine($"{tF:n2} is {tC:n2}"); // outputs: 32.00 °F is 0.00 °C
var K = SI.Temperatures.DegreesKelvin;
var tF2 = tF + 75;
var tK = (tF + 75).To(K);
Console.WriteLine($"{tF2:n3} is {tK:n3}"); // outputs: 107.000 °F is 314.817 °K
var pT = Durations.PlanckTime;
var ms = Durations.Millisecond;
var oneMs = 1.As(ms);
var oneMsInPlankTime = oneMs.To(pT);
Console.WriteLine($"{oneMs:n2} is {oneMsInPlankTime:e5}"); // outputs: 1.00 ms is 1.85487e+040 tP
var twip = US.Lengths.Twip;
var mm = SI.Lengths.Millimeter;
var twentyTwips = 20.As(twip);
var twentyTwipsInMm = twentyTwips.To(mm);
Console.WriteLine($"{twentyTwips:n2} is {twentyTwipsInMm:n2}"); // outputs: 20.00 twip is 0.35 mm
// The following line will not compile.
// if (twentyTwips == oneMs) { /* that's just weird. */ }
Comparisons.
By default, this library uses the compiler provided IEEE754 double comparison. It's wrapped in a class that implements an interface that allows us to replace the comparison strategy at run time.
NOTE: As mentioned above you are responsible for ensuring your code doesn't replace comparison strategies while quantity an unit of measure comparisons and calculations are underway. Those methods are not thread safe.
For example the following code will execute and give the standard (sometimes unexpected results) from built-in double comparison.
var exactLength = (111.1).As(Lengths.Meter);
var userProvidedLength = GetValueFromUser(Lengths.Meter); // fake method.
// do some math on it. Increase by 1/3rd.
var mutatedLength = userProvidedLength * (1+1d/3d);
if (mutatedLength == exactLength)
{
// This will not be reached even if the user input 83.325 (the magic number, you must use a different comparison)
}
However, if during application startup we registered a better comparison strategy, such as a rounding comparer, it will work as intended.
using Jcd.Units.DoubleComparison;
// adding this one line and the using statement above, now fixes the comparison issue.
GlobalDoubleComparisonStrategy.Quantity = BuiltInRoundingComparer.FifteenDecimalPlaces;
var exactLength = (111.1).As(Lengths.Meter);
var userProvidedLength = GetValueFromUser(Lengths.Meter); // fake method.
// do some math on it. Increase by 1/3rd.
var mutatedLength = userProvidedLength * (1+1d/3d);
if (mutatedLength == exactLength)
{
// Because of the registered comparer, this code will be reached when the user inputs 83.325 (the magic number)
}
Conversion Among Unit Types
Given the limitations and context sensitivity of when one discards or changes the unit of measure applied to a number, four are three things to be aware of.
- Changing unit of measure is the same as
.RawValue.As(NewUnit). - No reference to the old unit of measure is maintained.
- Derive units of measure still abide by the
Coeffifient+Offsetconversions with the same unit type. - When calculating something like Area, ensure the quantities are in the requisite units of measure as needed by the destination unit of measure. So for calculating a rate as m/s, first convert the Length quantity to meters, and the Duration quantity to seconds, then perform the division, then apply the new unit of measure.
Example: Computing Area, Volume, and Rate
using Jcd.Units;
using Jcd.Units.UnitsOfMeasure;
using Jcd.Units.UnitsOfMeasure.SI;
/// capture the units for readability.
var m = Lengths.Meter;
var dm = Lengths.Decimeter;
var cm = Lengths.Centimeter;
var ms = Durations.Millisecond;
var s = Durations.Second;
var l1 = 10.As(cm);
var l2 = 20.As(dm);
var l3 = 30.As(m);
// DON'T do this if you need to convert to area.
var area = (l1*l2).ReplaceUnit(Areas.SquareCentimeter); // Which unit was actually selected? The default is the larger unit!
// Convert l2 to cm instead.
var area2 = (l1 * l2.To(cm)).ReplaceUnit(Areas.SquareCentimeter);
Badges
Initial development.
-
.NETFramework 6.0
- Jcd.Reflection (>= 2.0.26)
- Jcd.RichEnumerations (>= 0.2.51)
-
.NETFramework 7.0
- Jcd.Reflection (>= 2.0.26)
- Jcd.RichEnumerations (>= 0.2.51)
-
.NETFramework 8.0
- Jcd.Reflection (>= 2.0.26)
- Jcd.RichEnumerations (>= 0.2.51)
- .NETFramework 6.0: 6.0.0.0
- .NETFramework 7.0: 7.0.0.0
- .NETFramework 8.0: 8.0.0.0
OwnersJason C. Daniels |
AuthorsJason C. Daniels |
Project URLhttps://github.com/jason-c-daniels/Jcd.Units |
LicenseUnknown |
Info320 total downloads |
| 83 downloads for version 0.0.82 |
| Download (681.75 KB) |
| Found on the current feed only |
Package history
| Version | Size | Last updated | Downloads | Mirrored? | |||
|---|---|---|---|---|---|---|---|
|
|
0.0.82 | 681.75 KB | Mon, 26 Aug 2024 21:14:13 GMT | 83 |
|
||
|
|
0.0.51 | 328.94 KB | Mon, 15 Apr 2024 00:20:19 GMT | 82 |
|
||
|
|
0.0.6 | 328.78 KB | Sat, 01 Apr 2023 16:19:12 GMT | 68 |
|
||
|
|
0.0.2 | 310.36 KB | Sat, 01 Apr 2023 06:21:53 GMT | 87 |
|