OSDN Git Service

Licensing changes to GPLv3 resp. GPLv3 with GCC Runtime Exception.
[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-2009, 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 3,  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.                                     --
17 --                                                                          --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception,   --
20 -- version 3.1, as published by the Free Software Foundation.               --
21 --                                                                          --
22 -- You should have received a copy of the GNU General Public License and    --
23 -- a copy of the GCC Runtime Library Exception along with this program;     --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25 -- <http://www.gnu.org/licenses/>.                                          --
26 --                                                                          --
27 -- GNAT was originally developed  by the GNAT team at  New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
29 --                                                                          --
30 ------------------------------------------------------------------------------
31
32 with System.Img_Int; use System.Img_Int;
33
34 package body System.Img_Dec is
35
36    -------------------
37    -- Image_Decimal --
38    -------------------
39
40    procedure Image_Decimal
41      (V     : Integer;
42       S     : in out String;
43       P     : out Natural;
44       Scale : Integer)
45    is
46       pragma Assert (S'First = 1);
47
48    begin
49       --  Add space at start for non-negative numbers
50
51       if V >= 0 then
52          S (1) := ' ';
53          P := 1;
54       else
55          P := 0;
56       end if;
57
58       Set_Image_Decimal (V, S, P, Scale, 1, Integer'Max (1, Scale), 0);
59    end Image_Decimal;
60
61    ------------------------
62    -- Set_Decimal_Digits --
63    ------------------------
64
65    procedure Set_Decimal_Digits
66      (Digs  : in out String;
67       NDigs : Natural;
68       S     : out String;
69       P     : in out Natural;
70       Scale : Integer;
71       Fore  : Natural;
72       Aft   : Natural;
73       Exp   : Natural)
74    is
75       Minus : constant Boolean := (Digs (Digs'First) = '-');
76       --  Set True if input is negative
77
78       Zero : Boolean := (Digs (Digs'First + 1) = '0');
79       --  Set True if input is exactly zero (only case when a leading zero
80       --  is permitted in the input string given to this procedure). This
81       --  flag can get set later if rounding causes the value to become zero.
82
83       FD : Natural := 2;
84       --  First digit position of digits remaining to be processed
85
86       LD : Natural := NDigs;
87       --  Last digit position of digits remaining to be processed
88
89       ND : Natural := NDigs - 1;
90       --  Number of digits remaining to be processed (LD - FD + 1)
91
92       Digits_Before_Point : Integer := ND - Scale;
93       --  Number of digits before decimal point in the input value. This
94       --  value can be negative if the input value is less than 0.1, so
95       --  it is an indication of the current exponent. Digits_Before_Point
96       --  is adjusted if the rounding step generates an extra digit.
97
98       Digits_After_Point : constant Natural := Integer'Max (1, Aft);
99       --  Digit positions after decimal point in result string
100
101       Expon : Integer;
102       --  Integer value of exponent
103
104       procedure Round (N : Natural);
105       --  Round the number in Digs. N is the position of the last digit to be
106       --  retained in the rounded position (rounding is based on Digs (N + 1)
107       --  FD, LD, ND are reset as necessary if required. Note that if the
108       --  result value rounds up (e.g. 9.99 => 10.0), an extra digit can be
109       --  placed in the sign position as a result of the rounding, this is
110       --  the case in which FD is adjusted.
111
112       procedure Set (C : Character);
113       pragma Inline (Set);
114       --  Sets character C in output buffer
115
116       procedure Set_Blanks_And_Sign (N : Integer);
117       --  Sets leading blanks and minus sign if needed. N is the number of
118       --  positions to be filled (a minus sign is output even if N is zero
119       --  or negative, For a positive value, if N is non-positive, then
120       --  a leading blank is filled.
121
122       procedure Set_Digits (S, E : Natural);
123       pragma Inline (Set_Digits);
124       --  Set digits S through E from Digs, no effect if S > E
125
126       procedure Set_Zeroes (N : Integer);
127       pragma Inline (Set_Zeroes);
128       --  Set N zeroes, no effect if N is negative
129
130       -----------
131       -- Round --
132       -----------
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 (Digs'First + 1) >= '5' then
151                Digs (Digs'First) := '1';
152             else
153                Digs (Digs'First) := '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 (Digs'First) := '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       ---------
193       -- Set --
194       ---------
195
196       procedure Set (C : Character) is
197       begin
198          P := P + 1;
199          S (P) := C;
200       end Set;
201
202       -------------------------
203       -- Set_Blanks_And_Sign --
204       -------------------------
205
206       procedure Set_Blanks_And_Sign (N : Integer) is
207          W : Integer := N;
208
209       begin
210          if Minus then
211             W := W - 1;
212
213             for J in 1 .. W loop
214                Set (' ');
215             end loop;
216
217             Set ('-');
218
219          else
220             for J in 1 .. W loop
221                Set (' ');
222             end loop;
223          end if;
224       end Set_Blanks_And_Sign;
225
226       ----------------
227       -- Set_Digits --
228       ----------------
229
230       procedure Set_Digits (S, E : Natural) is
231       begin
232          for J in S .. E loop
233             Set (Digs (J));
234          end loop;
235       end Set_Digits;
236
237       ----------------
238       -- Set_Zeroes --
239       ----------------
240
241       procedure Set_Zeroes (N : Integer) is
242       begin
243          for J in 1 .. N loop
244             Set ('0');
245          end loop;
246       end Set_Zeroes;
247
248    --  Start of processing for Set_Decimal_Digits
249
250    begin
251       --  Case of exponent given
252
253       if Exp > 0 then
254          Set_Blanks_And_Sign (Fore - 1);
255          Round (Digits_After_Point + 2);
256          Set (Digs (FD));
257          FD := FD + 1;
258          ND := ND - 1;
259          Set ('.');
260
261          if ND >= Digits_After_Point then
262             Set_Digits (FD, FD + Digits_After_Point - 1);
263          else
264             Set_Digits (FD, LD);
265             Set_Zeroes (Digits_After_Point - ND);
266          end if;
267
268          --  Calculate exponent. The number of digits before the decimal point
269          --  in the input is Digits_Before_Point, and the number of digits
270          --  before the decimal point in the output is 1, so we can get the
271          --  exponent as the difference between these two values. The one
272          --  exception is for the value zero, which by convention has an
273          --  exponent of +0.
274
275          if Zero then
276             Expon := 0;
277          else
278             Expon := Digits_Before_Point - 1;
279          end if;
280
281          Set ('E');
282          ND := 0;
283
284          if Expon >= 0 then
285             Set ('+');
286             Set_Image_Integer (Expon, Digs, ND);
287          else
288             Set ('-');
289             Set_Image_Integer (-Expon, Digs, ND);
290          end if;
291
292          Set_Zeroes (Exp - ND - 1);
293          Set_Digits (1, ND);
294          return;
295
296       --  Case of no exponent given. To make these cases clear, we use
297       --  examples. For all the examples, we assume Fore = 2, Aft = 3.
298       --  A P in the example input string is an implied zero position,
299       --  not included in the input string.
300
301       else
302          --  Round at correct position
303          --    Input: 4PP      => unchanged
304          --    Input: 400.03   => unchanged
305          --    Input  3.4567   => 3.457
306          --    Input: 9.9999   => 10.000
307          --    Input: 0.PPP5   => 0.001
308          --    Input: 0.PPP4   => 0
309          --    Input: 0.00003  => 0
310
311          Round (LD - (Scale - Digits_After_Point));
312
313          --  No digits before point in input
314          --    Input: .123   Output: 0.123
315          --    Input: .PP3   Output: 0.003
316
317          if Digits_Before_Point <= 0 then
318             Set_Blanks_And_Sign (Fore - 1);
319             Set ('0');
320             Set ('.');
321             Set_Zeroes (-Digits_Before_Point);
322             Set_Digits (FD, LD);
323             Set_Zeroes (Digits_After_Point - Scale);
324
325          --  At least one digit before point in input
326
327          else
328             --  Less digits in input than are needed before point
329             --    Input: 1PP  Output: 100.000
330
331             if ND < Digits_Before_Point then
332
333                --  Special case, if the input is the single digit 0, then we
334                --  do not want 000.000, but instead 0.000.
335
336                if ND = 1 and then Digs (FD) = '0' then
337                   Set_Blanks_And_Sign (Fore - 1);
338                   Set ('0');
339
340                --  Normal case where we need to output scaling zeroes
341
342                else
343                   Set_Blanks_And_Sign (Fore - Digits_Before_Point);
344                   Set_Digits (FD, LD);
345                   Set_Zeroes (Digits_Before_Point - ND);
346                end if;
347
348                --  Set period and zeroes after the period
349
350                Set ('.');
351                Set_Zeroes (Digits_After_Point);
352
353             --  Input has full amount of digits before decimal point
354
355             else
356                Set_Blanks_And_Sign (Fore - Digits_Before_Point);
357                Set_Digits (FD, FD + Digits_Before_Point - 1);
358                Set ('.');
359                Set_Digits (FD + Digits_Before_Point, LD);
360                Set_Zeroes (Digits_After_Point - (ND - Digits_Before_Point));
361             end if;
362          end if;
363       end if;
364    end Set_Decimal_Digits;
365
366    -----------------------
367    -- Set_Image_Decimal --
368    -----------------------
369
370    procedure Set_Image_Decimal
371      (V     : Integer;
372       S     : in out String;
373       P     : in out Natural;
374       Scale : Integer;
375       Fore  : Natural;
376       Aft   : Natural;
377       Exp   : Natural)
378    is
379       Digs : String := Integer'Image (V);
380       --  Sign and digits of decimal value
381
382    begin
383       Set_Decimal_Digits (Digs, Digs'Length, S, P, Scale, Fore, Aft, Exp);
384    end Set_Image_Decimal;
385
386 end System.Img_Dec;