The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

inch/centimeter/mile/etc library?

I've been goofing around a lot lately with strongly typed functional languages (ocaml and F#) and representing measurements is one area that I think is expressed rather nicely with ADTs and type safety.  You really can't have something like the Mars Climate Orbiter error, where one team was using English measurements and the other was using metric units.

I started thinking, How would I do the same thing in an OO way? And the design I come up with is a convoluted mess.  So I figured there has to be some java library out there that does this, but everything I'm finding is just static conversion functions with the units stored as doubles, leading back to something along the lines of using hungarian notation to know what type your variable is.  Nothing where they try to enforce type safety by creating (say) an inch class.

So that leaves me with two questions:

Can anyone recommend an OO library that tries to safely control manipulations of measurements with different units where I can look at the source?

And if not, how would you design something like that in an OO way? Am I missing a clear and concise way to do this in an OO style?
Grant Send private email
Wednesday, June 06, 2007
 
 
This is one case where the presence or absence of things like  operator overloading and implicit conversion operators could strongly influence the design of the library.

Having thought about the problem for all of a minute or two, my first reaction would be to have a base class for each general category of unit, such as distance or speed. That class works in terms of some standardized representation, such as meters for distance. Each specific unit is then represented by a derived class, each of which provides virtual functions to get or set the current value in terms of the standard unit as well as a constructor, e.g:

class distance {
public:
  // various arithmetic operators, etc as needed

protected:
  virtual double in_meters() = 0;
  virtual void set_in_meters(double n_meters) = 0;
};

class feet : public distance {
public:
  explicit feet(double n_feet) : itsValue(n_feet) {}

private:
  virtual double in_meters() {
      return itsValue * 0.3048;
  }
  virtual void set_in_meters(double n_meters) {
      itsValue = n_meters / 0.3048;
  }

  double itsValue;
};


The key here is that all operations other than creating an object in the first place can be provided by the base class for each type of unit.

Since the derived classes differ only in the conversion factor, it should be possible to create them as instances of a single template in C++. I assume you could do something similar through a different mechanism in other languages.

In this case, God and/or the devil is in the details. The way you define conversions and arithmetic operations will determine how useable the system is.
clcr
Wednesday, June 06, 2007
 
 
Thanks.

I wasn't thinking about storing a default unit in the base class.  So I was thinking you'd end up with a cross product of conversion functions, and would have to modify like 20 classes when you added a new unit type to implement .convertTo/FromCubits().
Grant Send private email
Wednesday, June 06, 2007
 
 
Yeah, that could get rough when your users want to know their car's gas mileage in rods to the hogshead or whatever.
clcr
Wednesday, June 06, 2007
 
 
Google can convert gallons to hogsheads, and miles to rods - so it may seem silly, but it's not actually that complicated.  ;)

Wednesday, June 06, 2007
 
 
If you are using C++, template is your friend:

template<class T>
class UnitConverter
{
public:
    template<typename UnitT>
    static    UnitT FromEnglish(UnitT val) { return val*T::GetFactor(val); }
    
    template<typename UnitT>
    static    UnitT ToEnglish(UnitT val) { return val/T::GetFactor(val); }
};

class inch_to_cm
{
public:
    template<typename UnitT>
    static UnitT GetFactor(UnitT) { return    (UnitT)2.54; }
};

For each conversion pair, write a class similar to inch_to_cm. The class is so defined so that it can accept custom unit class type, as long as there is a conversion from double to that unit class type, and operators * and / are defined.

Now do some conversions:
    double val_in_inch = 1.345;
    double val_in_cm = UnitConverter<inch_to_cm>::FromEnglish(val_in_inch);

    std::cout << val_in_cm;

    float val_in_cm2 = 1.345f;
    std::cout << UnitConverter<inch_to_cm>::ToEnglish(val_in_cm2);
Glitch
Wednesday, June 06, 2007
 
 
The converter can be applied on an integer type - the result is, of course, still an integer:

    std::cout << UnitConverter<inch_to_cm>::ToEnglish(12345);
Glitch
Wednesday, June 06, 2007
 
 
Glitch:

The OP's goal is not so much to ease conversions as to make it impossible to use different units in the same expression. It's not clear to me that your code achieves that. If I understand correctly, the custom unit classes would have to provide an implicit conversion to double, which would introduce exactly the kind of type safety problems that you'd have if you just used double throughout.

Correct me if I'm wrong.
clcr
Wednesday, June 06, 2007
 
 
From the top of my head, D language allos you to have strong type def alias. So for example.

typedef int INT
typedef int DOUBLE

INT i = (INT)3;
DOUBLE d = i; // would report error, typedef definition miss match.

The other problem with encapsulating primitive types into object is the runtime overhead that you will be paying for your application. You need to take into account, typically those space agency's are working with limited at best RAM/ROM modules and hardware. The next thing is they need to make sure that x routine completes in y amount of time. So I think even themselves looked at the option your currently research, and decided the overhead is too great.

You have three choices and i think the best is a combination of all three.

Drop the OO idea about encapsulating primitive data types, its going to cause you more problems than it solves.

Introduce a standard measurement unit throughout the source code. For example 1 = 1 meter.

use typedef for different measurement units, for your data types to make the calling to the function explicit.

Look into contract programing, to define all your pre and post conditions of your routines. So the person whos using it has all the contractual obligations defined to him when calling a routine.

In my opinion your trying too hard to build your routines that are trying to do something usefull but also taking into account all the other times when it will be used incorrectly. If you continue, that i hope you haven't then you will find simple routines here and there soon blossems into very complex monster. Instead keep your routines clear and precise to the people who will be using them.

Such I recommendations to drop OOP. and go with typedefs and contracts to explicitly tell your client what he needs to call the function.

If he violates these conditions then all bets are off. You either assert or throw and exception. You cannot do anything else.
entity Send private email
Wednesday, June 06, 2007
 
 
The problem is rather deeper if you need to support more than simple add/subtract/compare operations.  Multiplication and division of different units within the same system of measures represents not a change of scale, but a change of dimension.  A robust system needs to know:
grams * centimeters / seconds^2 yields dynes, and
kilograms * meters / seconds^2 yields Newtons, and
multiplying meters by grams is probably leading nowhere.

Not to mention the problem of measures that may be decomposed among different dimensions:  watt-hours, foot-pounds, and litre-atmospheres all measure the same dimension as Joules.
Mikkin
Wednesday, June 06, 2007
 
 
"If he violates these conditions then all bets are off."

entity, I think the OP wants to make it impossible to violate the conditions.  You suggest a pragmatic approach, but I'll bet your approach is pretty close to what was used by the NASA engineers.  Unfortunately, the compiler was not able to tell that they violated the constraints.
Doug
Wednesday, June 06, 2007
 
 
In my accounting software I store everything in SI metric.  The user can enter or display in any unit he wants but all are stored internally in SI metric.  The conversion happens transparently depending on the user's preferred units.  The multiplication and division of units will take an extra string argument that will specify the exact unit the user is working on at present.

I coded almost every type of unit types.
Billable units:
Lengh, area, volume, weight, energy, data, time.
Item description units:
Power, velocity, line density, density, pressure, torque, spread rate by volume, fuel consumption, absorbed dose, acceleration, admittance, substance, angle, capacitance, charge, conductance, current, data transfer rate, electrical displacement, elastance, voltage, electric field strength, flow rate, fluidity, force, frequency, illuminance, inductance, luminance, luminous intensity, luminous flux, magnetic field density, magnetic flux, magnetic flux density, magneto motive force, permeability, permeance, permitivity, gain, reluctance, resistance, resistivity, rotational velocity, susceptance, viscosity, temperature, area density.

... phew coding multiple UOM for each of those unit types was a lot of work...
Donald Duck
Thursday, June 07, 2007
 
 
We store things as the user entered them, i.e. as strings. This is far from optimal from a processor/memory point of view, but we only deal with at most a couple of hundred values so this is irrelevant on modern computers.

The advantage we gain is that we always display what the user mean't. As an example, an American woodworker typically uses a quarter inch notation. They like to enter things such as 8/4" to represent 2" because this is how their wood comes from the saw mill.
Adrian
Thursday, June 07, 2007
 
 
Look at some Money packages for inspiration. The Test Infected article that introduced JUnit to many of us handles bags of mixed currencies and resolves all the differences only when you want a total value or something.
Stan James Send private email
Thursday, June 07, 2007
 
 
"Drop the OO idea about encapsulating primitive data types, its going to cause you more problems than it solves."

Well yeah, that was my point.  ADT's handle this problem rather elegantly, and I wanted to make sure that there wasn't some blatantly obvious OO-style way that would invalidate my assumption that ADT's were better in this case.

Thanks for the D example though.  I keep hearing it's 'better' C++.  Illustrating the point that typedefs are real declarations, and not some pre-processor bs really drives that home.
Grant Send private email
Thursday, June 07, 2007
 
 
My 2c

Don't create a class for every unit, encapsulate the unit along with the value.

class Distance {
  Distance(double theValue, Unit theUnit) ...

then build the arithmetic routines to do appropriate conversions.

So then you can do:

Distance x = Distance(12, Unit.Inch) + Distance(2, Unit.Foot);
if (x == Distance(1, Unit.Yard)) ...
Mike Stockdale Send private email
Thursday, June 07, 2007
 
 
I would just have a Distance class with accessors in different units:

class Distance
{
private:
  float distance_in_milimeters;
  static const float mmToInches = ...;
  static const float mmToMeters = ...;
  static const float mmToMiles = ...;
  static const float mmToPicas = ...;
  static const float mmToCiceros = ...;
  static const float mmToFeet = ...;
  static const float mmToYards = ...;
public:
  float inches(void);
  float meters(void);
  float miles(void);
  float picas(void);
  float ciceros(void);
  float feet(void);
  float yards(void);

  float setInches(float n);
  float setMeters(float n);
  float setMiles(float n);
  float setPicas(float n);
  flaot setCiceros(float n);
  float setFeet(float n);
  float setYards(float n);

  ...
}

Now all distances can be compared and combined regardless of the unit used to set or get the value.
Jeff Dutky Send private email
Thursday, June 07, 2007
 
 
There is a Python module that does what you describe (as far as I can see, at least): Unum ( http://home.scarlet.be/be052320/Unum.html ).

I know it exists but I have never looked at it, so I have no idea how easily you could transfer the concepts to Java.
Roel Schroeven Send private email
Thursday, June 07, 2007
 
 
"class Distance
{
private:
  float distance_in_milimeters;"

Use double, float is no way precise enough when converted twice.
Also meter is the metric unit.  Always store in metric and convert to and from metric.  Kilogram is the only SI metric that has a default prefix of kilo.
Donald Duck
Friday, June 08, 2007
 
 
Grant, just a follow up in regards to the post. Just curious what solution did you eventual go with? Or is it still on the table?
Entity Send private email
Sunday, June 10, 2007
 
 
Checkout the JSR 275 work:

  https://jsr-275.dev.java.net/

Sunday, June 10, 2007
 
 
And there was I thinking that "rods" and "hogsheads" were made-up by the writers on The Simpsons!
Syd Send private email
Monday, June 11, 2007
 
 
Entity,

Like I said, I was just more interested in seeing designs, I wasn't looking for a library per se.  The most flexible approach seems to be 'new Distance(12, units.Yards)', although that doesn't strike me as particulary "Object-Oriented".  That is to say, it doesn't use Polymorphism, doesn't do anything you couldn't do via 'structured programming' and a C-style union.
Grant Send private email
Monday, June 11, 2007
 
 
Of course, as with many things, this is ridiculously easy in Ruby.  Just add new calls to the built-in numeric class.  Here's one guy's solution.

http://warrenseen.com/blog/2007/02/22/ruby-cheap-tricks-monkeypatching-unit-conversion/
Still not missing Java
Monday, June 11, 2007
 
 
I've worked on a library that did exactly this (proprietary, so I'm afraid I can't sell it to you).

The trick is not to code a separate class for each kind of measurement you might encounter (like distance) but to have a class that records the 'dimensionality' of the quantity in terms of fundamental units. Fundamental units are "mass", "length", "time" plus (if you think you will need them) angle and a few more. "Speed" therefore mass*time^-1. You define a fundamental set of units (SI is good) and your fundamental object has the 'value' (in SI) plus the dimensionality of the quanity. Knowing the dimensionality allows you to freely convert to anything with the same dimensionality, provided you know the conversion factors between that unit and the SI equivalent. Thus you can convert from any quantity to any other.
DJ Clayworth
Tuesday, June 12, 2007
 
 
Sorry, in the above read "speed=length*time^-1" of course.
DJ Clayworth
Tuesday, June 12, 2007
 
 
yeah that is good.  So it would be something like this (C++ like pseudo code)

class number
{
double value
int distance
int time
int mass

}

now for multiplication you add the ints and multiply the values.  For division you subtract and divide.  Addition and subtraction are only legal if the ints are the same, and then you operate on the values.  To do this at compiletime you would have to switch to a templates that have similar semantics.  Anyway.  Now to make this system simple you add a constructor for number types that sets the ints to 0.  Then you make a bunch of predefined objects representing units.  So the object meter would have value=1, distance=1, time=0, and mass=0.  Ok have to go.
Tom C
Tuesday, June 12, 2007
 
 
err wait, forgot the conclusion:  so you could do this: 5*meter.  The result would be an object with value 5, distance=1, time=0, and mass=0.  Then if you divided by 2*second you would get value=2.5, distance=1, time=-1, and mass=0.  So a velocity of 2.5 m/s
Tom C
Tuesday, June 12, 2007
 
 
So what do you do for area? You'd need two distance values. I think what DJ was suggesting was a single value, plus a constant that defines what the value represents. Please correct me if I'm wrong.

I think in C++ you could do something fun with templates, but I haven't had time to flesh it out yet. I'm still thinking of separate base classes for distance, area, mass, etc. because that provides the type safety the O.P. was looking for.
Mark Ransom Send private email
Tuesday, June 12, 2007
 
 
ok it is like this (not real code)

the key is that it is templated on integrals, not types

template<int distance, int time, int mass, etc.....>
class measure
{
  double value

  //throw in a private constructor taking 1 parameter
  //maybe a public taking 0

}


measure<d3,t3,m3> operator* (measure<d1,t1,m1> left, measure<d2,t2,m2> right)
{
return measure<d1+d2,t1+t2,m1+m3>(left.value*right.value;
}


measure<d,t,m> operator+ (measure<d,t,m> left, measure<d,t,m> right)
{
return measure<d,t,m>(left.value+right.value;
}


then make kilo be the following object:

measure<0,0,0>(1000);

and meter be the following object:

measure<1,0,0>(1);

let there be a conversion that turns 6 into the following object:

measure<0,0,0>(6);

now this code:

6*kilo*meter

creates this object:

measure<1,0,0>(6000)

Then if you multiply that by itself:

measure<2,0,0>(36000000>

which is 36 square kilometers
Tom C
Wednesday, June 13, 2007
 
 
Yep, that's pretty much it. I wouldn't do it with templates, I'd make an object that defines the dimensionality, so something like

class Dimension {

  int distance;
  int mass;
  int time;
  int temperature;

  public Dimension(int dis,int mas,int tim,int temp);

}

and

class UnitValue {
  double value;
  Dimension dimension;

  public UnitValue(double val,int dis,int tim,int temp);
}

UnitValue squareKilometer = new UnitValue(1000000,2,0,0,0);

That makes it easier to add new dimensions; you might eventually want angle, solidangle, luminance.

Incidentally, there are some (fairly obscure) quantities that have non-integer values of dimensions; you probably won't need them unless you are writing a general purpose library.
DJ Clayworth
Wednesday, June 13, 2007
 
 
I have not implemented it yet (this weekend) but I think that templates would be easier for the user in addition to being faster.  For example, under my system velocity is the type:

measurement<1,-1,0>

so I would do a typedef as follows:

typedef velocity measurement<1,-1,0>;

and then I could do this:

velocity car_velocity = 55*mile/hour;
mass car_mass = 1000*kilogram;
momentum car_momentum = car_velocity*car_momentum;

and it would be compile-time checked and the math would be efficient.
Tom C
Wednesday, June 13, 2007
 
 
Your solution would probably be better if you're dealing only with a small set of quantities.
DJ Clayworth
Thursday, June 14, 2007
 
 
but you can't get compile-time type-safety with your solution.  Type-safety is the main point of this library right?
Tom C
Thursday, June 14, 2007
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz