OSDN Git Service

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