[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