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 + -
显示快捷键?