123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- MODULE RealStr;
- (*
- * Purpose:
- * Provides REAL/string conversions
- *
- * Log:
- * April 96 jl initial version
- *
- * Notes:
- * Complies with ISO/IEC 10514-1:1996 (as RealStr)
- *
- * Modified for Component Pascal by kjg, February 2004
- *
- *)
- IMPORT RTS;
- (***************************************************************)
- (* *)
- (* PRIVATE - NOT EXPORTED *)
- (* *)
- (***************************************************************)
- CONST
- err = 9999;
- TYPE
- CharPtr = POINTER TO ARRAY OF CHAR;
- DigArray = ARRAY 28 OF CHAR;
- (*===============================================================*)
- PROCEDURE Message(OUT str : ARRAY OF CHAR; IN mss : ARRAY OF CHAR);
- VAR idx : INTEGER;
- BEGIN
- idx := 0;
- WHILE (idx < LEN(str)) & (idx < LEN(mss)) DO
- str[idx] := mss[idx]; INC(idx);
- END;
- IF idx < LEN(str) THEN str[idx] := 0X END;
- END Message;
- (*===============================================================*)
- PROCEDURE expLen(exp : INTEGER) : INTEGER;
- BEGIN
- exp := ABS(exp);
- IF exp < 10 THEN RETURN 3;
- ELSIF exp < 100 THEN RETURN 4;
- ELSE RETURN 5;
- END;
- END expLen;
- (*===============================================================*)
- PROCEDURE CopyCh(ch : CHAR;
- VAR ix : INTEGER;
- VAR st : ARRAY OF CHAR);
- BEGIN
- IF ix < LEN(st) THEN st[ix] := ch; INC(ix) END;
- END CopyCh;
- (*===============================================================*)
- PROCEDURE CopyExp(ex : INTEGER;
- VAR ix : INTEGER;
- VAR st : ARRAY OF CHAR);
- VAR abX, val, len, idx, dHi : INTEGER;
- BEGIN
- dHi := LEN(st) - 1;
- len := expLen(ex);
- IF ix + len > dHi THEN ix := dHi - len END;
- IF ix < 2 THEN
- FOR idx := 0 TO MIN(ix+len, dHi-1) DO st[idx] := "*"; ix := idx+1 END;
- ELSE
- CopyCh("E",ix,st);
- IF ex > 0 THEN CopyCh("+",ix,st) ELSE CopyCh("-",ix,st) END;
- abX := ABS(ex); val := abX;
- IF abX >= 100 THEN
- CopyCh(CHR(val DIV 100 + ORD("0")),ix,st);
- val := val MOD 100;
- END;
- IF abX >= 10 THEN
- CopyCh(CHR(val DIV 10 + ORD("0")),ix,st);
- END;
- CopyCh(CHR(val MOD 10 + ORD("0")),ix,st);
- END;
- END CopyExp;
- (*===============================================================*)
- PROCEDURE GetDigits(real : REAL;
- OUT digits : DigArray;
- OUT dPoint : INTEGER;
- OUT isNeg : BOOLEAN);
- VAR rIdx : INTEGER; (* the read index *)
- wIdx : INTEGER; (* the write index *)
- iLen : INTEGER; (* integer part len *)
- eVal : INTEGER; (* exponent value *)
- buff : DigArray; (* temporary buffer *)
- eNeg : BOOLEAN; (* exponent is neg. *)
- rChr : CHAR; (* last read char *)
- BEGIN
- (*
- * We want to assert that digit[0] # "0",
- * unless real = zero. So to avoid a sack o'woe
- *)
- IF real = 0.0 THEN
- digits := "0";
- dPoint := 1;
- isNeg := FALSE; RETURN; (* PREEMPTIVE RETURN HERE *)
- END;
- RTS.RealToStrInvar(real, buff);
- rIdx := 0;
- wIdx := 0;
- eVal := 0;
- (* get optional sign *)
- isNeg := (buff[0] = "-");
- IF isNeg THEN INC(rIdx) END;
-
- rChr := buff[rIdx]; INC(rIdx);
- WHILE rChr = "0" DO
- rChr := buff[rIdx]; INC(rIdx);
- END;
- (* get integer part *)
- WHILE (rChr <= "9") & (rChr >= "0") DO
- digits[wIdx] := rChr; INC(wIdx);
- rChr := buff[rIdx]; INC(rIdx);
- END;
- iLen := wIdx; (* integer part ended *)
- IF rChr = "." THEN (* get fractional part *)
- rChr := buff[rIdx]; INC(rIdx);
- IF wIdx = 0 THEN
- (* count any leading zeros *)
- WHILE rChr = "0" DO
- rChr := buff[rIdx]; INC(rIdx); DEC(iLen);
- END;
- END;
- WHILE (rChr <= "9") & (rChr >= "0") DO
- digits[wIdx] := rChr; INC(wIdx);
- rChr := buff[rIdx]; INC(rIdx);
- END;
- END;
- digits[wIdx] := 0X; (* terminate char arr. *)
- IF (rChr = "E") OR (rChr = "e") THEN
- (* get fractional part *)
- rChr := buff[rIdx]; INC(rIdx);
- IF rChr = "-" THEN
- eNeg := TRUE;
- rChr := buff[rIdx]; INC(rIdx);
- ELSE
- eNeg := FALSE;
- IF rChr = "+" THEN rChr := buff[rIdx]; INC(rIdx) END;
- END;
- WHILE (rChr <= "9") & (rChr >= "0") DO
- eVal := eVal * 10;
- INC(eVal, (ORD(rChr) - ORD("0")));
- rChr := buff[rIdx]; INC(rIdx);
- END;
- IF eNeg THEN eVal := -eVal END;
- END;
- (* At this point, if we are not ended, we have a NaN *)
- IF rChr # 0X THEN
- digits := buff; dPoint := err;
- ELSE
- (* Index of virtual decimal point is eVal + iLen *)
- DEC(eVal);
- dPoint := iLen + eVal;
- END;
- END GetDigits;
- (***************************************************************)
- PROCEDURE RoundRelative(VAR str : DigArray;
- VAR exp : INTEGER;
- num : INTEGER);
- VAR len : INTEGER;
- idx : INTEGER;
- chr : CHAR;
- BEGIN
- len := LEN(str$); (* we want num+1 digits *)
- IF num < 0 THEN
- str[0] := 0X;
- ELSIF num = 0 THEN
- chr := str[0];
- IF chr > "4" THEN
- str := "1"; INC(exp);
- ELSE
- str[num] := 0X;
- END;
- ELSIF num < len THEN
- chr := str[num];
- IF chr > "4" THEN (* round up str[num-1] *)
- idx := num-1;
- LOOP
- str[idx] := CHR(ORD(str[idx]) + 1);
- IF str[idx] <= "9" THEN EXIT;
- ELSE
- str[idx] := "0"; (* and propagate *)
- IF idx = 0 THEN (* need a shift *)
- FOR idx := num TO 0 BY -1 DO str[idx+1] := str[idx] END;
- str[0] := "1"; INC(exp); EXIT;
- END;
- END;
- DEC(idx);
- END;
- END;
- str[num] := 0X;
- END;
- END RoundRelative;
- (***************************************************************)
- (* *)
- (* PUBLIC - EXPORTED *)
- (* *)
- (***************************************************************)
- (*===============================================================*
- *
- * Ignores any leading spaces in str. If the subsequent characters in str
- * are in the format of a signed real number, assigns a corresponding value
- * to real. Assigns a value indicating the format of str to res.
- *)
- PROCEDURE StrToReal*(str : ARRAY OF CHAR;
- OUT real : REAL;
- OUT res : BOOLEAN);
- VAR clrStr : RTS.NativeString;
- BEGIN
- clrStr := MKSTR(str);
- RTS.StrToRealInvar(clrStr, real, res);
- END StrToReal;
- (*===============================================================*
- *
- * Converts the value of real to floating-point string form, with sigFigs
- * significant digits, and copies the possibly truncated result to str.
- *)
- PROCEDURE RealToFloat*(real : REAL;
- sigFigs : INTEGER;
- OUT str : ARRAY OF CHAR);
- VAR len, fWid, index, ix : INTEGER;
- dExp : INTEGER; (* decimal exponent *)
- neg : BOOLEAN;
- digits : DigArray;
- BEGIN
- GetDigits(real, digits, dExp, neg);
- IF dExp = err THEN Message(str, digits); RETURN END;
- RoundRelative(digits, dExp, sigFigs);
- index := 0;
- IF neg THEN CopyCh("-", index, str) END;
- fWid := LEN(digits$);
- IF fWid = 0 THEN (* result is 0 *)
- CopyCh("0", index, str);
- dExp := 0;
- ELSE
- CopyCh(digits[0], index, str);
- END;
- IF sigFigs > 1 THEN
- CopyCh(".",index,str);
- IF fWid > 1 THEN
- FOR ix := 1 TO fWid - 1 DO CopyCh(digits[ix], index, str) END;
- END;
- FOR ix := fWid TO sigFigs - 1 DO CopyCh("0", index, str) END;
- END;
- (*
- * IF dExp # 0 THEN CopyExp(dExp,index,str) END;
- *)
- CopyExp(dExp,index,str);
- IF index <= LEN(str)-1 THEN str[index] := 0X END;
- END RealToFloat;
- (*===============================================================*
- *
- * Converts the value of real to floating-point string form, with sigFigs
- * significant digits, and copies the possibly truncated result to str.
- * The number is scaled with one to three digits in the whole number part and
- * with an exponent that is a multiple of three.
- *)
- PROCEDURE RealToEng*(real : REAL;
- sigFigs : INTEGER;
- OUT str : ARRAY OF CHAR);
- VAR len, index, ix : INTEGER;
- dExp : INTEGER; (* decimal exponent *)
- fact : INTEGER;
- neg : BOOLEAN;
- digits : DigArray;
- BEGIN
- GetDigits(real, digits, dExp, neg);
- IF dExp = err THEN Message(str, digits); RETURN END;
- RoundRelative(digits, dExp, sigFigs);
- len := LEN(digits$); INC(dExp);
- IF len = 0 THEN dExp := 1 END; (* result = 0 *)
- fact := ((dExp - 1) MOD 3) + 1;
- DEC(dExp,fact); (* make exponent multiple of three *)
- index := 0;
- IF neg THEN CopyCh("-",index,str) END;
- IF fact <= len THEN
- FOR ix := 0 TO fact - 1 DO CopyCh(digits[ix],index,str) END;
- ELSE
- IF len > 0 THEN
- FOR ix := 0 TO len - 1 DO CopyCh(digits[ix],index,str) END;
- END;
- FOR ix := len TO fact - 1 DO CopyCh("0",index,str) END;
- END;
- IF fact < sigFigs THEN
- CopyCh(".",index,str);
- IF fact < len THEN
- FOR ix := fact TO len - 1 DO CopyCh(digits[ix],index,str) END;
- ELSE
- len := fact;
- END;
- FOR ix := len TO sigFigs - 1 DO CopyCh("0",index,str) END;
- END;
- (*
- * IF dExp # 0 THEN CopyExp(dExp,index,str) END;
- *)
- CopyExp(dExp,index,str);
- IF index <= LEN(str)-1 THEN str[index] := 0X END;
- END RealToEng;
- (*===============================================================*
- *
- * Converts the value of real to fixed-point string form, rounded to the
- * given place relative to the decimal point, and copies the result to str.
- *)
- PROCEDURE RealToFixed*(real : REAL;
- place : INTEGER; (* requested no of frac. places *)
- OUT str : ARRAY OF CHAR);
- VAR lWid : INTEGER; (* Leading digit-str width *)
- fWid : INTEGER; (* Width of fractional part *)
- tWid : INTEGER; (* Total width of str-rep. *)
- zWid : INTEGER; (* Leading zeros in frac. *)
- len : INTEGER; (* Significant digit length *)
- dExp : INTEGER; (* Pos. of rad. in dig-arr. *)
- dLen : INTEGER; (* Length of dest. array *)
- index : INTEGER;
- ix : INTEGER;
- neg : BOOLEAN;
- radix : BOOLEAN;
- digits : DigArray;
- BEGIN
- (* the decimal point and fraction part *)
- (* ["-"] "0" "." d^(fWid) -- if dExp < 0 *)
- (* ["-"] d^(lWid) "." d^(fWid) -- if fWid > 0 *)
- (* ["-"] d^(lWid) -- if fWid = 0 *)
-
- tWid := 0;
- dLen := LEN(str);
- IF place >= 0 THEN fWid := place ELSE fWid := 0 END;
- radix := (fWid > 0);
- GetDigits(real, digits, dExp, neg);
- IF dExp = err THEN Message(str, digits); RETURN END;
- RoundRelative(digits, dExp, place+dExp+1); (* this can change dExp! *)
- (* Semantics of dExp value *)
- (* 012345 ... digit index *)
- (* dddddd ... digit content *)
- (* ^-------- dExp value *)
- (* "ddd.ddd..." result str. *)
- len := LEN(digits$);
- IF len = 0 THEN neg := FALSE END; (* don't print "-0" *)
- IF dExp >= 0 THEN lWid := dExp+1 ELSE lWid := 1 END;
- IF neg THEN INC(tWid) END;
- IF radix THEN INC(tWid) END;
- INC(tWid, lWid);
- INC(tWid, fWid);
- IF tWid > dLen THEN tWid := dLen END;
- index := 0;
- (*
- * Now copy the optional signe
- *)
- IF neg THEN CopyCh("-",index,str) END;
- (*
- * Now copy the integer part
- *)
- IF dExp < 0 THEN
- CopyCh("0",index,str);
- ELSE
- IF lWid <= len THEN
- FOR ix := 0 TO lWid - 1 DO CopyCh(digits[ix],index,str) END;
- ELSE
- IF len > 0 THEN
- FOR ix := 0 TO len - 1 DO CopyCh(digits[ix],index,str) END;
- END;
- FOR ix := len TO lWid - 1 DO CopyCh("0",index,str) END;
- END;
- END;
- (*
- * Now copy the fractional part
- *)
- IF radix THEN
- CopyCh(".",index,str);
- IF dExp < 0 THEN
- (* 012345 ... digit idx *)
- (* dddddd ... digit str. *)
- (* ^-------- dExp = -1 *)
- zWid := MIN(-dExp-1, fWid); (* leading zero width *)
- FOR ix := 0 TO zWid - 1 DO CopyCh("0",index,str) END;
- FOR ix := 0 TO len - 1 DO CopyCh(digits[ix],index,str) END;
- ELSIF lWid < len THEN
- FOR ix := lWid TO len - 1 DO CopyCh(digits[ix],index,str) END;
- END;
- WHILE index < tWid DO CopyCh("0",index,str) END;
- END;
- IF index <= dLen-1 THEN str[index] := 0X END;
- END RealToFixed;
- (*===============================================================*
- *
- * Converts the value of real as RealToFixed if the sign and magnitude can be
- * shown within the capacity of str, or otherwise as RealToFloat, and copies
- * the possibly truncated result to str.
- * The number of places or significant digits are implementation-defined.
- *)
- PROCEDURE RealToStr*(real: REAL; OUT str: ARRAY OF CHAR);
- BEGIN
- RTS.RealToStrInvar(real, str);
- RESCUE (x);
- RealToFloat(real, 16, str);
- END RealToStr;
- (* ---------------------------------------- *)
- END RealStr.
|