October 07, 2008
Reply to bearophile,

> BCS:
> 
>> take a look at si.d first as it's the most useful intro (look way
>> down at the bottom)
>> 
> I think there's a need of some syntactic sugar :-)
> 
> Bye,
> bearophile

I put up some better examples.

http://www.dsource.org/projects/scrapple/browser/trunk/units/si.d


October 09, 2008
This has been done in D already:
----------------------------------
// 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);
	}
}
October 09, 2008
Reply to Walter,

> This has been done in D already:

It has some interesting features :) (I might have to steal a few for my version)


October 09, 2008
On Thu, 09 Oct 2008 23:35:58 +0400, BCS <ao@pathlink.com> wrote:

> Reply to Walter,
>
>> This has been done in D already:
>
> It has some interesting features :) (I might have to steal a few for my version)
>
>

I still think my version is superior (it handled floating point powers, supports an arbitrary number of basic units (given that they are orthogonal) and allows adding them with no original code modification) :p
October 09, 2008
Reply to Denis,

> On Thu, 09 Oct 2008 23:35:58 +0400, BCS <ao@pathlink.com> wrote:
> 
>> Reply to Walter,
>> 
>>> This has been done in D already:
>>> 
>> It has some interesting features :) (I might have to steal a few for
>> my  version)
>> 
> I still think my version is superior (it handled floating point
> powers,

Re: FP, I would count that as worse than basic integers because it runs the risk of FP rounding errors. Mine will handle rational powers (1/2, 23/43, etc) and won't suffer from the loss of precision. The only places I have ever seen non rational exponents in use are in data fitting applications and just switching to a close enough rational is as good as anything there. Also, the non rational cases where mine might suffer little are the cases where FP problem are /most/ likely to crop up.

>  supports an arbitrary number of basic units (given that they
> are  orthogonal) and allows adding them with no original code
> modification) :p
> 

You may have me on for that point, but extensibility also has it's down side; different people add the same dimension independently and then someone wants to mix them.

The 5 dimensions I picked will cover almost all that cases for just about anyone.

I think we each picked a different set of design choices and created a solution for them. I do think that mine is better than yours by the criteria I'm using. I can see legitimate criteria where yours is better.


1 2
Next ›   Last »