[phobos] RFC: units type for D

Walter Bright walter at digitalmars.com
Wed Aug 25 10:52:28 PDT 2010



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:

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

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) {
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;
}

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);
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"; }

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);
}
}




More information about the phobos mailing list