OSDN Git Service

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