1 // A version of Delphi that supports in-line assembly language
2 // is required (Enterprise or maybe Professional)
3
4 // Convert Base-Ten whole Number to string, with minimal string-
5 // space usage, faster than Format(), and Thread-Safe.
6 // Width and Precision lengths can be specified or zero (ignored
7 // if zero); WIDTH IS A FIXED FIELD SIZE; padding
8 // spaces are added if Number is smaller; use negative Width to
9 // left-justify Number; a too-long Number has its
10 // Least Significant Digits truncated; a minus sign can cost
11 // another Digit (YOU must ensure Width is big enough!)
12 // PRECISION IS A MINIMUM NUMBER SIZE; takes precedence over
13 // Width if Width is smaller; adds leading zeros if Number
14 // is smaller; a negative Precision causes truncation of Most
15 // Significant Digits if Number is bigger than Width
16 function Num2Str(w, p, n: Integer): string;
17 var
18 i, j, k: Integer;
19 begin
20 // Result := Format('%*.*d', [w, p, n]); // MOST of what this
21 // function does can
22 // be handled by this
23 // one line. Usually
24 // that one line will
25 // be adequate. But
26 // the innards of
27 // Format() are MUCH
28 // more complex than
29 // below, and thus
30 // are rather slower.
31 i := 0; // initialize a digits-on-stack counter
32 j := 0; // future flag
33 k := 0; // future length-adjustment value
34 asm
35 MOV EAX, n // get number we want to convert
36 // (low-order part of 64-bit dividend)
37 or EAX, EAX // set flags (MOV affects no flags)
38 JGE @@skp1 // skip if a positive number (Greater than
39 // or Equal to zero)
40 INC j // indicate later need for a minus sign
41 INC k // adustment=1: a negative number, of
42 // course, consists of at least a minus
43 // sign and a digit
44 NEG EAX // negate a negative (make positive)
45 @@skp1: MOV ECX, 10 // set a divisor: ten
46 @@mor: MOV EDX, 0 // erase high-order portion of 64-bit
47 // dividend
48 div ECX // divide 32 bits into 64, quotient in
49 // EAX, remainder in EDX
50 PUSH EDX // save the remainder on the stack
51 // (last-to-1st digit of future string)
52 INC i // increase count of digits (quotient
53 // always has at least one)
54 or EAX, EAX // set flags
55 JNZ @@mor // if any digits not placed one-at-a-time
56 // on stack, loop to do another
57 MOV EAX, w // get Width
58 or EAX, EAX // set flags
59 JZ @@ign1 // ignore if zero
60 JG @@pos1 // already positive?
61 NEG EAX // negate a negative (make positive)
62 @@pos1: SUB EAX, i // compute a difference in lengths
63 MOV k, EAX // set length-adjustment (accommodates
64 // extra space or truncation, minus sign
65 // INCLUDED)
66 @@ign1: MOV ECX, i // get length of Number
67 MOV EAX, p // get Precision
68 MOV EDX, EAX // copy it
69 or EAX, EAX // set flags...
70 JZ @@ok1 // OK to ignore if no Precision
71 JG @@pos2 // already positive?
72 NEG EAX // convert negative into positive
73 MOV EDX, EAX // copy Precision again
74 @@pos2: SUB EAX, ECX // compute a difference
75 CMP EAX, k // compare Precision-Number difference
76 // with Width-Number difference
77 JLE @@ok1 // if Precision is Less than or Equal to
78 // Width, OK!
79 MOV k, EAX // replace prior difference with one that
80 // takes precedence
81 @@ok1: PUSH EDX // save absolute value of (modified)
82 // Precision on the stack
83 ADD ECX, k // i + k
84 PUSH ECX // save it on the stack
85 end;
86 Result := StringOfChar(' ', (i + k)); // create string of
87 // correct final
88 // length (Format()
89 // concatenates)
90 asm
91 POP ECX // recover last value PUSHed onto stack:
92 // i + k
93 SUB ECX, j // now account for possible minus sign
94 MOV k, ECX // save that quantity of SPACE for
95 // allowed digits
96 MOV EAX, i // get length of Number
97 MOV EDX, [ESP] // get absolute value of precision
98 // (keep it on stack)
99 CMP EAX, EDX // compare two possibly-different
100 // lengths
101 JAE @@ok2 // if Above or Equal, length of Number
102 // is OK
103 MOV EAX, EDX // precision is longer than Number, so
104 // leading zeros will be added
105 @@ok2: CMP EAX, ECX // compare two possibly-different
106 // lengths
107 JA @@trc2 // if Number is Above i+k, truncation
108 // will happen
109 MOV k, EAX // set k to the Number's length
110 // (it fits!)
111 JE @@trc2 // go start at string element 0 if
112 // Number exactly fits in string
113 @@trc1: MOV EAX, w // get width
114 or EAX, EAX // set flags (test for left-justify)
115 JL @@trc2 // like truncation, left-justify starts
116 // at element 0 (see jump destination)
117 SUB ECX, i // get difference between string's and
118 // Number's lengths
119 MOV EAX, ECX // save possible index to
120 // (right-justified) first
121 // string-element we'll access
122 JA @@ok3 // OK if string actually long enough
123 // even for a negative Number
124 @@trc2: MOV EAX, 0 // initialize a string-index counter
125 // (in Assembly Language,
126 // 1st element is 0)
127 @@ok3: MOV EDX, Result // prepare to dump data into Result
128 // string
129 MOV EDX, [EDX] // finish getting the address of that
130 // string
131 MOV ECX, j // get minus-sign indicator
132 or ECX, ECX // test it
133 JZ @@skp2 // skip if positive
134 MOV byte ptr [EDX + EAX], '-' // put the minus sign into
135 // the string
136 INC EAX // increment the index
137 @@skp2: MOV ECX, k // quantity of digits we'll be putting
138 // into string, even if truncating
139 SUB ECX, i // compute quantity of digits we'll be
140 // truncating, as a negative value
141 MOV w, ECX // save as a Most-Sig-Digits truncation
142 // counter
143 MOV ECX, p // get Precedence
144 or ECX, ECX // set flags to test for Most/Least
145 // Sig-Digit truncation
146 JL @@skp3 // a negative value means we'll truncate
147 // Most Significant Digits
148 MOV w, ECX // replace the negative value in w with
149 // a positive value
150 @@skp3: POP ECX // recover absolute value of Precision
151 // from stack
152 SUB ECX, j // remove previously-included space for
153 // minus sign
154 JZ @@loop // no Precision specified (no leading
155 // zeros or Most-Sig-Digits truncation)
156 SUB ECX, i // compute number of possible leading
157 // zeros for Number
158 JLE @@loop // skip if none
159 @@zero: MOV byte ptr [EDX + EAX], '0' // put a leading zero into
160 // the string
161 INC EAX // increment the index
162 DEC ECX // decrement number of leading zeros
163 JNE @@zero // loop til necessary quantity are
164 // embedded in string
165 JMP @@loop // skip the DEC (faster than
166 // precompensating for it with INC)
167 @@sync: DEC i // countdown an about-to-be-removed
168 // digit from the stack
169 @@loop: POP ECX // get a digit (first to last) off the
170 // stack
171 INC w // count possible Most-Sig-Digit
172 // truncation
173 JLE @@sync // if still Less than or Equal to zero,
174 // we ARE truncating Most-Sig-Digits
175 DEC k // countdown quantity of allowed digits,
176 // before Least-Sig-Digits truncation
177 JL @@skp4 // skip when this countdown passes zero
178 ADD CL, '0' // convert from numeral to ASCII
179 // character
180 MOV byte ptr [EDX + EAX], CL // put the character into
181 // the Result string
182 INC EAX // increment the string-index-counter
183 @@skp4: DEC i // decrement the remaining quantity of
184 // digits on the stack
185 JNE @@loop // if Not Equal to zero, loop to convert
186 // those remaining digits
187 end;
188 end;
|