5

I'm trying to find a way to automatically format a measurement and unit into a String in engineering notation. This is a special case of scientific notation, in that the exponent is always a multiple of three, but is denoted using kilo, mega, milli, micro prefixes.

This would be similar to this post except it should handle the whole range of SI units and prefixes.

For example, I'm after a library that will format quantities such that: 12345.6789 Hz would be formatted as 12 kHz or 12.346 kHz or 12.3456789 kHz 1234567.89 J would be formatted as 1 MJ or 1.23 MJ or 1.2345 MJ And so on.

JSR-275 / JScience handle the unit measurement ok, but I'm yet to find something that will work out the most appropriate scaling prefix automatically based on the magnitude of the measurement.

Cheers, Sam.

Community
  • 1
  • 1
SamWest
  • 132
  • 1
  • 8
  • This seems a straightforward place to roll your own implementation. The only tricky part is dealing with quantities like mass, where the SI unit already has a metric prefix. – Anon. Feb 18 '11 at 00:42
  • @Anon mass would probably be easy if you just told it to think in terms of grams, even if it's not the proper SI unit. – corsiKa Feb 18 '11 at 00:46

1 Answers1

6
import java.util.*;
class Measurement {
    public static final Map<Integer,String> prefixes;
    static {
        Map<Integer,String> tempPrefixes = new HashMap<Integer,String>();
        tempPrefixes.put(0,"");
        tempPrefixes.put(3,"k");
        tempPrefixes.put(6,"M");
        tempPrefixes.put(9,"G");
        tempPrefixes.put(12,"T");
        tempPrefixes.put(-3,"m");
        tempPrefixes.put(-6,"u");
        prefixes = Collections.unmodifiableMap(tempPrefixes);
    }

    String type;
    double value;

    public Measurement(double value, String type) {
        this.value = value;
        this.type = type;
    }

    public String toString() {
        double tval = value;
        int order = 0;
        while(tval > 1000.0) {
            tval /= 1000.0;
            order += 3;
        }
        while(tval < 1.0) {
            tval *= 1000.0;
            order -= 3;
        }
        return tval + prefixes.get(order) + type;
    }

    public static void main(String[] args) {
        Measurement dist = new Measurement(1337,"m"); // should be 1.337Km
        Measurement freq = new Measurement(12345678,"hz"); // should be 12.3Mhz
        Measurement tiny = new Measurement(0.00034,"m"); // should be 0.34mm

        System.out.println(dist);
        System.out.println(freq);
        System.out.println(tiny);

    }

}
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • Note that "kilo" should be a lowercase k. Additionally, I'm pretty sure that you want, say, "340 um" (rather than 0.34 mm), so your second loop should be `while(tval < 1)`. – Anon. Feb 18 '11 at 00:59
  • I see. I'll throw those in. I was assuming anything within 3 of the decimal point is okay. It appears we want anything the component to be 1 <= c < 1000. Thanks for the clarification! I also tossed u into the map (even though it's not the exact letter, it should work.) – corsiKa Feb 18 '11 at 01:04
  • Comment by user without comment privileges (Xp-ert): Add `if(tval != 0) {` before `while(tval > 1000.0) {` and `}` before `return tval + prefixes.get(order) + type;` , otherwise the app loops forever in the second loop when value is `0`. – Anne Nov 22 '11 at 20:28
  • @Anne I see what you're saying. Yes, zero is a special case. I think instead of having it be 0, we should establish a range close enough to zero for us to call it 0 instead of comparing directly to 0. I'd also want to expand my list of prefixes down to whatever the threshold was, so that all inputs would be covered. – corsiKa Nov 22 '11 at 20:54