OSDN Git Service

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