From 9c95bc1c1ca13c4de56fd983391d5068f2c004b0 Mon Sep 17 00:00:00 2001 From: David Malone Date: Sat, 16 Aug 2008 16:27:41 +0000 Subject: [PATCH] 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) --- usr.bin/units/units.c | 75 +++++++++++++++++++++++++++++++---------- usr.bin/units/units.lib | 10 +++--- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/usr.bin/units/units.c b/usr.bin/units/units.c index 9ca5cda7f81..e700c94a031 100644 --- a/usr.bin/units/units.c +++ b/usr.bin/units/units.c @@ -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); } diff --git a/usr.bin/units/units.lib b/usr.bin/units/units.lib index d7c0796e81f..67127bcb9e3 100644 --- a/usr.bin/units/units.lib +++ b/usr.bin/units/units.lib @@ -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