real2str.inc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Michael Van Canneyt,
  4. member of the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. type
  12. { See symconst.pas tfloattype }
  13. treal_type = (
  14. rt_s32real,rt_s64real,rt_s80real,rt_sc80real,
  15. rt_c64bit,rt_currency,rt_s128real
  16. );
  17. { corresponding to single double extended fixed comp for i386 }
  18. {$if not declared(mul_by_power10)}
  19. function mul_by_power10 (x : ValReal; power : integer) : ValReal; forward;
  20. {$endif}
  21. Procedure str_real (len,f : longint; d : ValReal; real_type :treal_type; out s : string);
  22. {$ifdef SUPPORT_EXTENDED}
  23. type
  24. TSplitExtended = packed record
  25. case byte of
  26. 0: (bytes: Array[0..9] of byte);
  27. 1: (words: Array[0..4] of word);
  28. 2: (cards: Array[0..1] of cardinal; w: word);
  29. end;
  30. const
  31. maxDigits = 17;
  32. {$else}
  33. {$ifdef SUPPORT_DOUBLE}
  34. {$ifndef cpujvm}
  35. type
  36. TSplitDouble = packed record
  37. case byte of
  38. 0: (bytes: Array[0..7] of byte);
  39. 1: (words: Array[0..3] of word);
  40. 2: (cards: Array[0..1] of cardinal);
  41. end;
  42. {$endif}
  43. const
  44. maxDigits = 15;
  45. {$else}
  46. {$ifdef SUPPORT_SINGLE}
  47. type
  48. TSplitSingle = packed record
  49. case byte of
  50. 0: (bytes: Array[0..3] of byte);
  51. 1: (words: Array[0..1] of word);
  52. 2: (cards: Array[0..0] of cardinal);
  53. end;
  54. const
  55. maxDigits = 9;
  56. {$endif SUPPORT_SINGLE}
  57. {$endif SUPPORT_DOUBLE}
  58. {$endif SUPPORT_EXTENDED}
  59. type
  60. { the value in the last position is used for rounding }
  61. TIntPartStack = array[1..maxDigits+1] of valReal;
  62. var
  63. {$ifdef cpujvm}
  64. doublebits: int64;
  65. {$endif}
  66. roundCorr, corrVal, factor : valReal;
  67. high_exp10_reduced,
  68. spos, endpos, fracCount: longint;
  69. correct, currprec: longint;
  70. temp : string;
  71. power : string[10];
  72. sign : boolean;
  73. dot : byte;
  74. fraczero, expMaximal: boolean;
  75. maxlen : longint; { Maximal length of string for float }
  76. minlen : longint; { Minimal length of string for float }
  77. explen : longint; { Length of exponent, including E and sign.
  78. Must be strictly larger than 2 }
  79. const
  80. maxexp = 1e+35; { Maximum value for decimal expressions }
  81. minexp = 1e-35; { Minimum value for decimal expressions }
  82. zero = '0000000000000000000000000000000000000000';
  83. procedure RoundStr(var s: string; lastPos: byte);
  84. var carry: longint;
  85. begin
  86. carry := 1;
  87. repeat
  88. s[lastPos] := chr(ord(s[lastPos])+carry);
  89. carry := 0;
  90. if s[lastPos] > '9' then
  91. begin
  92. s[lastPos] := '0';
  93. carry := 1;
  94. end;
  95. dec(lastPos);
  96. until carry = 0;
  97. end;
  98. procedure getIntPart(d: valreal);
  99. var
  100. intPartStack: TIntPartStack;
  101. intPart, stackPtr, endStackPtr, digits: longint;
  102. overflow: boolean;
  103. begin
  104. {$ifdef DEBUG_NASM}
  105. writeln(stderr,'getintpart(d) entry');
  106. {$endif DEBUG_NASM}
  107. { position in the stack (gets increased before first write) }
  108. stackPtr := 0;
  109. { number of digits processed }
  110. digits := 0;
  111. { did we wrap around in the stack? Necessary to know whether we should round }
  112. overflow :=false;
  113. { generate a list consisting of d, d/10, d/100, ... until d < 1.0 }
  114. while d > 1.0-roundCorr do
  115. begin
  116. inc(stackPtr);
  117. inc(digits);
  118. if stackPtr > maxDigits+1 then
  119. begin
  120. stackPtr := 1;
  121. overflow := true;
  122. end;
  123. intPartStack[stackPtr] := d;
  124. d := d / 10.0;
  125. end;
  126. { if no integer part, exit }
  127. if digits = 0 then
  128. exit;
  129. endStackPtr := stackPtr+1;
  130. if endStackPtr > maxDigits + 1 then
  131. endStackPtr := 1;
  132. { now, all digits are calculated using trunc(d*10^(-n)-int(d*10^(-n-1))*10) }
  133. corrVal := 0.0;
  134. { the power of 10 with which the resulting string has to be "multiplied" }
  135. { if the decimal point is placed after the first significant digit }
  136. correct := digits-1;
  137. {$ifdef DEBUG_NASM}
  138. writeln(stderr,'endStackPtr = ',endStackPtr);
  139. {$endif DEBUG_NASM}
  140. repeat
  141. if (currprec > 0) then
  142. begin
  143. intPart:= trunc(intPartStack[stackPtr]-corrVal);
  144. dec(currPrec);
  145. inc(spos);
  146. temp[spos] := chr(intPart+ord('0'));
  147. {$ifdef DEBUG_NASM}
  148. writeln(stderr,'stackptr =',stackptr,' intpart = ',intpart);
  149. {$endif DEBUG_NASM}
  150. if temp[spos] > '9' then
  151. begin
  152. temp[spos] := chr(ord(temp[spos])-10);
  153. roundStr(temp,spos-1);
  154. end;
  155. end;
  156. corrVal := int(intPartStack[stackPtr]) * 10.0;
  157. {$ifdef DEBUG_NASM}
  158. writeln(stderr,'trunc(corrval) = ',trunc(corrval));
  159. {$endif DEBUG_NASM}
  160. dec(stackPtr);
  161. if stackPtr = 0 then
  162. stackPtr := maxDigits+1;
  163. until (overflow and (stackPtr = endStackPtr)) or
  164. (not overflow and (stackPtr = maxDigits+1)) or (currPrec = 0);
  165. { round if we didn't use all available digits yet and if the }
  166. { remainder is > 5 }
  167. if (overflow or
  168. (stackPtr < maxDigits+1)) then
  169. begin
  170. { we didn't use all available digits of the whole part -> make sure }
  171. { the fractional part is not used for rounding later }
  172. currprec := -1;
  173. { instead, round based on the next whole digit }
  174. if (int(intPartStack[stackPtr]-corrVal) >= 5.0) then
  175. roundStr(temp,spos);
  176. end;
  177. {$ifdef DEBUG_NASM}
  178. writeln(stderr,'temp at getintpart exit is = ',temp);
  179. {$endif DEBUG_NASM}
  180. end;
  181. function reduce_exponent (d : ValReal; out scaled : ValReal) : longint;
  182. { Returns decimal exponent which was used for scaling, and a scaled value out }
  183. const
  184. C_LN10 = ln(10);
  185. var
  186. log10_d : longint;
  187. begin
  188. reduce_exponent := 0;
  189. if d<>0 then
  190. begin
  191. // get exponent approximation ["d" is assumed to be non-negative]
  192. log10_d:=trunc(ln(d)/C_LN10);
  193. // trying to stay at least 1 digit away from introducing integer/fractional part
  194. if log10_d > maxDigits+1 then
  195. reduce_exponent := log10_d-maxDigits
  196. else
  197. if log10_d < -(maxDigits+1) then
  198. reduce_exponent := log10_d+maxDigits
  199. // else
  200. // the number is already suitable enough
  201. end;
  202. // do scaling if needed
  203. if reduce_exponent<>0
  204. then scaled := mul_by_power10(d,-reduce_exponent) // denormals should be handled properly by this call
  205. else scaled := d;
  206. end;
  207. begin
  208. case real_type of
  209. rt_s32real :
  210. begin
  211. maxlen:=16;
  212. minlen:=8;
  213. explen:=4;
  214. { correction used with comparing to avoid rounding/precision errors }
  215. roundCorr := 1.1920928955e-07;
  216. end;
  217. rt_s64real :
  218. begin
  219. maxlen := 22;
  220. { correction used with comparing to avoid rounding/precision errors }
  221. roundCorr := 2.2204460493e-16;
  222. minlen:=9;
  223. explen:=5;
  224. end;
  225. rt_s80real,
  226. rt_sc80real:
  227. begin
  228. { Different in TP help, but this way the output is the same (JM) }
  229. maxlen:=25;
  230. minlen:=10;
  231. explen:=6;
  232. { correction used with comparing to avoid rounding/precision errors }
  233. roundCorr := 1.0842021725e-19;
  234. end;
  235. rt_c64bit :
  236. begin
  237. maxlen:=23;
  238. minlen:=10;
  239. { according to TP (was 5) (FK) }
  240. explen:=6;
  241. { correction used with comparing to avoid rounding/precision errors }
  242. roundCorr := 2.2204460493e-16;
  243. end;
  244. rt_currency :
  245. begin
  246. { Different in TP help, but this way the output is the same (JM) }
  247. maxlen:=25;
  248. minlen:=10;
  249. explen:=0;
  250. { correction used with comparing to avoid rounding/precision errors }
  251. roundCorr := 1.0842021725e-19;
  252. end;
  253. rt_s128real :
  254. begin
  255. { Different in TP help, but this way the output is the same (JM) }
  256. maxlen:=25;
  257. minlen:=10;
  258. explen:=6;
  259. { correction used with comparing to avoid rounding/precision errors }
  260. roundCorr := 1.0842021725e-19;
  261. end;
  262. else
  263. begin
  264. { keep JVM byte code verifier happy }
  265. maxlen:=0;
  266. minlen:=0;
  267. explen:=0;
  268. roundCorr:=0;
  269. end;
  270. end;
  271. { check parameters }
  272. { default value for length is -32767 }
  273. if len=-32767 then
  274. len:=maxlen;
  275. { determine sign. before precision, needs 2 less calls to abs() }
  276. {$ifndef endian_big}
  277. {$ifdef SUPPORT_EXTENDED}
  278. { extended, format (MSB): 1 Sign bit, 15 bit exponent, 64 bit mantissa }
  279. sign := (TSplitExtended(d).w and $8000) <> 0;
  280. expMaximal := (TSplitExtended(d).w and $7fff) = 32767;
  281. fraczero := (TSplitExtended(d).cards[0] = 0) and
  282. ((TSplitExtended(d).cards[1] and $7fffffff) = 0);
  283. {$else SUPPORT_EXTENDED}
  284. {$ifdef SUPPORT_DOUBLE}
  285. {$ifdef FPC_DOUBLE_HILO_SWAPPED}
  286. { double, format (MSB): 1 Sign bit, 11 bit exponent, 52 bit mantissa }
  287. { high and low dword are swapped when using the arm fpa }
  288. sign := ((TSplitDouble(d).cards[0] shr 20) and $800) <> 0;
  289. expMaximal := ((TSplitDouble(d).cards[0] shr 20) and $7ff) = 2047;
  290. fraczero:= (TSplitDouble(d).cards[0] and $fffff = 0) and
  291. (TSplitDouble(d).cards[1] = 0);
  292. {$else FPC_DOUBLE_HILO_SWAPPED}
  293. { double, format (MSB): 1 Sign bit, 11 bit exponent, 52 bit mantissa }
  294. sign := ((TSplitDouble(d).cards[1] shr 20) and $800) <> 0;
  295. expMaximal := ((TSplitDouble(d).cards[1] shr 20) and $7ff) = 2047;
  296. fraczero := (TSplitDouble(d).cards[1] and $fffff = 0) and
  297. (TSplitDouble(d).cards[0] = 0);
  298. {$endif FPC_DOUBLE_HILO_SWAPPED}
  299. {$else SUPPORT_DOUBLE}
  300. {$ifdef SUPPORT_SINGLE}
  301. { single, format (MSB): 1 Sign bit, 8 bit exponent, 23 bit mantissa }
  302. sign := ((TSplitSingle(d).words[1] shr 7) and $100) <> 0;
  303. expMaximal := ((TSplitSingle(d).words[1] shr 7) and $ff) = 255;
  304. fraczero := (TSplitSingle(d).cards[0] and $7fffff = 0);
  305. {$else SUPPORT_SINGLE}
  306. {$error No little endian floating type supported yet in real2str}
  307. {$endif SUPPORT_SINGLE}
  308. {$endif SUPPORT_DOUBLE}
  309. {$endif SUPPORT_EXTENDED}
  310. {$else endian_big}
  311. {$ifdef SUPPORT_EXTENDED}
  312. {$error sign/NaN/Inf not yet supported for big endian CPU's in str_real}
  313. {$else SUPPORT_EXTENDED}
  314. {$ifdef SUPPORT_DOUBLE}
  315. {$ifdef cpujvm}
  316. doublebits := JLDouble.doubleToLongBits(d);
  317. sign := doublebits<0;
  318. expMaximal := (doublebits shr (32+20)) and $7ff = 2047;
  319. fraczero:= (((doublebits shr 32) and $fffff) = 0) and
  320. (longint(doublebits)=0);
  321. {$else cpujvm}
  322. { double, format (MSB): 1 Sign bit, 11 bit exponent, 52 bit mantissa }
  323. sign := ((TSplitDouble(d).cards[0] shr 20) and $800) <> 0;
  324. expMaximal := ((TSplitDouble(d).cards[0] shr 20) and $7ff) = 2047;
  325. fraczero:= (TSplitDouble(d).cards[0] and $fffff = 0) and
  326. (TSplitDouble(d).cards[1] = 0);
  327. {$endif cpujvm}
  328. {$else SUPPORT_DOUBLE}
  329. {$ifdef SUPPORT_SINGLE}
  330. { single, format (MSB): 1 Sign bit, 8 bit exponent, 23 bit mantissa }
  331. sign := ((TSplitSingle(d).bytes[0] and $80)) <> 0;
  332. expMaximal := ((TSplitSingle(d).words[0] shr 7) and $ff) = 255;
  333. fraczero:= (TSplitSingle(d).cards[0] and $7fffff = 0);
  334. {$else SUPPORT_SINGLE}
  335. {$error No big endian floating type supported yet in real2str}
  336. {$endif SUPPORT_SINGLE}
  337. {$endif SUPPORT_DOUBLE}
  338. {$endif SUPPORT_EXTENDED}
  339. {$endif endian}
  340. if expMaximal then
  341. if fraczero then
  342. if sign then
  343. temp := '-Inf'
  344. else temp := '+Inf'
  345. else temp := 'Nan'
  346. else
  347. begin
  348. { d:=abs(d); this converts d to double so we loose precision }
  349. { for the same reason I converted d:=frac(d) to d:=d-int(d); (PM) }
  350. if sign then
  351. d:=-d;
  352. { determine precision : maximal precision is : }
  353. currPrec := maxlen-explen-2;
  354. { this is also the maximal number of decimals !!}
  355. if f>currprec then
  356. f:=currprec;
  357. { when doing a fixed-point, we need less characters.}
  358. if (f<0) {or ((d<>0) and ((d>maxexp) and (d>minexp)))} then
  359. begin
  360. { determine maximal number of decimals }
  361. if (len>=0) and (len<minlen) then
  362. len:=minlen;
  363. if (len>0) and (len<maxlen) then
  364. currprec:=len-explen-2;
  365. end;
  366. { leading zero, may be necessary for things like str(9.999:0:2) to }
  367. { be able to insert an extra character at the start of the string }
  368. temp := ' 0';
  369. { position in the temporary output string }
  370. spos := 2;
  371. // workaround to make follow-up things go somewhat faster
  372. high_exp10_reduced := 0;
  373. case real_type of
  374. // blacklist, in order of increasing headache:
  375. //? rt_s32real :;
  376. // ? needs additional testing to ensure any reasonable benefit
  377. // without lost of accuracy due to an extra conversion
  378. rt_c64bit, rt_currency :;
  379. // no much sense to touch them
  380. else
  381. // acceptable:
  382. // ? rt_s32real [see above]
  383. // rt_s64real
  384. // rt_s80real, rt_sc80real
  385. // ? rt_s128real [have not tried]
  386. high_exp10_reduced := reduce_exponent(d,d);
  387. end;
  388. { get the integer part }
  389. correct := 0;
  390. GetIntPart(d);
  391. inc(correct,high_exp10_reduced); // end of workaround
  392. { now process the fractional part }
  393. if d > 1.0- roundCorr then
  394. d := frac(d);
  395. { if we have to round earlier than the amount of available precision, }
  396. { only calculate digits up to that point }
  397. if (f >= 0) and (currPrec > f) then
  398. currPrec := f;
  399. { if integer part was zero, go to the first significant digit of the }
  400. { fractional part }
  401. { make sure we don't get an endless loop if d = 0 }
  402. if (spos = 2) and (d <> 0.0) then
  403. begin
  404. { take rounding errors into account }
  405. while d < 0.1-roundCorr do
  406. begin
  407. d := d * 10.0;
  408. dec(correct);
  409. { adjust the precision depending on how many digits we }
  410. { already "processed" by multiplying by 10, but only if }
  411. { the amount of precision is specified }
  412. if f >= 0 then
  413. dec(currPrec);
  414. end;
  415. dec(correct);
  416. end;
  417. { current length of the output string in endPos }
  418. endPos := spos;
  419. { always calculate at least 1 fractional digit for rounding }
  420. if (currPrec >= 0) then
  421. begin
  422. corrVal := 0.5;
  423. factor := 1;
  424. for fracCount := 1 to currPrec do
  425. factor := factor * 10.0;
  426. corrval := corrval / factor;
  427. { for single, we may write more significant digits than are available,
  428. so the rounding correction itself can show up -> don't round in that
  429. case
  430. }
  431. if real_type<>rt_s32real then
  432. d:=d+d*roundCorr;
  433. if d >= corrVal then
  434. d := d + corrVal;
  435. if int(d) = 1 then
  436. begin
  437. roundStr(temp,spos);
  438. d := frac(d);
  439. end;
  440. { calculate the necessary fractional digits }
  441. for fracCount := 1 to currPrec do
  442. begin
  443. if d > 1.0 then
  444. d := frac(d) * 10.0
  445. else d := d * 10.0;
  446. inc(spos);
  447. temp[spos] := chr(trunc(d)+ord('0'));
  448. if temp[spos] > '9' then
  449. { possible because trunc and the "*10.0" aren't exact :( }
  450. begin
  451. temp[spos] := chr(ord(temp[spos]) - 10);
  452. roundStr(temp,spos-1);
  453. end;
  454. end;
  455. { new length of string }
  456. endPos := spos;
  457. end;
  458. setLength(temp,endPos);
  459. { delete leading zero if we didn't need it while rounding at the }
  460. { string level }
  461. if temp[2] = '0' then
  462. delete(temp,2,1)
  463. { the rounding caused an overflow to the next power of 10 }
  464. else inc(correct);
  465. if sign then
  466. temp[1] := '-';
  467. if (f<0) or (correct>(round(ln(maxexp)/ln(10)))) then
  468. begin
  469. insert ('.',temp,3);
  470. str(abs(correct),power);
  471. if length(power)<explen-2 then
  472. power:=copy(zero,1,explen-2-length(power))+power;
  473. if correct<0 then
  474. power:='-'+power
  475. else
  476. power:='+'+power;
  477. temp:=temp+'E'+power;
  478. end
  479. else
  480. begin
  481. if not sign then
  482. begin
  483. delete(temp,1,1);
  484. dot := 2
  485. end
  486. else
  487. dot := 3;
  488. { set zeroes and dot }
  489. if correct>=0 then
  490. begin
  491. if length(temp)<correct+dot+f-1 then
  492. temp:=temp+copy(zero,1,correct+dot+f-length(temp));
  493. insert ('.',temp,correct+dot);
  494. end
  495. else
  496. begin
  497. correct:=abs(correct);
  498. insert(copy(zero,1,correct),temp,dot-1);
  499. insert ('.',temp,dot);
  500. end;
  501. { correct length to fit precision }
  502. if f>0 then
  503. setlength(temp,pos('.',temp)+f)
  504. else
  505. setLength(temp,pos('.',temp)-1);
  506. end;
  507. end;
  508. if length(temp)<len then
  509. s:=space(len-length(temp))+temp
  510. else s:=temp;
  511. end;
  512. Procedure str_real_iso (len,f : longint; d : ValReal; real_type :treal_type; out s : string);
  513. var
  514. i : Integer;
  515. begin
  516. str_real(len,f,d,real_type,s);
  517. for i:=1 to Length(s) do
  518. if s[i]='E' then
  519. s[i]:='e';
  520. end;