3 This file is part of the Free Pascal run time library.
4 Copyright (c) 1999-2000 by Michael Van Canneyt,
5 member of the Free Pascal development team
7 See the file COPYING.FPC, included in this distribution,
8 for details about the copyright.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 **********************************************************************}
17 { See symdefh.inc tfloattyp }
18 treal_type = (rt_s32real,rt_s64real,rt_s80real,rt_c64bit,rt_f16bit,rt_f32bit);
19 { corresponding to single double extended fixed comp for i386 }
21 Procedure str_real (len,f : longint; d : ValReal; real_type :treal_type; var s : string);
22 {$ifdef SUPPORT_EXTENDED}
24 TSplitExtended = packed record
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);
33 {$ifdef SUPPORT_DOUBLE}
35 TSplitDouble = packed record
37 0: (bytes: Array[0..7] of byte);
38 1: (words: Array[0..3] of word);
39 2: (cards: Array[0..1] of cardinal);
44 {$ifdef SUPPORT_SINGLE}
46 TSplitSingle = packed record
48 0: (bytes: Array[0..3] of byte);
49 1: (words: Array[0..1] of word);
50 2: (cards: Array[0..0] of cardinal);
54 {$endif SUPPORT_SINGLE}
55 {$endif SUPPORT_DOUBLE}
56 {$endif SUPPORT_EXTENDED}
59 { the value in the last position is used for rounding }
60 TIntPartStack = array[1..maxDigits+1] of valReal;
63 roundCorr, corrVal: valReal;
64 intPart, spos, endpos, fracCount: longint;
65 correct, currprec: longint;
70 mantZero, expMaximal: boolean;
72 procedure RoundStr(var s: string; lastPos: byte);
77 s[lastPos] := chr(ord(s[lastPos])+carry);
79 if s[lastPos] > '9' then
88 procedure getIntPart(d: extended);
90 intPartStack: TIntPartStack;
91 stackPtr, endStackPtr, digits: longint;
94 { position in the stack (gets increased before first write) }
96 { number of digits processed }
98 { did we wrap around in the stack? Necessary to know whether we should round }
100 { generate a list consisting of d, d/10, d/100, ... until d < 1.0 }
101 while d > 1.0-roundCorr do
105 if stackPtr > maxDigits+1 then
110 intPartStack[stackPtr] := d;
113 { if no integer part, exit }
116 endStackPtr := stackPtr+1;
117 if endStackPtr > maxDigits + 1 then
119 { now, all digits are calculated using trunc(d*10^(-n)-int(d*10^(-n-1))*10) }
121 { the power of 10 with which the resulting string has to be "multiplied" }
122 { if the decimal point is placed after the first significant digit }
125 if (currprec > 0) then
127 intPart:= trunc(intPartStack[stackPtr]-corrVal);
130 temp[spos] := chr(intPart+ord('0'));
131 if temp[spos] > '9' then
133 temp[spos] := chr(ord(temp[spos])-10);
134 roundStr(temp,spos-1);
137 corrVal := int(intPartStack[stackPtr]) * 10.0;
140 stackPtr := maxDigits+1;
141 until (overflow and (stackPtr = endStackPtr)) or
142 (not overflow and (stackPtr = maxDigits+1)) or (currPrec = 0);
143 { round if we didn't use all available digits yet and if the }
146 (trunc(intPartStack[stackPtr]-corrVal) > 5.0 - roundCorr) then
150 var maxlen : longint; { Maximal length of string for float }
151 minlen : longint; { Minimal length of string for float }
152 explen : longint; { Length of exponent, including E and sign.
153 Must be strictly larger than 2 }
155 maxexp = 1e+35; { Maximum value for decimal expressions }
156 minexp = 1e-35; { Minimum value for decimal expressions }
157 zero = '0000000000000000000000000000000000000000';
166 { correction used with comparing to avoid rounding/precision errors }
167 roundCorr := (1/exp((16-4-3)*ln(10)));
171 { if the maximum suppported type is double, we can print out one digit }
172 { less, because otherwise we can't round properly and 1e-400 becomes }
173 { 0.99999999999e-400 (JM) }
174 {$ifdef support_extended}
176 { correction used with comparing to avoid rounding/precision errors }
177 roundCorr := (1/exp((23-5-3)*ln(10)));
178 {$else support_extended}
179 {$ifdef support_double}
181 { correction used with comparing to avoid rounding/precision errors }
182 roundCorr := (1/exp((22-4-3)*ln(10)));
183 {$endif support_double}
184 {$endif support_extended}
190 { Different in TP help, but this way the output is the same (JM) }
194 { correction used with comparing to avoid rounding/precision errors }
195 roundCorr := (1/exp((25-6-3)*ln(10)));
201 { according to TP (was 5) (FK) }
203 { correction used with comparing to avoid rounding/precision errors }
204 roundCorr := (1/exp((23-6-3)*ln(10)));
211 { correction used with comparing to avoid rounding/precision errors }
212 roundCorr := (1/exp((16-4-3)*ln(10)));
219 { correction used with comparing to avoid rounding/precision errors }
220 roundCorr := (1/exp((16-4-3)*ln(10)));
224 { default value for length is -32767 }
227 { determine sign. before precision, needs 2 less calls to abs() }
229 {$ifdef SUPPORT_EXTENDED}
230 { extended, format (MSB): 1 Sign bit, 15 bit exponent, 64 bit mantissa }
231 sign := (TSplitExtended(d).w and $8000) <> 0;
232 expMaximal := (TSplitExtended(d).w and $7fff) = 32767;
233 mantZero := (TSplitExtended(d).cards[0] = 0) and
234 (TSplitExtended(d).cards[1] = 0);
235 {$else SUPPORT_EXTENDED}
236 {$ifdef SUPPORT_DOUBLE}
237 { double, format (MSB): 1 Sign bit, 11 bit exponent, 52 bit mantissa }
238 sign := ((TSplitDouble(d).cards[1] shr 20) and $800) <> 0;
239 expMaximal := ((TSplitDouble(d).cards[1] shr 20) and $7ff) = 2047;
240 mantZero := (TSplitDouble(d).cards[1] and $fffff = 0) and
241 (TSplitDouble(d).cards[0] = 0);
242 {$else SUPPORT_DOUBLE}
243 {$ifdef SUPPORT_SINGLE}
244 { single, format (MSB): 1 Sign bit, 8 bit exponent, 23 bit mantissa }
245 sign := ((TSplitSingle(d).words[1] shr 7) and $100) <> 0;
246 expMaximal := ((TSplitSingle(d).words[1] shr 7) and $ff) = 255;
247 mantZero := (TSplitSingle(d).cards[0] and $7fffff = 0);
248 {$else SUPPORT_SINGLE}
249 {$error No big endian floating type supported yet in real2str}
250 {$endif SUPPORT_SINGLE}
251 {$endif SUPPORT_DOUBLE}
252 {$endif SUPPORT_EXTENDED}
254 {$error sign/NaN/Inf not yet supported for big endian CPU's in str_real}
264 { d:=abs(d); this converts d to double so we loose precision }
265 { for the same reason I converted d:=frac(d) to d:=d-int(d); (PM) }
268 { determine precision : maximal precision is : }
269 currPrec := maxlen-explen-2;
270 { this is also the maximal number of decimals !!}
273 { when doing a fixed-point, we need less characters.}
274 if (f<0) {or ((d<>0) and ((d>maxexp) and (d>minexp)))} then
276 { determine maximal number of decimals }
277 if (len>=0) and (len<minlen) then
279 if (len>0) and (len<maxlen) then
280 currprec:=len-explen-2;
283 { leading zero, may be necessary for things like str(9.999:0:2) to }
284 { be able to insert an extra character at the start of the string }
286 { position in the temporary output string }
288 { get the integer part }
291 { now process the fractional part }
292 if d > 1.0- roundCorr then
294 { if we have to round earlier than the amount of available precision, }
295 { only calculate digits up to that point }
296 if (f >= 0) and (currPrec > f) then
298 { if integer part was zero, go to the first significant digit of the }
300 { make sure we don't get an endless loop if d = 0 }
301 if (spos = 2) and (d <> 0.0) then
303 { take rounding errors into account }
304 while d < 0.1-roundCorr do
308 { adjust the precision depending on how many digits we }
309 { already "processed" by multiplying by 10, but only if }
310 { the amount of precision is specified }
316 { current length of the output string in endPos }
318 { always calculate at least 1 fractional digit for rounding }
319 if (currPrec >= 0) then
322 for fracCount := 1 to currPrec do
323 corrVal := corrVal / 10.0;
331 { calculate the necessary fractional digits }
332 for fracCount := 1 to currPrec do
334 if d > 1.0- roundCorr then
338 temp[spos] := chr(trunc(d)+ord('0'));
339 if temp[spos] > '9' then
340 { possible because trunc and the "*10.0" aren't exact :( }
342 temp[spos] := chr(ord(temp[spos]) - 10);
343 roundStr(temp,spos-1);
346 { new length of string }
349 setLength(temp,endPos);
350 { delete leading zero if we didn't need it while rounding at the }
352 if temp[2] = '0' then
354 { the rounding caused an overflow to the next power of 10 }
358 if (f<0) or (correct>(round(ln(maxexp)/ln(10)))) then
361 str(abs(correct),power);
362 if length(power)<explen-2 then
363 power:=copy(zero,1,explen-2-length(power))+power;
368 temp:=temp+'E'+power;
379 { set zeroes and dot }
382 if length(temp)<correct+dot+f-1 then
383 temp:=temp+copy(zero,1,correct+dot+f-length(temp));
384 insert ('.',temp,correct+dot);
388 correct:=abs(correct);
389 insert(copy(zero,1,correct),temp,dot-1);
390 insert ('.',temp,dot);
392 { correct length to fit precision }
394 setlength(temp,pos('.',temp)+f)
396 setLength(temp,pos('.',temp)-1);
399 if length(temp)<len then
400 s:=space(len-length(temp))+temp
406 Revision 1.1 2002/02/19 08:25:28 sasu
409 Revision 1.1 2000/07/13 06:30:48 michael
412 Revision 1.32 2000/07/07 18:03:08 jonas
413 * changed precision for extended back (otherwise strreal2.pp in
416 Revision 1.31 2000/07/06 21:09:45 florian
417 * fixed writing of comp and extended: the precision of comp was to little
418 while it was too high for extended
420 Revision 1.30 2000/03/26 11:36:28 jonas
421 + $maxfpuregisters 0 for i386 in systemh (to avoid requiring too much
422 empty FPU registers for sysstem routines
423 * fixed bug in str_real when using :x:0
424 * str_real now doesn't call exp() anymore at runtime, so it should
425 require less free FPU registers now (and be slightly faster)
427 Revision 1.29 2000/03/21 12:00:30 jonas
428 * fixed more bugs due to inexact nature of FPU
430 Revision 1.28 2000/03/17 20:20:33 jonas
431 * fixed rounding bugs with certain formatting parameters in str_real
432 * fixed tbs0218 so it compares both results only until max precision
434 Revision 1.27 2000/03/05 09:41:05 jonas
435 * fixed rounding problem when writing out single/double type vars
437 Revision 1.26 2000/03/02 07:35:57 jonas
438 * sign was not written in some cases
440 Revision 1.25 2000/02/27 14:41:25 peter
441 * removed warnings/notes
443 Revision 1.24 2000/02/26 18:53:11 jonas
444 * fix for lost precision because sometimes the correction value was
445 larger than the number to be corrected
446 * incompatibility with TP's output fixed
448 Revision 1.23 2000/02/26 15:49:40 jonas
449 + new str_real which is completely TP compatible regarding output
450 format and which should have no rounding errors anymore
452 Revision 1.22 2000/02/09 16:59:31 peter
455 Revision 1.21 2000/02/09 12:17:51 peter
456 * moved halt to system.inc
457 * syslinux doesn't use direct asm anymore
459 Revision 1.20 2000/01/17 13:00:51 jonas
460 + support for NaN's, cleaner support for Inf
462 Revision 1.19 2000/01/07 16:41:36 daniel
465 Revision 1.18 1999/11/28 23:57:23 pierre
466 * Infinite loop for infinite value problem fixed
468 Revision 1.17 1999/11/03 09:54:24 peter
469 * another fix for precision
471 Revision 1.16 1999/11/03 00:55:09 pierre
472 * problem of last commit for large d values corrected
474 Revision 1.15 1999/11/02 15:05:53 peter
475 * better precisio by dividing only once with a calculated longint
476 instead of multiple times by 10
478 Revision 1.14 1999/08/03 21:58:44 peter
479 * small speed improvements