OSDN Git Service

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