home *** CD-ROM | disk | FTP | other *** search
- {********** Pascal/Z compiler options **********}
- {$C-}{ control-c keypress checking OFF }
- {$M-}{ integer mult & divd error checking OFF }
- {********** Pascal/Z compiler options **********}
-
-
-
- PROGRAM SuperMetrics;
- { PROGRAM TITLE: An Automatic Metric Conversion Program }
- { WRITTEN BY: David A. Mundie }
- { }
- { 12/15/82 - modified for Pascal/Z by Raymond E. Penley }
- { }
- { SUMMARY:
- The program distinguishes rigorously between customary units,
- primary metric units, and secondary metric units. By "primary" metric
- units is meant the System International (SI) base units such as metre,
- kilogram, kelvin, and so on, as well as the derived units such as
- watt, newton, m/s, pascal, and volt. By "secondary" metric units is
- meant units like degree C and km/h which are accepted but not part of
- SI, along with the formulas for derived units with special names, eg.
- kg m/s as the formulas for the newton.
-
- The program automatically converts customary and secondary units
- to primary units. Primary units may be converted to customary by using
- the "c" command, while the "s" command converts them to secondary units.
-
- PREFIXES USED BY SUPERMETRIC FOR MEASURMENTS OTHER THAN VOLUMES & AREAS.
-
- power prefix abbreviation
- ----- ------ ------------
- 10^18 exa E
- 10^15 peta P
- 10^12 tera T
- 10^9 giga G
- 10^6 mega M
- 10^3 kilo k
- 10^-3 milli m
- 10^-6 micro u
- 10^-9 nano n
- 10^-12 pico p
- 10^-15 femto f
- 10^-18 atto a
-
-
- PREFIXES USED BY SUPERMETRIC FOR VOLUMES AND AREAS
-
- power prefix abbreviation
- ----- ------ ------------
- 10^3 kilo k
- 10^2 hecto h
- 10^1 deka da
- 10^-1 deci d
- 10^-2 centi c
- 10^-3 milli m
-
- DIFFERENCES BETWEEN SUPERMETRIC AND CORRECT System International (SI) SYMBOLS.
-
- correct SI form SUPERMETRIC
- mu u
- da D
- . * (multiplication)
- o $ (degrees)
- m2, etc. m2
-
-
- }
- CONST
- CmdLine = 'COMMANDS: c(ustomary, h(elp, s(econdary, l(ist, f(inished';
- normp = 'afpnum kMGTPE'; { normal prefixes }
- specp = 'mcd Dhk'; { special prefixes for areas and volumes }
- maxentries = 100;
- z = 48; { ord('0') }
-
- TYPE
- index = 0..maxentries;
- strng = STRING 40;
- entry = RECORD
- left,right: strng;
- factor : real
- END;
-
- string0 = STRING 0;
- string255 = STRING 255;
-
- VAR
- bell : char; { console bell }
- curtop : index; { current top of table }
- current : index; { points to current entry }
- finished : boolean;
- leftside : boolean;
- line : strng; { one line of user input }
- m : real; { the measurement }
- maxcust : index; { top of customary section of table }
- oldm,oldf : real;
- p : integer; { the precision }
- table : ARRAY [ index ] OF entry;
- top : index; { permanent top of table }
- u : strng; { the unit }
-
- {*********************** Utilities ******************************************}
-
- FUNCTION length ( source: string255 ): integer;
- external;
-
- FUNCTION pos ( PATTERN, SOURCE: string255 ): integer;
- external;
-
- PROCEDURE delete ( var source: string0; start, count: integer );
- external;
-
- PROCEDURE copy ( var sub: string0; source: string255; here, count: integer );
- external;
-
- PROCEDURE insert ( pattern: string255; var dest: string0; posn: integer);
- external;
-
- PROCEDURE concat ( var new: string0; arg1,arg2: string255 );
- external;
-
- {*********************** mathematical utilities *****************************}
-
- FUNCTION floor ( r: real ): integer;
- BEGIN
- floor := trunc( r - ord( (r<0.0) AND (r<>trunc(r)) ) )
- END;
-
- FUNCTION nl ( a: real ): real;
- BEGIN
- IF a < 1.0
- THEN nl := -LN( a )
- ELSE nl := LN( a )
- END;
-
- FUNCTION power ( i,j: integer ): real;
- BEGIN
- power := exp ( nl(abs(i)) * j)
- END;
-
- FUNCTION log ( r: real ): real;
- BEGIN
- log := nl(abs(r)) / LN(10.0)
- END;
-
- FUNCTION norm ( r: real ): real;
- BEGIN
- norm := r / power(10,floor(log(r)))
- END;
-
-
- {************ convert a string to a real number *****************************}
-
- FUNCTION value ( VAR s: strng; VAR p: integer ): real;
- { returns p = number of significant digits }
- CONST
- limit = 1.67772E6; { (2**23)/5) }
- VAR
- a,y : real;
- e,i,j,p2 : integer;
- neg, negexp, gtl : boolean;
- digits: SET OF char;
-
- FUNCTION val ( a : real; ch: char ): real;
- BEGIN
- val := 10.0 * a + ord(ch)-z
- END{val};
-
- BEGIN
- i := 1;
- p := 0;
- p2 := 0;
- gtl := false;
- digits := ['0'..'9'];
- append(s,'%'); { safety character }
- a := 0.0;
- e := 0;
- neg := (s[i]='-');
- WHILE s[i]=' ' DO
- i := i + 1;
- IF (s[i]='+') OR (neg) THEN
- i := i + 1;
- WHILE s[i] IN digits DO BEGIN
- IF s[i]='0' THEN
- p2 := p2 + 1
- ELSE BEGIN
- p := p+p2+1;
- p2 := 0;
- gtl := true
- END;
- IF a<limit THEN
- a := val ( a, s[i] )
- ELSE
- e := e + 1;
- i := i + 1
- END;
- IF s[i]='.' THEN BEGIN
- p := p + p2;
- i := i + 1;
- IF NOT (s[i] IN digits) THEN BEGIN
- insert ( '0',s,i );
- i := i + 1
- END
- END;
- p2 := 0;
- WHILE s[i]='0' DO BEGIN
- p2 := p2 + 1;
- IF a<limit THEN BEGIN
- a := val ( a, s[i] );
- e := e - 1
- END;
- i := i + 1
- END;
- IF gtl THEN
- p := p + p2;
- WHILE s[i] IN digits DO BEGIN
- p := p+1;
- IF a<limit THEN BEGIN
- a := val ( a,s[i] );
- e := e - 1
- END;
- i := i+1
- END;
- IF (s[i] IN ['E','e']) THEN BEGIN
- i := i + 1;
- j := 0;
- negexp := (s[i]='-');
- IF (s[i]='+') OR negexp THEN
- i := i+1;
- WHILE s[i] IN digits DO BEGIN
- IF j<limit THEN
- j := 10*j+ord(s[i]) - z;
- i := i +1
- END;
- IF negexp THEN
- e := e - j
- ELSE
- e := e + j
- END;
- y := a;
- IF neg THEN
- y := -y;
- IF e<0 THEN
- value := y/power(10,-e)
- ELSE IF e<>0 THEN
- value := y*power(10,e)
- ELSE
- value := y;
- WHILE s[i]=' ' DO
- i := i+1;
- copy ( s,s,i,length(s)-i)
- END{value};
-
-
- {************* Write a real in appropriate format and return a blank *********}
-
- FUNCTION f ( r:real ): char;
- CONST
- width = 23;
- VAR
- intpart,decimals,floating: integer;
- BEGIN
- intpart := floor(log(r));
- decimals := p - intpart - 1;
- IF (r>10000.0) OR (r<0.0001) THEN {floating point}
- write ( r:width )
- ELSE IF decimals <= 0 THEN {integer}
- write ( round(r): width )
- ELSE {fixed point}
- write ( r:width:decimals );
- f := ' '
- END;
-
-
- {*************** Special handling for temperatures ***************************}
-
- PROCEDURE temperature ( VAR m: real; b: boolean; fact: integer );
- VAR d: integer;
- BEGIN
- d := p - floor(log(m))-1;
- m := m + fact * 273.15 + fact * 186.52 * ord( b );
- p := d + floor(log(m)) + 1
- END;
-
-
- {*************** Find u in the table of units *******************************}
-
- FUNCTION inlist: boolean;
- VAR t: strng;
-
- FUNCTION match ( s: strng ): boolean;
- BEGIN
- match := ((u=s) OR (t=s))
- END;
-
- BEGIN
- { start scan with left list }
- leftside := true;
- current := 1;
- t := u;
- IF length(t) > 1 THEN
- delete(t,1,1);
- WHILE (NOT(match(table[current].left))) and (current<=curtop) DO
- current := current + 1;
- IF current<=curtop THEN
- inlist := true
- ELSE BEGIN
- { scan the right list starting at the top working towards the bottom }
- current := curtop;
- leftside := false;
- WHILE (NOT(match(table[current].right))) and (current>0) DO
- current := current - 1;
- inlist := (current>0)
- END
- END{inlist};
-
-
- {*************** Add correct metric prefix **********************************}
-
- PROCEDURE prefix ( m: real; u: strng );
-
- PROCEDURE pref ( a: strng; fac,term: integer );
- VAR i,range: integer;
- BEGIN
- range := floor ( log(m) / fac );
- IF abs(range) > term THEN BEGIN
- {*** range := term * ( 1 - (2 * ord( (range<(-term)) )) ); ***}
- range := term * ( 1 - (2 * ord( (range<=term) )) );
- END;
- m := m / power ( 10,(fac*range) );
- IF range<>0 THEN BEGIN
- a := a[range+term+1];
- concat ( u,a,u );{ u := concat(a,u); }
- writeln(f(m),u )
- END
- END{pref};
-
- BEGIN{prefix}
- IF pos('2',u)=2 THEN
- pref(specp,2,3)
- ELSE IF pos('3',u)=2 THEN
- pref(specp,3,3)
- ELSE
- pref(normp,3,6)
- END{prefix};
-
-
- {******************** Convert to primary units *******************************}
-
- PROCEDURE primary;
- VAR oldp: integer;
- BEGIN
- WITH table[current] DO BEGIN
- IF u='mpg' THEN
- m := 1.0 / m;
- IF length(u)=2 THEN
- IF (u[1]='$') AND (u[2] IN ['F','C']) THEN
- temperature(m,(u[2]='F'),1);
- oldm := m;
- oldf := factor;
- oldp := p;
- p := p + ord( norm(m) * norm(factor) >= 10.0 );
- u := right;
- m := m * factor;
- writeln ( f(m),u );
- prefix(m,u);
- p := oldp;
- leftside := false
- END
- END{primary};
-
-
- {*************** check metric prefix and adjust if necessary *****************}
-
- PROCEDURE normalize ( VAR m: real; VAR u: strng );
- VAR s: strng;
-
- PROCEDURE depref ( a: strng; fac,term: integer );
- VAR range,k : integer;
- needspref: boolean;
- BEGIN
- needspref := ( floor( log(m)/fac )<>0 );
- IF pos(s,u)=2 THEN BEGIN
- range := term+1;
- FOR k:=1 TO length(a) DO BEGIN
- IF u[1]=a[k] THEN
- range := k-term-1
- END;
- k := range+term+1;
- IF (k>=1) AND (k<=(term*2+1)) THEN BEGIN
- m := m * power ( 10,fac*range );
- delete(u,1,1);
- writeln( f(m),u )
- END
- ELSE
- writeln('illegal prefix ignored')
- END;
- IF needspref THEN
- prefix(m,u)
- END{depref};
-
- BEGIN{normalize}
- WITH table[current] DO BEGIN
- IF leftside
- THEN s := left
- ELSE s := right
- END;
- IF pos('2',s) = 2 THEN
- depref(specp,2,3)
- ELSE IF pos('3',s)=2 THEN
- depref(specp,3,3)
- ELSE
- depref(normp,3,6)
- END{normalize};
-
-
- {*************** Convert to customary or secondary units *********************}
-
- PROCEDURE custandsec ( m: real );
- VAR oldp: integer;
- BEGIN
- WITH table[current] DO BEGIN
- oldp := p;
- p := p + ord( norm(oldm) * norm(oldf/factor) >= 10.0 );
- m := m / factor;
- IF (u='m3/m') AND (current<=maxcust) THEN
- m := 1.0 / m;
- IF u='K' THEN
- temperature ( m, (left[2]='F'), -1 );
- writeln ( f(m), left );
- IF current > maxcust THEN
- prefix ( m, left );
- p := oldp
- END
- END{custandsec};
-
-
- {********** Pascal/Z compiler options **********}
- {$F-}{ floating point error checking OFF }
- {$R-}{ range checking OFF }
- {********** Pascal/Z compiler options **********}
-
-
- {*********************** Set up the table ***********************************}
-
- PROCEDURE initialize;
-
- PROCEDURE data ( L,R: strng; f: real );
- BEGIN
- curtop := curtop+1;
- WITH table[curtop] DO BEGIN
- left := L;
- right := R;
- factor := f;
- END
- END;
-
- BEGIN{initialize}
- bell := chr(7);
- WITH table[0] DO BEGIN
- left := 'bottom';
- right := 'bottom';
- factor := 0.0
- END;
-
- curtop := 0;
-
- { CUSTOMARY UNITS/PRIMARY UNITS/CONVERSION FACTOR }
- data ( '$F', 'K', 5.5556e-1 );
- data ( 'mpg', 'm3/m', 2.352e-6 );
- data ( 'horsepower', 'W', 7.355e2 );
- data ( 'inch of mercury', 'Pa', 3.37685e3 );
- data ( 'mph', 'm/s', 4.4704e-1 );
- data ( 'yard', 'm', 9.144e-1 );
- data ( 'yard2', 'm2', 8.361274e-1 );
- data ( 'acre', 'm2', 4047.0 );
- data ( 'barrel', 'm3', 0.159 );
- data ( 'kCal', 'J', 4.1868e3 );
- data ( 'BTU', 'J', 1055.0 );
- data ( 'Curie', 'Bq', 3.7e10 );
-
- maxcust := curtop;
-
- { SECONDARY UNITS/PRIMARY UNITS/CONVERSION FACTOR }
- data ( 'L', 'm3', 1.0e-3 );
- data ( 'N/m2', 'Pa', 1.0 );
- data ('L/100 km', 'm3/m', 1.0e-8 );
- data ( 'm/h', 'm/s', 2.777e-4 );
- data ('kW-h', 'J', 3.6e6 );
- data ('$C', 'K', 1.0 );
- data ('N*m', 'J', 1.0 );
-
- data ( 'top','top', 0.0 ); { strings left & right must be initted }
-
- top := curtop
- END{initialize};
-
-
- {*************** Main subprograms *******************************************}
-
- PROCEDURE give_help;
- { WRITTEN BY: Raymond E. Penley }
- { DATE WRITTEN: Dec 15, 1982 }
- BEGIN
- writeln;
- writeln(
- ' The program distinguishes rigorously between customary units,');
- writeln(
- 'primary metric units, and secondary metric units. By "primary" metric');
- writeln(
- 'units is meant the System International (SI) base units such as metre,');
- writeln(
- 'kilogram, kelvin, and so on, as well as the derived units such as');
- writeln(
- 'watt, newton, m/s, pascal, and volt. By "secondary" metric units is');
- writeln(
- 'meant units like degree C and km/h which are accepted but not part of');
- writeln(
- 'SI, along with the formulas for derived units with special names, eg.');
- writeln(
- 'kg m/s as the formulas for the newton.');
- writeln;
- writeln(
- ' Primary units may be converted to customary by using the');
- writeln(
- '"c" command, while the "s" command converts them to secondary units.');
- writeln;
- writeln('Enter commands like:');
- writeln('Measure and unit>>5700 kJ');
- writeln(' 5.70000E+06 J');
- writeln(' 5.7 MJ');
- writeln;
- writeln('Measure and unit>>secondary');
- writeln(' 5.70000E+06 N*m');
- writeln(' 5.7 MN*m');
- writeln;
- END{give_help};
-
-
- PROCEDURE commands;
- VAR i: integer;
- BEGIN
- CASE line[1] OF
- 'F','f':
- finished := true;
-
- 'S','s':
- IF (inlist) AND (current>maxcust) AND (NOT leftside) THEN
- custandsec(m);
-
- 'H','h':
- give_help;
-
- 'C','c':
- BEGIN
- curtop := maxcust;
- IF inlist THEN
- custandsec ( m );
- curtop := top
- END;
-
- 'L','l':
- BEGIN
- writeln( 'CUSTOMARY UNITS PRIMARY UNITS CONVERSION FACTOR' );
- FOR i:=1 TO maxcust DO BEGIN
- WITH table[i] DO
- writeln ( left:15, ' ',right:15,' ',factor:15)
- END;
- writeln;
- writeln( 'SECONDARY UNITS PRIMARY UNITS CONVERSION FACTOR' );
- FOR i:=maxcust+1 TO top-1 DO BEGIN
- WITH table[i] DO
- writeln ( left:15, ' ',right:15,' ',factor:15)
- END;
- END
- ELSE:
- BEGIN
- writeln;
- writeln( CmdLine )
- END
- END{case};
- writeln
- END{commands};
-
-
- PROCEDURE process;
- BEGIN
- m := value ( line,p );
- u := line;
- oldf := 1.0;
- IF NOT inlist THEN
- writeln(bell, 'unit not available')
- ELSE BEGIN
- IF (current > maxcust) OR (NOT leftside) THEN
- normalize ( m,u );
- IF leftside THEN
- primary
- END;
- writeln
- END{process};
-
-
- BEGIN { SuperMetrics }
- finished := false;
- initialize;
- writeln(' ':22, 'SUPERMETRIC CONVERSION PROGRAM');
- writeln; writeln;
- writeln; writeln;
- writeln ( CmdLine );
- REPEAT
- writeln;
- write('Measure and unit >>');
- readln(line);
- IF (line[1] IN ['0'..'9','+','-']) THEN
- process
- ELSE
- commands
- UNTIL finished
- END.
-