// floating.c // // This file supports the "real" type and decimal-to-floating-point conversion. // // by John D. de Boer #include "common.h" #include "cstring.h" #include "cyber.h" #include "disk.h" #include "floating.h" // Floating-point utility functions real modtwopi(real X) { X=fmod(X,twopi); if (X<0.0) X+=twopi; return X; } real modtwopisym(real X) { X=fmod(X,twopi); if (X<-pi) X+=twopi; if (X>pi) X-=twopi; return X; } real fgtr(real X, real Lower) { return XUpper ? Upper : X; } real fbetween(real X, real Lower, real Upper) { if (XUpper) return Upper; return X; } static bool decimalmantissa(real *R, short *EXP, bool *Neg) { int C,E; real X; if (!R || isnan(X=*R)) return 0; if (Neg) *Neg=0; if (X<0.0) { X=-X; if (Neg) *Neg=1; } if (EXP) *EXP=0; if (X==0.0) return 1; E=0; for (C=0;C<256 && X < 0.1;C++) { E--; X*=10.0; } for (C=0;C<256 && X >= 1.0;C++) { E++; X/=10.0; } if (EXP) *EXP=E; *R=X; return 1; } real rounddown(real X, bool Two, bool Five) { short E; bool Neg; if (!decimalmantissa(&X,&E,&Neg)) return 0.0; if (X==0.0) return 0.0; X*=10.0; E--; if (Five && X>=5.0) X=5.0; else if (Two && X>=2.0) X=2.0; else X=1.0; for (;E>0;E--) X*=10.0; for (;E<0;E++) X/=10.0; return Neg ? -X : X; } // Floating-point string functions // decimalstring() finds the decimal expression of a real number Frac in the range [0,1), // and puts it in *Buf. Buf must be at least PLACES + 1 in length, to hold a terminating // nul. *Carry is set to 1 if Frac rounds to unity, in which case Buf contains only '0' // characters. *Carry gets 0 otherwise. The return value indicates success. static bool decimalstring(char *Buf, real Frac, int PLACES, bool *Carry) { int INT; char *P,*End; if (!Buf || PLACES<1) return 0; if (Carry) *Carry=0; End= Buf + PLACES; if (isnan(Frac) || Frac<0.0 || Frac>=1.0) { for (P=Buf;PBuf;P--) if (*P>'9') { *P='0'; P[-1]++; } else return 1; if (*Buf>'9') { *Buf='0'; if (Carry) *Carry=1; } return 1; } static string zerostring(int PLACES) { char *S; S=fillstring(PLACES + 1,'0'); S[1]='.'; return S; } string floatstring(real Num, int PLACES, short *EXP) { char Dec[32]; short E; bool Carry,Neg; if (isnan(Num)) { if (EXP) *EXP=0; return copyof("NaN"); } PLACES=between(PLACES,2,24); if (!Num) { if (EXP) *EXP=0; return zerostring(PLACES); } decimalmantissa(&Num,&E,&Neg); if (EXP) E+=*EXP; if (!decimalstring(Dec + 2,Num,PLACES,&Carry)) return copyof("ERROR"); if (Carry) Dec[1]='1'; else { Dec[1]=Dec[2]; E--; } Dec[2]='.'; if (EXP) *EXP=E; if (Neg) { Dec[0]='-'; return leftstr(Dec,PLACES + 2); } return leftstr(Dec + 1,PLACES + 1); } string engineeringrep(real Num, int PLACES, short *EXP) { char Dec[32]; short i,E; long L; int OrigPLACES,LEFT; bool Carry,Neg; if (isnan(Num)) { if (EXP) *EXP=0; return copyof("NaN"); } PLACES=between(PLACES,2,24); if (!Num) { if (EXP) *EXP=0; return zerostring(PLACES); } decimalmantissa(&Num,&E,&Neg); if (EXP) E+=*EXP; LEFT= E % 3; if (LEFT<1) LEFT+=3; E-=LEFT; for (i=0;i999) return copyof("EngRepErr#2"); if (EXP) *EXP=E; if (Neg) return cat4(2,"-",fig(L),".",Dec); return cat3(1,fig(L),".",Dec); } string fixedstring(real Num, int LEFT, int RIGHT) { long L; bool Neg,Carry; char Dec[16],*S,*N; LEFT=between(LEFT,1,8); RIGHT=between(RIGHT,1,8); if (isnan(Num)) return cat3(5,fillstring(LEFT - 1,' '),"NaN",fillstring(RIGHT - 1,' ')); Neg=(Num<0.0); if (Neg) Num=-Num; L=(long)Num; Num-=L; decimalstring(Dec + 1,Num,RIGHT,&Carry); if (Carry) L++; Dec[0]='.'; S=cat(1,intstring(L,LEFT,'R'),Dec); if (!Neg) return S; if (S[0]!=' ') return cat(2,"-",S); N=S; while (N[1]==' ') N++; *N='-'; return S; } bool parsereal(const char *S, real *X) { bool Neg; long INT; char C; real Dec,P; if (!S || !X) return 0; Neg=(*S=='-'); if (Neg || *S=='+') S++; if (numeral(*S)) { parseinteger(S,&INT); while (numeral(*S)) S++; if (*S=='.') S++; } else if (*S=='.') { INT=0; S++; } else return 0; Dec=0.0; P=1.0; while (numeral(C=*S)) { P/=10.0; Dec+= P * (C - '0'); S++; } Dec+=(real)INT; if (Neg) Dec=-Dec; while (*S==' ') S++; if (*S!='e' && *S!='E') { *X=Dec; return 1;} S++; while (*S==' ') S++; if (!parseinteger(S,&INT)) return 0; for (;INT>0;INT--) Dec*=10.0; for (;INT<0;INT++) Dec/=10.0; *X=Dec; return 1; } bool modalreal(const char *Msg, char **S, real *X) { bool R; if (!S) return 0; R=modaltextentry(Msg,S); if (!R) return 0; R=parsereal(*S,X); return R; } // what about S if R==0 (tiny memory leak) // Decimal strings with SI units static const int SIPRIXES = 11; static const char SIPrefixes[]="afpnm kMGT"; static void adjustsiprefix(short *EXP, char *Pref) { char *P; int N; short E; if (!EXP || !Pref) return; E=*EXP; P=firstocc(*Pref,SIPrefixes); if (!P) return; N= P - SIPrefixes; while (E>2 && N < SIPRIXES - 1) { E-=3; N++; } while (E<0 && N>0) { E+=3; N--; } *EXP=E; *Pref=SIPrefixes[N]; } string sifloat(real Num, int PLACES, short *EXP, char *Pref, char *Units) { char *S,U; short E; real ANum; if (isnan(Num)) { if (EXP) *EXP=0; if (Pref) *Pref=' '; return copyof("NaN"); } ANum=fabs(Num); E = EXP ? *EXP : 0; U= Units ? *Units : ' '; if (U=='' && !E && (ANum<1.0 || ANum>=1000.0)) { U='m'; E-=10; } if (Units) *Units=U; S=engineeringrep(Num,PLACES,&E); if (EXP) *EXP=E; if (U!='') adjustsiprefix(EXP,Pref); return S; } static string sistring(real X, int PREC, const char *Units) { short EXP; char Pref,U,*S,*P; EXP=0; Pref=' '; U=' '; S=sifloat(X,PREC,&EXP,&Pref,&U); if (EXP) S=cat3(5,S,"e",fig(EXP)); if (Pref==' ') P=copyof(" "); else { P=copyof(" "); P[1]=Pref; } return cat3(3,S,P,(char *)Units); } // Angle string functions bool parsedegrees(const char *S, real *Rads) { real Q; if (!S || !Rads) return 0; if (!parsereal(S,&Q)) return 0; *Rads= Q / fiftyseven; return 1; } bool parsedegminsec(const char *S, real *Rads) { long D,M; real Q; bool Neg; if (!S || !Rads) return 0; while (*S==' ') S++; Neg=(*S=='-'); if (Neg) S++; if (!parseinteger(S,&D)) return 0; nextword((char **)&S); if (!parseinteger(S,&M)) return 0; nextword((char **)&S); if (!parsereal(S,&Q)) return 0; Q/=60.0; Q+=M; Q/=60.0; Q+=D; *Rads= (Neg ? -Q : Q) / fiftyseven; return 1; } bool parsehourminsec(const char *S, real *Rads) { long H,M; real Q; if (!S || !Rads) return 0; if (!parseinteger(S,&H)) return 0; nextword((char **)&S); if (!parseinteger(S,&M)) return 0; nextword((char **)&S); if (!parsereal(S,&Q)) return 0; Q/=60.0; Q+=M; Q/=60.0; Q+=H; *Rads= Q * pi / 12.0; return 1; } string anglestring(real Angle, bool Radians) { short DEG,MIN; bool Neg; if (Radians) return sistring(Angle,3,"rad"); Neg=(Angle<0.0); if (Neg) Angle=-Angle; Angle*=fiftyseven; DEG=(short)Angle; Angle-=DEG; Angle*=60.0; MIN=(short)Angle; Angle-=MIN; Angle*=60.0; if (DEG>10) return cat(1,fig(Neg ? -DEG : DEG),""); if (DEG) return cat4(5,fig(Neg ? -DEG : DEG)," ",fig(MIN),"'"); if (MIN>10) return cat(1,fig(Neg ? -MIN : MIN),"'"); if (MIN) return cat4(5,fig(Neg ? -MIN : MIN),"' ",fig(Angle),"''"); return sistring(Neg ? -Angle : Angle,3,"sec"); } string degminstring(real Angle, bool Pos) { int DEG,MIN; bool Neg; Angle= Pos ? modtwopi(Angle) : modtwopisym(Angle); Neg=(Angle<0.0); if (Neg) Angle=-Angle; Angle*=fiftyseven; DEG=(int)Angle; Angle-=DEG; Angle*=60.0; MIN=(int)Angle; Angle-=MIN; if (Angle>0.5) MIN++; if (MIN>=60) { MIN-=60; DEG++; } return cat4(5,fig(Neg ? -DEG : DEG)," ",fig(MIN),"'"); } // Drawing real numbers /* void drawreal(real X, int PLACES) { short E=0; odrawold(engineeringrep(X,PLACES,&E)); if (E) odrawexponent(E); } void drawfixed(real X, int LEFT, int RIGHT) { odrawold(fixedstring(X,LEFT,RIGHT)); }*/ // Reading and writing real numbers void writereal(real X) { writebytes(&X,sizeof(real)); } real readreal(void) { real X; readbytes(&X,sizeof(real)); return X; }