[phobos] RFC: units type for D

Benjamin Shropshire benjamin at precisionsoftware.us
Wed Aug 25 15:26:21 PDT 2010


Hello Walter,

 > Benjamin Shropshire wrote:
 >
 >> I have offered up a library that supports statically encoding units
 >> in the type system so as to prevent unit errors (adding distance and
 >> time) and to enforce correct conversions all around.
 >>
 >> http://d.puremagic.com/issues/show_bug.cgi?id=3725
 >>
 >> I'm looking for comments: What's holding it back from inclusion? What
 >> would need to be improved? The API? Better comments? (I haven't
 >> tested it recently so; make it build again?)
 >>
 > How does it compare with the one Oskar Linde posted:
 >

Don't know. Haven't looked at it much. But one or the other should be 
added. I'm offering fix specific reasons people come up with to not add 
mine.

 > // by Oskar Linde Aug 2006
 > // This is just a quick hack to test
 > // IFTI operators opMul and opDel

Mine's not exactly a quick hack.

 > import std.stdio;
 > import std.math;
 > import std.string;
 > version = unicode;
 >
 > struct SiQuantity(T,int e1, int e2, int e3, int e4, int e5, int e6,
 > int
 > e7) {

doesn't seem to support fractional units, mine does.

 > T value = 0;
 > alias T ValueType;
 > const exp1 = e1;
 > const exp2 = e2;
 > const exp3 = e3;
 > const exp4 = e4;
 > const exp5 = e5;
 > const exp6 = e6;
 > const exp7 = e7;
 > static assert(SiQuantity.sizeof == ValueType.sizeof);
 >
 > template AddDimensions(int mul, U) {
 > static assert(is(U.ValueType == ValueType) ||
 > is(U == ValueType),
 > "incompatible value types");
 > static if (is(U == ValueType))
 > alias SiQuantity AddDimensions;
 > else
 > alias SiQuantity!(T,exp1+mul*U.exp1,exp2+mul*U.exp2,
 > exp3+mul*U.exp3,exp4+mul*U.exp4,
 > exp5+mul*U.exp5,exp6+mul*U.exp6,
 > exp7+U.exp7) AddDimensions;
 > }
 > SiQuantity opAddAssign(SiQuantity rhs) {
 > value += rhs.value;
 > return this;
 > }
 > SiQuantity opSubAssign(SiQuantity rhs) {
 > value -= rhs.value;
 > return this;
 > }
 > const
 > {
 > SiQuantity opAdd(SiQuantity rhs) {
 > SiQuantity ret;
 > ret.value = value + rhs.value;
 > return ret;
 > }
 > SiQuantity opSub(SiQuantity rhs) {
 > SiQuantity ret;
 > ret.value = value - rhs.value;
 > return ret;
 > }
 > SiQuantity opNeg() {
 > SiQuantity ret;
 > ret.value = -value;
 > return ret;
 > }
 > SiQuantity opPos() {
 > typeof(return) ret;
 > ret.value = value;
 > return ret;
 > }
 > int opCmp(SiQuantity rhs) {
 > if (value > rhs.value)
 > return 1;
 > if (value < rhs.value)
 > return -1;
 > return 0; // BUG: NaN
 > }
 > AddDimensions!(+1,Rhs) opMul(Rhs)(Rhs rhs) {
 > AddDimensions!(+1,Rhs) ret;
 > static if (is(Rhs : T))
 > ret.value = value * rhs;
 > else
 > ret.value = value * rhs.value;
 > return ret;
 > }
 > AddDimensions!(-1,Rhs) opDiv(Rhs)(Rhs rhs) {
 > AddDimensions!(-1,Rhs) ret;
 > static if (is(Rhs : T))
 > ret.value = value / rhs;
 > else
 > ret.value = value / rhs.value;
 > return ret;
 > }
 > SiQuantity opMul_r(T lhs) {
 > SiQuantity ret;
 > ret.value = lhs * value;
 > return ret;
 > }
 > SiQuantity!(T,-e1,-e2,-e3,-e4,-e5,-e6,-e7) opDiv_r(T lhs) {
 > SiQuantity!(T,-e1,-e2,-e3,-e4,-e5,-e6,-e7) ret;
 > ret.value = lhs / value;
 > return ret;
 > }

Mine lacks a unit formatting function (But I never figured out how to 
generate sane units: "W*s" vs. "m^2*kg*s^-2")

 > string toString() {
 > string prefix = "";
 > T multiplier = 1;
 > T value = this.value;
 > string unit;
 > static if (is(typeof(UnitName!(SiQuantity))))
 > unit = UnitName!(SiQuantity);
 > else {
 > value *= pow(cast(real)1e3,cast(uint)e2); // convert kg -> g
 > // Take mass (e2) first to handle kg->g prefix issue
 > if (e2 != 0) unit ~= format("·g^%s",e2);
 > if (e1 != 0) unit ~= format("·m^%s",e1);
 > if (e3 != 0) unit ~= format("·s^%s",e3);*kg*
 > if (e4 != 0) unit ~= format("·A^%s",e4);
 > if (e5 != 0) unit ~= format("·K^%s",e5);
 > if (e6 != 0) unit ~= format("·mol^%s",e6);
 > if (e7 != 0) unit ~= format("·cd^%s",e7);
 > if (unit)
 > unit = unit[2..$].split("^1").join("");
 > }
 > if (value >= 1e24) { prefix = "Y"; multiplier = 1e24; }
 > else if (value >= 1e21) { prefix = "Z"; multiplier = 1e21; }
 > else if (value >= 1e18) { prefix = "E"; multiplier = 1e18; }
 > else if (value >= 1e15) { prefix = "P"; multiplier = 1e15; }
 > else if (value >= 1e12) { prefix = "T"; multiplier = 1e12; }
 > else if (value >= 1e9) { prefix = "G"; multiplier = 1e9; }
 > else if (value >= 1e6) { prefix = "M"; multiplier = 1e6; }
 > else if (value >= 1e3) { prefix = "k"; multiplier = 1e3; }
 > else if (value >= 1) { }
 > else if (value >= 1e-3) { prefix = "m"; multiplier = 1e-3; }
 > else if (value >= 1e-6) {
 > version(unicode)
 > prefix = "µ";
 > else
 > prefix = "u";
 > multiplier = 1e-6; }
 > else if (value >= 1e-9) { prefix = "n"; multiplier = 1e-9; }
 > else if (value >= 1e-12) { prefix = "p"; multiplier = 1e-12; }
 > else if (value >= 1e-15) { prefix = "f"; multiplier = 1e-15; }
 > else if (value >= 1e-18) { prefix = "a"; multiplier = 1e-18; }
 > else if (value >= 1e-21) { prefix = "z"; multiplier = 1e-21; }
 > else if (value >= 1e-24) { prefix = "y"; multiplier = 1e-24; }
 > return format("%.3s %s%s",value/multiplier, prefix, unit);
 > }
 > }
 > }
 > //length meter m
 > //mass kilogram kg
 > //time second s
 > //electric current ampere A
 > //thermodynamic temperature kelvin K
 > //amount of substance mole mol
 > //luminous intensity candela cd
 > // Si base quantities
 > alias SiQuantity!(real,1,0,0,0,0,0,0) Length;
 > alias SiQuantity!(real,0,1,0,0,0,0,0) Mass;
 > alias SiQuantity!(real,0,0,1,0,0,0,0) Time;
 > alias SiQuantity!(real,0,0,0,1,0,0,0) Current;
 > alias SiQuantity!(real,0,0,0,0,1,0,0) Temperature;
 > alias SiQuantity!(real,0,0,0,0,0,1,0) AmountOfSubstance;
 > alias SiQuantity!(real,0,0,0,0,0,0,1) Intensity;
 > alias SiQuantity!(real,0,0,0,0,0,0,0) UnitLess;
 >
 > // Derived quantities
 > alias typeof(Length*Length) Area;
 > alias typeof(Length*Area) Volume;
 > alias typeof(Mass/Volume) Density;
 > alias typeof(Length*Mass/Time/Time) Force;
 > alias typeof(1/Time) Frequency;
 > alias typeof(Force/Area) Pressure;
 > alias typeof(Force*Length) Energy;
 > alias typeof(Energy/Time) Power;
 > alias typeof(Time*Current) Charge;
 > alias typeof(Power/Current) Voltage;
 > alias typeof(Charge/Voltage) Capacitance;
 > alias typeof(Voltage/Current) Resistance;
 > alias typeof(1/Resistance) Conductance;
 > alias typeof(Voltage*Time) MagneticFlux;
 > alias typeof(MagneticFlux/Area) MagneticFluxDensity;
 > alias typeof(MagneticFlux/Current) Inductance;
 > alias typeof(Intensity*UnitLess) LuminousFlux;
 > alias typeof(LuminousFlux/Area) Illuminance;
 > // SI fundamental units
 > const Length meter = {1};
 > const Mass kilogram = {1};
 > const Time second = {1};
 > const Current ampere = {1};
 > const Temperature kelvin = {1};
 > const AmountOfSubstance mole = {1};
 > const Intensity candela = {1};
 > // Derived units
 > const Frequency hertz = {1};
 > const Force newton = {1};
 > const Pressure pascal = {1};
 > const Energy joule = {1};
 > const Power watt = {1};
 > const Charge coulomb = {1};
 > const Voltage volt = {1};
 > const Capacitance farad = {1};
 > const Resistance ohm = {1};
 > const Conductance siemens = {1};
 > const MagneticFlux weber = {1};
 > const MagneticFluxDensity tesla = {1};
 > const Inductance henry = {1};
 > const LuminousFlux lumen = {1};
 > const Illuminance lux = {1};
 > template UnitName(U:Frequency) { const UnitName = "Hz"; }
 > template UnitName(U:Force) { const UnitName = "N"; }
 > template UnitName(U:Pressure) { const UnitName = "Pa"; }
 > template UnitName(U:Energy) { const UnitName = "J"; }
 > template UnitName(U:Power) { const UnitName = "W"; }
 > template UnitName(U:Charge) { const UnitName = "C"; }
 > template UnitName(U:Voltage) { const UnitName = "V"; }
 > template UnitName(U:Capacitance){ const UnitName = "F"; }
 > version(unicode) {
 > template UnitName(U:Resistance) { const UnitName = "Ω"; }
 > } else {
 > template UnitName(U:Resistance) { const UnitNAme = "ohm"; }
 > }
 > template UnitName(U:Conductance){ const UnitName = "S"; }
 > template UnitName(U:MagneticFlux){ const UnitName = "Wb"; }
 > template UnitName(U:MagneticFluxDensity) { const UnitName = "T"; }
 > template UnitName(U:Inductance) { const UnitName = "H"; }

Mine has a LOT more units included.

 > void main() {
 > Area a = 25 * meter * meter;
 > Length l = 10 * 1e3 * meter;
 > Volume vol = a * l;
 > Mass m = 100 * kilogram;
 > assert(!is(typeof(vol / m) == Density));
 > //Density density = vol / m; // dimension error -> syntax error
 > Density density = m / vol;
 > writefln("The volume is %s",vol.toString);
 > writefln("The mass is %s",m.toString);
 > writefln("The density is %s",density.toString);
 > writef("\nElectrical example:\n\n");
 > Voltage v = 5 * volt;
 > Resistance r = 1 * 1e3 * ohm;
 > Current i = v/r;
 > Time ti = 1 * second;
 > Power w = v*v/r;
 > Energy e = w * ti;
 > // One wishes the .toString was unnecessary...
 > writefln("A current of ",i.toString);
 > writefln("through a voltage of ",v.toString);
 > writefln("requires a resistance of ",r.toString);
 > writefln("and produces ",w.toString," of heat.");
 > writefln("Total energy used in ",ti.toString," is ",e.toString);
 > writef("\nCapacitor time curve:\n\n");
 >
 > Capacitance C = 0.47 * 1e-6 * farad; // Capacitance Voltage V0 = 5 *
 > volt; // Starting voltage Resistance R = 4.7 * 1e3 * ohm; //
 > Resistance
 >
 > for (Time t; t < 51 * 1e-3 * second; t += 1e-3 * second) { Voltage Vt
 > = V0 * exp((-t / (R*C)).value);
 >
 > writefln("at %5s the voltage is %s",t.toString,Vt.toString);
 > }
 > }
 > _______________________________________________
 > phobos mailing list
 > phobos at puremagic.com
 > http://lists.puremagic.com/mailman/listinfo/phobos



More information about the phobos mailing list