1 |
note |
2 |
|
3 |
description: |
4 |
"Formatter for non-integral numbers" |
5 |
|
6 |
library: "Free implementation of ELKS library" |
7 |
copyright: "Copyright (c) 2005, Eiffel Software and others" |
8 |
license: "Eiffel Forum License v2 (see forum.txt)" |
9 |
names: format_double; |
10 |
date: "$Date$" |
11 |
revision: "$Revision$" |
12 |
|
13 |
class FORMAT_DOUBLE |
14 |
|
15 |
inherit |
16 |
FORMAT_INTEGER |
17 |
rename |
18 |
make as set_defaults, |
19 |
split as split_integral, |
20 |
formatted as fm_formatted |
21 |
export {NONE} |
22 |
fm_formatted |
23 |
redefine |
24 |
comma_separate, |
25 |
underscore_separate, |
26 |
remove_separator |
27 |
end |
28 |
|
29 |
DOUBLE_MATH |
30 |
export {NONE} |
31 |
all |
32 |
end |
33 |
|
34 |
create |
35 |
make |
36 |
|
37 |
feature -- Initialization |
38 |
|
39 |
make (w, d: INTEGER) |
40 |
require |
41 |
reasonable_field: w >= 1 |
42 |
reasonable_decimals: d <= w |
43 |
do |
44 |
set_defaults (w) |
45 |
decimals := d |
46 |
decimal := '.' |
47 |
trailing_zeros_shown := True |
48 |
ensure |
49 |
blank_fill: fill_character = ' ' |
50 |
show_sign_negative: show_sign_negative |
51 |
no_separator: no_separator |
52 |
width_set: width = w |
53 |
right_justified: right_justified |
54 |
leading_sign: leading_sign |
55 |
decimals_set: decimals = d |
56 |
decimal_point: decimal = '.' |
57 |
trailing_zeros_shown: trailing_zeros_shown |
58 |
end |
59 |
|
60 |
feature -- Access |
61 |
|
62 |
after_decimal_separate: BOOLEAN |
63 |
-- Use separators after the decimal? |
64 |
|
65 |
decimals: INTEGER |
66 |
-- Number of digits after the decimal point. |
67 |
|
68 |
zero_not_shown: BOOLEAN |
69 |
-- Show 0.5 as .5 or 0.5? |
70 |
|
71 |
trailing_zeros_shown: BOOLEAN |
72 |
-- Show 0.5000 as 0.5 or 0.5000? |
73 |
|
74 |
decimal: CHARACTER |
75 |
-- What is used for the decimal |
76 |
|
77 |
feature -- Status setting |
78 |
|
79 |
point_decimal |
80 |
-- Use . as the decimal point. |
81 |
do |
82 |
decimal := '.' |
83 |
ensure |
84 |
decimal = '.' |
85 |
end |
86 |
|
87 |
comma_decimal |
88 |
-- Use , as the decimal point. |
89 |
do |
90 |
decimal := ',' |
91 |
ensure |
92 |
decimal = ',' |
93 |
end |
94 |
|
95 |
set_decimals (d: INTEGER) |
96 |
-- `d' decimals to be displayed. |
97 |
require |
98 |
d <= width |
99 |
do |
100 |
decimals := d |
101 |
ensure |
102 |
decimals = d |
103 |
end |
104 |
|
105 |
separate_after_decimal |
106 |
-- Use separators after the decimal. |
107 |
do |
108 |
after_decimal_separate := True |
109 |
ensure |
110 |
after_decimal_separate |
111 |
end |
112 |
|
113 |
no_separate_after_decimal |
114 |
-- Do not use separators after the decimal. |
115 |
do |
116 |
after_decimal_separate := False |
117 |
ensure |
118 |
not after_decimal_separate |
119 |
end |
120 |
|
121 |
underscore_separate |
122 |
-- Set the separator to be underscore. |
123 |
do |
124 |
separator := '_' |
125 |
separate_after_decimal |
126 |
ensure then |
127 |
after_decimal_separate |
128 |
end |
129 |
|
130 |
comma_separate |
131 |
-- Set the separator to be comma. |
132 |
do |
133 |
separator := ',' |
134 |
separate_after_decimal |
135 |
ensure then |
136 |
after_decimal_separate |
137 |
end |
138 |
|
139 |
remove_separator |
140 |
-- Remove the separator. |
141 |
do |
142 |
separator := '%U' |
143 |
no_separate_after_decimal |
144 |
ensure then |
145 |
not after_decimal_separate |
146 |
end |
147 |
|
148 |
show_zero |
149 |
-- Show 0.5 as 0.5 . |
150 |
do |
151 |
zero_not_shown := False |
152 |
ensure |
153 |
not zero_not_shown |
154 |
end |
155 |
|
156 |
show_trailing_zeros |
157 |
-- Show 0.5000 as 0.5000. |
158 |
do |
159 |
trailing_zeros_shown := True |
160 |
ensure |
161 |
trailing_zeros_shown_set: not trailing_zeros_shown |
162 |
end |
163 |
|
164 |
hide_zero |
165 |
-- Show 0.5 as .5 . |
166 |
do |
167 |
zero_not_shown := True |
168 |
ensure |
169 |
zero_not_shown |
170 |
end |
171 |
|
172 |
hide_trailing_zeros |
173 |
-- Show 0.5000 as 0.5, and 0.0000 as 0.0. |
174 |
do |
175 |
trailing_zeros_shown := False |
176 |
ensure |
177 |
trailing_zeros_shown_set: trailing_zeros_shown |
178 |
end |
179 |
|
180 |
feature -- Conversion |
181 |
|
182 |
formatted (d: DOUBLE): STRING |
183 |
-- Format `d'. |
184 |
local |
185 |
sign: INTEGER |
186 |
integral, fraction: DOUBLE |
187 |
ints, fracs: STRING |
188 |
value: DOUBLE |
189 |
do |
190 |
value := d |
191 |
sign := 1 |
192 |
|
193 |
if d < 0 then |
194 |
sign := -1 |
195 |
value := -value |
196 |
end |
197 |
|
198 |
value := value + 5 * 10 ^(- decimals - 1) |
199 |
|
200 |
integral := floor (value) |
201 |
fraction := floor ((value - integral) * 10 ^(decimals + 1)) |
202 |
|
203 |
if not no_separator then |
204 |
ints := split_integral (integral.out) |
205 |
if after_decimal_separate then |
206 |
fracs := separate_fraction (pad_fraction (fraction)) |
207 |
else |
208 |
fracs := pad_fraction (fraction) |
209 |
end |
210 |
else |
211 |
ints := integral.out |
212 |
fracs := pad_fraction (fraction) |
213 |
end |
214 |
create Result.make (width) |
215 |
if integral /= 0 or else not zero_not_shown then |
216 |
Result.append (ints) |
217 |
end |
218 |
if not Result.has ('e') then |
219 |
Result.extend (decimal) |
220 |
if decimals > 0 then |
221 |
Result.append (fracs) |
222 |
end |
223 |
if not ignore_sign then |
224 |
Result := process_sign (Result, sign) |
225 |
end |
226 |
end |
227 |
if justification /= No_justification and then Result.count < width then |
228 |
Result := justify (Result) |
229 |
end |
230 |
ensure |
231 |
exists: Result /= Void |
232 |
correct_length: not_justified or Result.count >= width |
233 |
end |
234 |
|
235 |
feature {NONE} -- Implementation |
236 |
|
237 |
pad_fraction (f: DOUBLE): STRING |
238 |
-- Stretch or shrink `f' to length `decimals' . |
239 |
local |
240 |
i: INTEGER |
241 |
do |
242 |
Result := f.out |
243 |
Result.remove_tail (1) |
244 |
if Result.count > decimals then |
245 |
Result := Result.substring (1, decimals) |
246 |
else |
247 |
from |
248 |
until |
249 |
Result.count = decimals |
250 |
loop |
251 |
Result.precede ('0') |
252 |
end |
253 |
end |
254 |
if not trailing_zeros_shown then |
255 |
-- Remove all but one trailing zero from the fraction part |
256 |
from |
257 |
i := Result.count |
258 |
until |
259 |
i = 1 or else Result.item (i) /= '0' |
260 |
loop |
261 |
Result.remove (i) |
262 |
i := i - 1 |
263 |
end |
264 |
end |
265 |
ensure |
266 |
Result.count = decimals |
267 |
end |
268 |
|
269 |
separate_fraction (s: STRING): STRING |
270 |
-- Apply separators to the fraction. |
271 |
require |
272 |
efficiency: separator /= '%U' |
273 |
local |
274 |
count, sep_length: INTEGER |
275 |
do |
276 |
from |
277 |
count := 1 |
278 |
create Result.make (width) |
279 |
until |
280 |
count > s.count - 3 |
281 |
loop |
282 |
from |
283 |
sep_length := 0 |
284 |
until |
285 |
sep_length = 3 |
286 |
loop |
287 |
Result.extend (s.item (count)) |
288 |
count := count + 1 |
289 |
sep_length := sep_length + 1 |
290 |
end |
291 |
Result.extend (separator) |
292 |
end |
293 |
from |
294 |
until |
295 |
count > s.count |
296 |
loop |
297 |
Result.extend (s.item (count)) |
298 |
count := count + 1 |
299 |
end |
300 |
end |
301 |
|
302 |
invariant |
303 |
separate_all: no_separator implies not after_decimal_separate |
304 |
|
305 |
end |