OSDN Git Service

2008-04-08 Hristian Kirtchev <kirtchev@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-imgdec.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT RUN-TIME COMPONENTS                         --
4 --                                                                          --
5 --                        S Y S T E M . I M G _ D E C                       --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1992-2007, Free Software Foundation, Inc.         --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with System.Img_Int; use System.Img_Int;
35
36 package body System.Img_Dec is
37
38    -------------------
39    -- Image_Decimal --
40    -------------------
41
42    procedure Image_Decimal
43      (V     : Integer;
44       S     : in out String;
45       P     : out Natural;
46       Scale : Integer)
47    is
48       pragma Assert (S'First = 1);
49
50    begin
51       --  Add space at start for non-negative numbers
52
53       if V >= 0 then
54          S (1) := ' ';
55          P := 1;
56       else
57          P := 0;
58       end if;
59
60       Set_Image_Decimal (V, S, P, Scale, 1, Integer'Max (1, Scale), 0);
61    end Image_Decimal;
62
63    ------------------------
64    -- Set_Decimal_Digits --
65    ------------------------
66
67    procedure Set_Decimal_Digits
68      (Digs  : in out String;
69       NDigs : Natural;
70       S     : out String;
71       P     : in out Natural;
72       Scale : Integer;
73       Fore  : Natural;
74       Aft   : Natural;
75       Exp   : Natural)
76    is
77       Minus : constant Boolean := (Digs (Digs'First) = '-');
78       --  Set True if input is negative
79
80       Zero : Boolean := (Digs (Digs'First + 1) = '0');
81       --  Set True if input is exactly zero (only case when a leading zero
82       --  is permitted in the input string given to this procedure). This
83       --  flag can get set later if rounding causes the value to become zero.
84
85       FD : Natural := 2;
86       --  First digit position of digits remaining to be processed
87
88       LD : Natural := NDigs;
89       --  Last digit position of digits remaining to be processed
90
91       ND : Natural := NDigs - 1;
92       --  Number of digits remaining to be processed (LD - FD + 1)
93
94       Digits_Before_Point : Integer := ND - Scale;
95       --  Number of digits before decimal point in the input value. This
96       --  value can be negative if the input value is less than 0.1, so
97       --  it is an indication of the current exponent. Digits_Before_Point
98       --  is adjusted if the rounding step generates an extra digit.
99
100       Digits_After_Point : constant Natural := Integer'Max (1, Aft);
101       --  Digit positions after decimal point in result string
102
103       Expon : Integer;
104       --  Integer value of exponent
105
106       procedure Round (N : Natural);
107       --  Round the number in Digs. N is the position of the last digit to be
108       --  retained in the rounded position (rounding is based on Digs (N + 1)
109       --  FD, LD, ND are reset as necessary if required. Note that if the
110       --  result value rounds up (e.g. 9.99 => 10.0), an extra digit can be
111       --  placed in the sign position as a result of the rounding, this is
112       --  the case in which FD is adjusted.
113
114       procedure Set (C : Character);
115       pragma Inline (Set);
116       --  Sets character C in output buffer
117
118       procedure Set_Blanks_And_Sign (N : Integer);
119       --  Sets leading blanks and minus sign if needed. N is the number of
120       --  positions to be filled (a minus sign is output even if N is zero
121       --  or negative, For a positive value, if N is non-positive, then
122       --  a leading blank is filled.
123
124       procedure Set_Digits (S, E : Natural);
125       pragma Inline (Set_Digits);
126       --  Set digits S through E from Digs, no effect if S > E
127
128       procedure Set_Zeroes (N : Integer);
129       pragma Inline (Set_Zeroes);
130       --  Set N zeroes, no effect if N is negative
131
132       procedure Round (N : Natural) is
133          D : Character;
134
135       begin
136          --  Nothing to do if rounding at or past last digit
137
138          if N >= LD then
139             return;
140
141          --  Cases of rounding before the initial digit
142
143          elsif N < FD then
144
145             --  The result is zero, unless we are rounding just before
146             --  the first digit, and the first digit is five or more.
147
148             if N = 1 and then Digs (Digs'First + 1) >= '5' then
149                Digs (Digs'First) := '1';
150             else
151                Digs (Digs'First) := '0';
152                Zero := True;
153             end if;
154
155             Digits_Before_Point := Digits_Before_Point + 1;
156             FD := 1;
157             LD := 1;
158             ND := 1;
159
160          --  Normal case of rounding an existing digit
161
162          else
163             LD := N;
164             ND := LD - 1;
165
166             if Digs (N + 1) >= '5' then
167                for J in reverse 2 .. N loop
168                   D := Character'Succ (Digs (J));
169
170                   if D <= '9' then
171                      Digs (J) := D;
172                      return;
173                   else
174                      Digs (J) := '0';
175                   end if;
176                end loop;
177
178                --  Here the rounding overflows into the sign position. That's
179                --  OK, because we already captured the value of the sign and
180                --  we are in any case destroying the value in the Digs buffer
181
182                Digs (Digs'First) := '1';
183                FD := 1;
184                ND := ND + 1;
185                Digits_Before_Point := Digits_Before_Point + 1;
186             end if;
187          end if;
188       end Round;
189
190       ---------
191       -- Set --
192       ---------
193
194       procedure Set (C : Character) is
195       begin
196          P := P + 1;
197          S (P) := C;
198       end Set;
199
200       -------------------------
201       -- Set_Blanks_And_Sign --
202       -------------------------
203
204       procedure Set_Blanks_And_Sign (N : Integer) is
205          W : Integer := N;
206
207       begin
208          if Minus then
209             W := W - 1;
210
211             for J in 1 .. W loop
212                Set (' ');
213             end loop;
214
215             Set ('-');
216
217          else
218             for J in 1 .. W loop
219                Set (' ');
220             end loop;
221          end if;
222       end Set_Blanks_And_Sign;
223
224       ----------------
225       -- Set_Digits --
226       ----------------
227
228       procedure Set_Digits (S, E : Natural) is
229       begin
230          for J in S .. E loop
231             Set (Digs (J));
232          end loop;
233       end Set_Digits;
234
235       ----------------
236       -- Set_Zeroes --
237       ----------------
238
239       procedure Set_Zeroes (N : Integer) is
240       begin
241          for J in 1 .. N loop
242             Set ('0');
243          end loop;
244       end Set_Zeroes;
245
246    --  Start of processing for Set_Decimal_Digits
247
248    begin
249       --  Case of exponent given
250
251       if Exp > 0 then
252          Set_Blanks_And_Sign (Fore - 1);
253          Round (Aft + 2);
254          Set (Digs (FD));
255          FD := FD + 1;
256          ND := ND - 1;
257          Set ('.');
258
259          if ND >= Digits_After_Point then
260             Set_Digits (FD, FD + Digits_After_Point - 1);
261
262          else
263             Set_Digits (FD, LD);
264             Set_Zeroes (Digits_After_Point - ND);
265          end if;
266
267          --  Calculate exponent. The number of digits before the decimal point
268          --  in the input is Digits_Before_Point, and the number of digits
269          --  before the decimal point in the output is 1, so we can get the
270          --  exponent as the difference between these two values. The one
271          --  exception is for the value zero, which by convention has an
272          --  exponent of +0.
273
274          if Zero then
275             Expon := 0;
276          else
277             Expon := Digits_Before_Point - 1;
278          end if;
279
280          Set ('E');
281          ND := 0;
282
283          if Expon >= 0 then
284             Set ('+');
285             Set_Image_Integer (Expon, Digs, ND);
286          else
287             Set ('-');
288             Set_Image_Integer (-Expon, Digs, ND);
289          end if;
290
291          Set_Zeroes (Exp - ND - 1);
292          Set_Digits (1, ND);
293          return;
294
295       --  Case of no exponent given. To make these cases clear, we use
296       --  examples. For all the examples, we assume Fore = 2, Aft = 3.
297       --  A P in the example input string is an implied zero position,
298       --  not included in the input string.
299
300       else
301          --  Round at correct position
302          --    Input: 4PP      => unchanged
303          --    Input: 400.03   => unchanged
304          --    Input  3.4567   => 3.457
305          --    Input: 9.9999   => 10.000
306          --    Input: 0.PPP5   => 0.001
307          --    Input: 0.PPP4   => 0
308          --    Input: 0.00003  => 0
309
310          Round (LD - (Scale - Digits_After_Point));
311
312          --  No digits before point in input
313          --    Input: .123   Output: 0.123
314          --    Input: .PP3   Output: 0.003
315
316          if Digits_Before_Point <= 0 then
317             Set_Blanks_And_Sign (Fore - 1);
318             Set ('0');
319             Set ('.');
320
321             Set_Zeroes (Digits_After_Point - ND);
322             Set_Digits (FD, LD);
323
324          --  At least one digit before point in input
325
326          else
327             Set_Blanks_And_Sign (Fore - Digits_Before_Point);
328
329             --  Less digits in input than are needed before point
330             --    Input: 1PP  Output: 100.000
331
332             if ND < Digits_Before_Point then
333                Set_Digits (FD, LD);
334                Set_Zeroes (Digits_Before_Point - ND);
335                Set ('.');
336                Set_Zeroes (Digits_After_Point);
337
338             --  Input has full amount of digits before decimal point
339
340             else
341                Set_Digits (FD, FD + Digits_Before_Point - 1);
342                Set ('.');
343                Set_Digits (FD + Digits_Before_Point, LD);
344                Set_Zeroes (Digits_After_Point - (ND - Digits_Before_Point));
345             end if;
346          end if;
347       end if;
348    end Set_Decimal_Digits;
349
350    -----------------------
351    -- Set_Image_Decimal --
352    -----------------------
353
354    procedure Set_Image_Decimal
355      (V     : Integer;
356       S     : in out String;
357       P     : in out Natural;
358       Scale : Integer;
359       Fore  : Natural;
360       Aft   : Natural;
361       Exp   : Natural)
362    is
363       Digs : String := Integer'Image (V);
364       --  Sign and digits of decimal value
365
366    begin
367       Set_Decimal_Digits (Digs, Digs'Length, S, P, Scale, Fore, Aft, Exp);
368    end Set_Image_Decimal;
369
370 end System.Img_Dec;