Daniel wrote:
I also kind of hoped we'd see standard real world unit types in D. Like sqft,
meters, ohms, kg, L, Hz, seconds,
etc. native to D. Being able to intrinsically convert types between these
things would make D the most
approachable General Programming Language for real world problems.
Here it is:
// 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);
}
}