RealStr.cp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. MODULE RealStr;
  2. (*
  3. * Purpose:
  4. * Provides REAL/string conversions
  5. *
  6. * Log:
  7. * April 96 jl initial version
  8. *
  9. * Notes:
  10. * Complies with ISO/IEC 10514-1:1996 (as RealStr)
  11. *
  12. * Modified for Component Pascal by kjg, February 2004
  13. *
  14. *)
  15. IMPORT RTS;
  16. (***************************************************************)
  17. (* *)
  18. (* PRIVATE - NOT EXPORTED *)
  19. (* *)
  20. (***************************************************************)
  21. CONST
  22. err = 9999;
  23. TYPE
  24. CharPtr = POINTER TO ARRAY OF CHAR;
  25. DigArray = ARRAY 28 OF CHAR;
  26. (*===============================================================*)
  27. PROCEDURE Message(OUT str : ARRAY OF CHAR; IN mss : ARRAY OF CHAR);
  28. VAR idx : INTEGER;
  29. BEGIN
  30. idx := 0;
  31. WHILE (idx < LEN(str)) & (idx < LEN(mss)) DO
  32. str[idx] := mss[idx]; INC(idx);
  33. END;
  34. IF idx < LEN(str) THEN str[idx] := 0X END;
  35. END Message;
  36. (*===============================================================*)
  37. PROCEDURE expLen(exp : INTEGER) : INTEGER;
  38. BEGIN
  39. exp := ABS(exp);
  40. IF exp < 10 THEN RETURN 3;
  41. ELSIF exp < 100 THEN RETURN 4;
  42. ELSE RETURN 5;
  43. END;
  44. END expLen;
  45. (*===============================================================*)
  46. PROCEDURE CopyCh(ch : CHAR;
  47. VAR ix : INTEGER;
  48. VAR st : ARRAY OF CHAR);
  49. BEGIN
  50. IF ix < LEN(st) THEN st[ix] := ch; INC(ix) END;
  51. END CopyCh;
  52. (*===============================================================*)
  53. PROCEDURE CopyExp(ex : INTEGER;
  54. VAR ix : INTEGER;
  55. VAR st : ARRAY OF CHAR);
  56. VAR abX, val, len, idx, dHi : INTEGER;
  57. BEGIN
  58. dHi := LEN(st) - 1;
  59. len := expLen(ex);
  60. IF ix + len > dHi THEN ix := dHi - len END;
  61. IF ix < 2 THEN
  62. FOR idx := 0 TO MIN(ix+len, dHi-1) DO st[idx] := "*"; ix := idx+1 END;
  63. ELSE
  64. CopyCh("E",ix,st);
  65. IF ex > 0 THEN CopyCh("+",ix,st) ELSE CopyCh("-",ix,st) END;
  66. abX := ABS(ex); val := abX;
  67. IF abX >= 100 THEN
  68. CopyCh(CHR(val DIV 100 + ORD("0")),ix,st);
  69. val := val MOD 100;
  70. END;
  71. IF abX >= 10 THEN
  72. CopyCh(CHR(val DIV 10 + ORD("0")),ix,st);
  73. END;
  74. CopyCh(CHR(val MOD 10 + ORD("0")),ix,st);
  75. END;
  76. END CopyExp;
  77. (*===============================================================*)
  78. PROCEDURE GetDigits(real : REAL;
  79. OUT digits : DigArray;
  80. OUT dPoint : INTEGER;
  81. OUT isNeg : BOOLEAN);
  82. VAR rIdx : INTEGER; (* the read index *)
  83. wIdx : INTEGER; (* the write index *)
  84. iLen : INTEGER; (* integer part len *)
  85. eVal : INTEGER; (* exponent value *)
  86. buff : DigArray; (* temporary buffer *)
  87. eNeg : BOOLEAN; (* exponent is neg. *)
  88. rChr : CHAR; (* last read char *)
  89. BEGIN
  90. (*
  91. * We want to assert that digit[0] # "0",
  92. * unless real = zero. So to avoid a sack o'woe
  93. *)
  94. IF real = 0.0 THEN
  95. digits := "0";
  96. dPoint := 1;
  97. isNeg := FALSE; RETURN; (* PREEMPTIVE RETURN HERE *)
  98. END;
  99. RTS.RealToStrInvar(real, buff);
  100. rIdx := 0;
  101. wIdx := 0;
  102. eVal := 0;
  103. (* get optional sign *)
  104. isNeg := (buff[0] = "-");
  105. IF isNeg THEN INC(rIdx) END;
  106. rChr := buff[rIdx]; INC(rIdx);
  107. WHILE rChr = "0" DO
  108. rChr := buff[rIdx]; INC(rIdx);
  109. END;
  110. (* get integer part *)
  111. WHILE (rChr <= "9") & (rChr >= "0") DO
  112. digits[wIdx] := rChr; INC(wIdx);
  113. rChr := buff[rIdx]; INC(rIdx);
  114. END;
  115. iLen := wIdx; (* integer part ended *)
  116. IF rChr = "." THEN (* get fractional part *)
  117. rChr := buff[rIdx]; INC(rIdx);
  118. IF wIdx = 0 THEN
  119. (* count any leading zeros *)
  120. WHILE rChr = "0" DO
  121. rChr := buff[rIdx]; INC(rIdx); DEC(iLen);
  122. END;
  123. END;
  124. WHILE (rChr <= "9") & (rChr >= "0") DO
  125. digits[wIdx] := rChr; INC(wIdx);
  126. rChr := buff[rIdx]; INC(rIdx);
  127. END;
  128. END;
  129. digits[wIdx] := 0X; (* terminate char arr. *)
  130. IF (rChr = "E") OR (rChr = "e") THEN
  131. (* get fractional part *)
  132. rChr := buff[rIdx]; INC(rIdx);
  133. IF rChr = "-" THEN
  134. eNeg := TRUE;
  135. rChr := buff[rIdx]; INC(rIdx);
  136. ELSE
  137. eNeg := FALSE;
  138. IF rChr = "+" THEN rChr := buff[rIdx]; INC(rIdx) END;
  139. END;
  140. WHILE (rChr <= "9") & (rChr >= "0") DO
  141. eVal := eVal * 10;
  142. INC(eVal, (ORD(rChr) - ORD("0")));
  143. rChr := buff[rIdx]; INC(rIdx);
  144. END;
  145. IF eNeg THEN eVal := -eVal END;
  146. END;
  147. (* At this point, if we are not ended, we have a NaN *)
  148. IF rChr # 0X THEN
  149. digits := buff; dPoint := err;
  150. ELSE
  151. (* Index of virtual decimal point is eVal + iLen *)
  152. DEC(eVal);
  153. dPoint := iLen + eVal;
  154. END;
  155. END GetDigits;
  156. (***************************************************************)
  157. PROCEDURE RoundRelative(VAR str : DigArray;
  158. VAR exp : INTEGER;
  159. num : INTEGER);
  160. VAR len : INTEGER;
  161. idx : INTEGER;
  162. chr : CHAR;
  163. BEGIN
  164. len := LEN(str$); (* we want num+1 digits *)
  165. IF num < 0 THEN
  166. str[0] := 0X;
  167. ELSIF num = 0 THEN
  168. chr := str[0];
  169. IF chr > "4" THEN
  170. str := "1"; INC(exp);
  171. ELSE
  172. str[num] := 0X;
  173. END;
  174. ELSIF num < len THEN
  175. chr := str[num];
  176. IF chr > "4" THEN (* round up str[num-1] *)
  177. idx := num-1;
  178. LOOP
  179. str[idx] := CHR(ORD(str[idx]) + 1);
  180. IF str[idx] <= "9" THEN EXIT;
  181. ELSE
  182. str[idx] := "0"; (* and propagate *)
  183. IF idx = 0 THEN (* need a shift *)
  184. FOR idx := num TO 0 BY -1 DO str[idx+1] := str[idx] END;
  185. str[0] := "1"; INC(exp); EXIT;
  186. END;
  187. END;
  188. DEC(idx);
  189. END;
  190. END;
  191. str[num] := 0X;
  192. END;
  193. END RoundRelative;
  194. (***************************************************************)
  195. (* *)
  196. (* PUBLIC - EXPORTED *)
  197. (* *)
  198. (***************************************************************)
  199. (*===============================================================*
  200. *
  201. * Ignores any leading spaces in str. If the subsequent characters in str
  202. * are in the format of a signed real number, assigns a corresponding value
  203. * to real. Assigns a value indicating the format of str to res.
  204. *)
  205. PROCEDURE StrToReal*(str : ARRAY OF CHAR;
  206. OUT real : REAL;
  207. OUT res : BOOLEAN);
  208. VAR clrStr : RTS.NativeString;
  209. BEGIN
  210. clrStr := MKSTR(str);
  211. RTS.StrToRealInvar(clrStr, real, res);
  212. END StrToReal;
  213. (*===============================================================*
  214. *
  215. * Converts the value of real to floating-point string form, with sigFigs
  216. * significant digits, and copies the possibly truncated result to str.
  217. *)
  218. PROCEDURE RealToFloat*(real : REAL;
  219. sigFigs : INTEGER;
  220. OUT str : ARRAY OF CHAR);
  221. VAR len, fWid, index, ix : INTEGER;
  222. dExp : INTEGER; (* decimal exponent *)
  223. neg : BOOLEAN;
  224. digits : DigArray;
  225. BEGIN
  226. GetDigits(real, digits, dExp, neg);
  227. IF dExp = err THEN Message(str, digits); RETURN END;
  228. RoundRelative(digits, dExp, sigFigs);
  229. index := 0;
  230. IF neg THEN CopyCh("-", index, str) END;
  231. fWid := LEN(digits$);
  232. IF fWid = 0 THEN (* result is 0 *)
  233. CopyCh("0", index, str);
  234. dExp := 0;
  235. ELSE
  236. CopyCh(digits[0], index, str);
  237. END;
  238. IF sigFigs > 1 THEN
  239. CopyCh(".",index,str);
  240. IF fWid > 1 THEN
  241. FOR ix := 1 TO fWid - 1 DO CopyCh(digits[ix], index, str) END;
  242. END;
  243. FOR ix := fWid TO sigFigs - 1 DO CopyCh("0", index, str) END;
  244. END;
  245. (*
  246. * IF dExp # 0 THEN CopyExp(dExp,index,str) END;
  247. *)
  248. CopyExp(dExp,index,str);
  249. IF index <= LEN(str)-1 THEN str[index] := 0X END;
  250. END RealToFloat;
  251. (*===============================================================*
  252. *
  253. * Converts the value of real to floating-point string form, with sigFigs
  254. * significant digits, and copies the possibly truncated result to str.
  255. * The number is scaled with one to three digits in the whole number part and
  256. * with an exponent that is a multiple of three.
  257. *)
  258. PROCEDURE RealToEng*(real : REAL;
  259. sigFigs : INTEGER;
  260. OUT str : ARRAY OF CHAR);
  261. VAR len, index, ix : INTEGER;
  262. dExp : INTEGER; (* decimal exponent *)
  263. fact : INTEGER;
  264. neg : BOOLEAN;
  265. digits : DigArray;
  266. BEGIN
  267. GetDigits(real, digits, dExp, neg);
  268. IF dExp = err THEN Message(str, digits); RETURN END;
  269. RoundRelative(digits, dExp, sigFigs);
  270. len := LEN(digits$); INC(dExp);
  271. IF len = 0 THEN dExp := 1 END; (* result = 0 *)
  272. fact := ((dExp - 1) MOD 3) + 1;
  273. DEC(dExp,fact); (* make exponent multiple of three *)
  274. index := 0;
  275. IF neg THEN CopyCh("-",index,str) END;
  276. IF fact <= len THEN
  277. FOR ix := 0 TO fact - 1 DO CopyCh(digits[ix],index,str) END;
  278. ELSE
  279. IF len > 0 THEN
  280. FOR ix := 0 TO len - 1 DO CopyCh(digits[ix],index,str) END;
  281. END;
  282. FOR ix := len TO fact - 1 DO CopyCh("0",index,str) END;
  283. END;
  284. IF fact < sigFigs THEN
  285. CopyCh(".",index,str);
  286. IF fact < len THEN
  287. FOR ix := fact TO len - 1 DO CopyCh(digits[ix],index,str) END;
  288. ELSE
  289. len := fact;
  290. END;
  291. FOR ix := len TO sigFigs - 1 DO CopyCh("0",index,str) END;
  292. END;
  293. (*
  294. * IF dExp # 0 THEN CopyExp(dExp,index,str) END;
  295. *)
  296. CopyExp(dExp,index,str);
  297. IF index <= LEN(str)-1 THEN str[index] := 0X END;
  298. END RealToEng;
  299. (*===============================================================*
  300. *
  301. * Converts the value of real to fixed-point string form, rounded to the
  302. * given place relative to the decimal point, and copies the result to str.
  303. *)
  304. PROCEDURE RealToFixed*(real : REAL;
  305. place : INTEGER; (* requested no of frac. places *)
  306. OUT str : ARRAY OF CHAR);
  307. VAR lWid : INTEGER; (* Leading digit-str width *)
  308. fWid : INTEGER; (* Width of fractional part *)
  309. tWid : INTEGER; (* Total width of str-rep. *)
  310. zWid : INTEGER; (* Leading zeros in frac. *)
  311. len : INTEGER; (* Significant digit length *)
  312. dExp : INTEGER; (* Pos. of rad. in dig-arr. *)
  313. dLen : INTEGER; (* Length of dest. array *)
  314. index : INTEGER;
  315. ix : INTEGER;
  316. neg : BOOLEAN;
  317. radix : BOOLEAN;
  318. digits : DigArray;
  319. BEGIN
  320. (* the decimal point and fraction part *)
  321. (* ["-"] "0" "." d^(fWid) -- if dExp < 0 *)
  322. (* ["-"] d^(lWid) "." d^(fWid) -- if fWid > 0 *)
  323. (* ["-"] d^(lWid) -- if fWid = 0 *)
  324. tWid := 0;
  325. dLen := LEN(str);
  326. IF place >= 0 THEN fWid := place ELSE fWid := 0 END;
  327. radix := (fWid > 0);
  328. GetDigits(real, digits, dExp, neg);
  329. IF dExp = err THEN Message(str, digits); RETURN END;
  330. RoundRelative(digits, dExp, place+dExp+1); (* this can change dExp! *)
  331. (* Semantics of dExp value *)
  332. (* 012345 ... digit index *)
  333. (* dddddd ... digit content *)
  334. (* ^-------- dExp value *)
  335. (* "ddd.ddd..." result str. *)
  336. len := LEN(digits$);
  337. IF len = 0 THEN neg := FALSE END; (* don't print "-0" *)
  338. IF dExp >= 0 THEN lWid := dExp+1 ELSE lWid := 1 END;
  339. IF neg THEN INC(tWid) END;
  340. IF radix THEN INC(tWid) END;
  341. INC(tWid, lWid);
  342. INC(tWid, fWid);
  343. IF tWid > dLen THEN tWid := dLen END;
  344. index := 0;
  345. (*
  346. * Now copy the optional signe
  347. *)
  348. IF neg THEN CopyCh("-",index,str) END;
  349. (*
  350. * Now copy the integer part
  351. *)
  352. IF dExp < 0 THEN
  353. CopyCh("0",index,str);
  354. ELSE
  355. IF lWid <= len THEN
  356. FOR ix := 0 TO lWid - 1 DO CopyCh(digits[ix],index,str) END;
  357. ELSE
  358. IF len > 0 THEN
  359. FOR ix := 0 TO len - 1 DO CopyCh(digits[ix],index,str) END;
  360. END;
  361. FOR ix := len TO lWid - 1 DO CopyCh("0",index,str) END;
  362. END;
  363. END;
  364. (*
  365. * Now copy the fractional part
  366. *)
  367. IF radix THEN
  368. CopyCh(".",index,str);
  369. IF dExp < 0 THEN
  370. (* 012345 ... digit idx *)
  371. (* dddddd ... digit str. *)
  372. (* ^-------- dExp = -1 *)
  373. zWid := MIN(-dExp-1, fWid); (* leading zero width *)
  374. FOR ix := 0 TO zWid - 1 DO CopyCh("0",index,str) END;
  375. FOR ix := 0 TO len - 1 DO CopyCh(digits[ix],index,str) END;
  376. ELSIF lWid < len THEN
  377. FOR ix := lWid TO len - 1 DO CopyCh(digits[ix],index,str) END;
  378. END;
  379. WHILE index < tWid DO CopyCh("0",index,str) END;
  380. END;
  381. IF index <= dLen-1 THEN str[index] := 0X END;
  382. END RealToFixed;
  383. (*===============================================================*
  384. *
  385. * Converts the value of real as RealToFixed if the sign and magnitude can be
  386. * shown within the capacity of str, or otherwise as RealToFloat, and copies
  387. * the possibly truncated result to str.
  388. * The number of places or significant digits are implementation-defined.
  389. *)
  390. PROCEDURE RealToStr*(real: REAL; OUT str: ARRAY OF CHAR);
  391. BEGIN
  392. RTS.RealToStrInvar(real, str);
  393. RESCUE (x);
  394. RealToFloat(real, 16, str);
  395. END RealToStr;
  396. (* ---------------------------------------- *)
  397. END RealStr.