Add limited support for units that are related by affine rather

than linear relations. We can now convert degC to degF.

586 units, 56 prefixes
You have: 24 degC
You want: degF
	75.2
You have: degC
You want: K
	 (-> x*1 +273.15)
	 (<- y*1 -273.15)
This commit is contained in:
David Malone 2008-08-16 16:27:41 +00:00
parent f70f5c2077
commit 9c95bc1c1c
2 changed files with 63 additions and 22 deletions

View File

@ -53,6 +53,8 @@ struct unittype {
char *numerator[MAXSUBUNITS];
char *denominator[MAXSUBUNITS];
double factor;
double offset;
int quantity;
};
struct {
@ -78,7 +80,7 @@ void initializeunit(struct unittype * theunit);
int addsubunit(char *product[], char *toadd);
void showunit(struct unittype * theunit);
void zeroerror(void);
int addunit(struct unittype * theunit, char *toadd, int flip);
int addunit(struct unittype *theunit, char *toadd, int flip, int quantity);
int compare(const void *item1, const void *item2);
void sortunit(struct unittype * theunit);
void cancelunit(struct unittype * theunit);
@ -207,8 +209,10 @@ readunits(const char *userfile)
void
initializeunit(struct unittype * theunit)
{
theunit->factor = 1.0;
theunit->numerator[0] = theunit->denominator[0] = NULL;
theunit->factor = 1.0;
theunit->offset = 0.0;
theunit->quantity = 0;
}
@ -237,6 +241,8 @@ showunit(struct unittype * theunit)
int counter = 1;
printf("\t%.8g", theunit->factor);
if (theunit->offset)
printf("&%.8g", theunit->offset);
for (ptr = theunit->numerator; *ptr; ptr++) {
if (ptr > theunit->numerator && **ptr &&
!strcmp(*ptr, *(ptr - 1)))
@ -284,16 +290,17 @@ zeroerror(void)
/*
Adds the specified string to the unit.
Flip is 0 for adding normally, 1 for adding reciprocal.
Quantity is 1 if this is a quantity to be converted rather than a pure unit.
Returns 0 for successful addition, nonzero on error.
*/
int
addunit(struct unittype * theunit, char *toadd, int flip)
addunit(struct unittype * theunit, char *toadd, int flip, int quantity)
{
char *scratch, *savescr;
char *item;
char *divider, *slash;
char *divider, *slash, *offset;
int doingtop;
if (!strlen(toadd))
@ -313,7 +320,17 @@ addunit(struct unittype * theunit, char *toadd, int flip)
item = strtok(scratch, " *\t\n/");
while (item) {
if (strchr("0123456789.", *item)) { /* item is a number */
double num;
double num, offsetnum;
if (quantity)
theunit->quantity = 1;
offset = strchr(item, '&');
if (offset) {
*offset = 0;
offsetnum = atof(offset+1);
} else
offsetnum = 0.0;
divider = strchr(item, '|');
if (divider) {
@ -323,19 +340,25 @@ addunit(struct unittype * theunit, char *toadd, int flip)
zeroerror();
return 1;
}
if (doingtop ^ flip)
if (doingtop ^ flip) {
theunit->factor *= num;
else
theunit->offset *= num;
} else {
theunit->factor /= num;
theunit->offset /= num;
}
num = atof(divider + 1);
if (!num) {
zeroerror();
return 1;
}
if (doingtop ^ flip)
if (doingtop ^ flip) {
theunit->factor /= num;
else
theunit->offset /= num;
} else {
theunit->factor *= num;
theunit->offset *= num;
}
}
else {
num = atof(item);
@ -343,12 +366,16 @@ addunit(struct unittype * theunit, char *toadd, int flip)
zeroerror();
return 1;
}
if (doingtop ^ flip)
if (doingtop ^ flip) {
theunit->factor *= num;
else
theunit->offset *= num;
} else {
theunit->factor /= num;
theunit->offset /= num;
}
}
if (doingtop ^ flip)
theunit->offset += offsetnum;
}
else { /* item is not a number */
int repeat = 1;
@ -534,7 +561,7 @@ reduceproduct(struct unittype * theunit, int flip)
free(*product);
*product = NULLUNIT;
}
if (addunit(theunit, toadd, flip))
if (addunit(theunit, toadd, flip, 0))
return ERROR;
}
}
@ -613,6 +640,20 @@ showanswer(struct unittype * have, struct unittype * want)
showunit(have);
showunit(want);
}
else if (have->offset != want->offset) {
if (want->quantity)
printf("WARNING: conversion of non-proportional quantities.\n");
printf("\t");
if (have->quantity)
printf("%.8g\n",
(have->factor + have->offset-want->offset)/want->factor);
else
printf(" (-> x*%.8g %+.8g)\n\t (<- y*%.8g %+.8g)\n",
have->factor / want->factor,
(have->offset-want->offset)/want->factor,
want->factor / have->factor,
(want->offset - have->offset)/have->factor);
}
else
printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor,
want->factor / have->factor);
@ -666,10 +707,10 @@ main(int argc, char **argv)
strlcpy(havestr, argv[optind], sizeof(havestr));
strlcpy(wantstr, argv[optind + 1], sizeof(wantstr));
initializeunit(&have);
addunit(&have, havestr, 0);
addunit(&have, havestr, 0, 1);
completereduce(&have);
initializeunit(&want);
addunit(&want, wantstr, 0);
addunit(&want, wantstr, 0, 1);
completereduce(&want);
showanswer(&have, &want);
}
@ -687,7 +728,7 @@ main(int argc, char **argv)
putchar('\n');
exit(0);
}
} while (addunit(&have, havestr, 0) ||
} while (addunit(&have, havestr, 0, 1) ||
completereduce(&have));
do {
initializeunit(&want);
@ -698,7 +739,7 @@ main(int argc, char **argv)
putchar('\n');
exit(0);
}
} while (addunit(&want, wantstr, 0) ||
} while (addunit(&want, wantstr, 0, 1) ||
completereduce(&want));
showanswer(&have, &want);
}

View File

@ -671,14 +671,14 @@ Xunit 1.00206e-13 m
k 1.38047e-16 erg/degC
degC K
degC 1&+273.15 K
kelvin K
brewster 1e-12 m2/newton
degF 5|9 degC
degreesrankine degF
degF 5|9&255.37222222222222222222 K
degreesrankine 5|9 K
degrankine degreesrankine
degreerankine degF
degreaumur 10|8 degC
degreerankine degreesrankine
degreaumur 10|8&+273.15 K
drachm 60 grain
poncelet 100 kg m g / sec
denier .05|450 gram / m