units.cpp
来自「eC++编译器源码」· C++ 代码 · 共 488 行 · 第 1/2 页
CPP
488 行
#pragma units
#pragma qualified
#include <String.h>
#include <Help.h>
#include <InOut.h>
#include <RealInOut.h>
#include <MathLib2.h>
#include <FInOut.h>
const unsigned int MAXNAME=20;
const unsigned int MAXTAGS=100;
const unsigned int MAXCONVERT=MAXTAGS*(MAXTAGS-1)/2;
int dm(int x, int y)
{
return (x)*((x)-1)/2+(y);
};
int sm(int x, int y)
{
if (x>=y) return (x)*((x)-1)/2+(y);
else return (y)*((y)-1)/2+(x);
};
typedef struct {
char name[MAXNAME+1]; //+1 for null
} Unit;
Unit tags[MAXTAGS];
unsigned int tagCnt;
float convert[MAXCONVERT];
float ratio(int i, int j, int k)
{
switch (int(j>k)*4+int(j>i)*2+int(i>k)) {
case 0: //k,j = k,i * i,j
case 7: //j,k = j,i * i,k
return convert[sm(i,j)]*convert[sm(i,k)];
case 1: //k,j = 1.0/i,k * i,j
case 6: //j,k = j,i * 1.0/k,i
return convert[sm(i,j)]/convert[sm(i,k)];
case 2: //k,j = k,i * 1.0/i,j
case 5: //j,k = 1.0/i,j * i,k
return convert[sm(i,k)]/convert[sm(i,j)];
case 3: //k,j = 1.0/k,i * 1.0/j,i
case 4: //j,k = 1.0/i,j * 1.0/k,i
return 1.0/(convert[sm(i,j)]*convert[sm(i,k)]);
};
};
void assert(boolean b)
{
if (!b) Help.Look();
};
//should we have preferences that tell what to convert to given op & units?
void FactTag(UNIT to, UNIT from, float oneFromTwo)
{
unsigned int i,j,k;
j = TRUNC(long(to)); k = TRUNC(long(from));
if (j==k) return;
if (j<k) {
i = j; j = k; k = i;
oneFromTwo = 1.0/oneFromTwo;
};
assert(convert[dm(j,k)] == 0.0);
convert[dm(j,k)] = oneFromTwo;
for (i=0; i<tagCnt; i=i+1) { //Warshall's transitive closure alg.
for (j=0; j<tagCnt; j=j+1) {
if (convert[sm(j,i)]!=0.0) {
for (k=0; k<tagCnt; k=k+1) {
if ((k==i)||(k==j)) continue;
if (convert[sm(i,k)]!=0.0) {
if (convert[sm(j,k)]==0.0) {
/*eureka*/ convert[sm(j,k)] = ratio(i,j,k);
};
};
};
};
};
};
};
/* Originally, I was going to use the Unit tags to do dynamic conversions
as arithmetic operators were evaluated. But now I am going to try
keeping each category in a canonical format. You still have to do the
unit canceling on arithmetic operations but there's no need for dynamic
conversions. We would add a Units number format that had to be manually
over-ridden and it would trigger the proper conversions on input and on
output.
*/
void Fact(char unitName1[], char unitName2[], float oneFromTwo)
{
int i,j,k;
if (String.Compare(unitName1, unitName2)==0) return;
j = -1; k = -1;
for (i=0; i<int(tagCnt); i=i+1) {
if ((j<0) && (String.Compare(unitName1, tags[i].name)==0)) {
j = i;
if (k>=0) break;
} else if ((k<0) && (String.Compare(unitName2, tags[i].name)==0)) {
k = i;
if (j>=0) break;
};
};
if ((j<0) || (k<0)) {
assert(tagCnt<MAXTAGS);
i = String.Length(unitName1);
assert((i!=0) && (i<MAXNAME));
i = String.Length(unitName2);
assert((i!=0) && (i<MAXNAME));
if ((j<0) && (k<0)) {
k = tagCnt;
INC(tagCnt);
String.Copy(tags[k].name, unitName2);
assert(tagCnt<MAXTAGS);
j = tagCnt;
INC(tagCnt);
String.Copy(tags[j].name, unitName1);
assert(convert[dm(j,k)] == 0.0);
convert[dm(j,k)] = oneFromTwo;
return; //skip transitive closure when both new
} else if (k<0) {
oneFromTwo = 1.0/oneFromTwo;
k = j;
j = tagCnt;
INC(tagCnt);
String.Copy(tags[j].name, unitName2);
} else {
j = tagCnt;
INC(tagCnt);
String.Copy(tags[j].name, unitName1);
};
};
FactTag(LONG(j), LONG(k), oneFromTwo);
};
UNIT Tag(char unitName[])
{
unsigned int i;
for (i=0; i<tagCnt; i=i+1)
if (String.Compare(unitName, tags[i].name)==0) return UNIT(LONG(i));
return NOUNIT;
};
int Name(UNIT tag, /*out*/char &unitName[])
{
unsigned int i;
i = TRUNC(tag);
assert((i>=0) && (i<tagCnt));
String.Copy(unitName, tags[i].name);
return 1;
};
float Convert(UNIT tagTo, UNIT tagFrom) //0.0 means none; to=from*float
{
if ((tagTo==NOUNIT) || (tagFrom==NOUNIT)) return 0.0;
if (tagTo == tagFrom) return 1.0;
if (long(tagTo) > long(tagFrom))
return convert[dm(TRUNC(long(tagTo)), TRUNC(long(tagFrom)))];
else
return 1.0/convert[dm(TRUNC(long(tagFrom)), TRUNC(long(tagTo)))];
};
float ConvertName(char unitTo[], char unitFrom[]) //0.0 means none
{
return Convert(Tag(unitTo), Tag(unitFrom));
};
/* I started by defining a byte string of types with a marker separating
the numerator from the denominator on the theory that a simple ratio
was the most complicated unit. Note that since types of units are
represented by a canonical value, we only have to worry about relatively
few(?) types (mass, time etc.). However, the next idea was to use a bit
vector, which will be a lot faster. The disadvantage is that only
relatively few exponents can be represented. Are there units**10??
Whoops, multiply was going polynomial so had to use some of the bits
to make the common case fast (disjoint types; no exps). However, now
we have a MAXINUSE loop every time so we may go to a combo bitmap/string.
Whoops, realized that units could be a single string/bitVector if
corresponding exp field is just +- (what a dope); however, we'll finish
this one first as it has advantage of having num/denom sorted for printing
purposes. However, just remembered that this would put exponents in the
string twice, once for + once for -. Whoops, just realized that a canonical
measure per type requires that output formats cover the ratio cases
otherwise there's no way of knowing that dollars/pound is preferred over
pesetas/fathom.
*/
const unsigned int MAXINUSE=32; //must be <= no of bits in the numerator
const unsigned int MAXTYPES=5; //these are reserved bits of of MAXINUSE total
const long MAXTYPEMASK=0x1fL; //(1<<MAXTYPES)-1
/* the type field for "types^1" is an application tag. For greater powers,
it is the index to the base definition.
*/
typedef struct {
long type, exponent;
} UnitTypes;
UnitTypes typesInUse[MAXINUSE];
int limits[MAXTYPES]
unsigned int inuseCnt;
long mask[32];
// Assume no deletions since then we have to worry about ref counting.
// Maybe we should byte string rep on sheet so this could be used just
// for recalcing. Even if a type has exponents, the bit in MAXTYPES is
// set.
int AddTypeExp(long type, long exponent) //returns index of entry
{
unsigned int i;
assert((type>=0L)&&(type<LONG(MAXTYPES)));
for (i=0; i<inuseCnt; i=i+1) {
if ((typesInUse[i].type==type)&&(typesInUse[i].exponent==exponent))
return i;
};
assert(inuseCnt<MAXINUSE);
typesInUse[inuseCnt].type = type;
typesInUse[inuseCnt].exponent = exponent;
INC(inuseCnt);
return inuseCnt-1;
};
int AddIndexExp(long index, long exponent) //returns index of entry
{
return AddTypeExp(typesInUse[TRUNC(index)].type, exponent);
};
long multiply(long left, long right)
{
unsigned int i,j,k;
long exps[MAXTYPES]; //this idea would work for strings too
long index[MAXTYPES];
for (i=0; i<MAXTYPES; i=i+1) {exps[i]=0L; index[i]=0L;};
if ((left&right&MAXTYPEMASK)!=0L) {
j = 1;
for (i=0; i<MAXINUSE; i=i+1) {
if ((left&LONG(j))!=0L) {
if (i>=MAXTYPES) {
k = TRUNC(typesInUse[i].type);
INC(exps[k], typesInUse[i].exponent-1L);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?