1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- S Y S T E M . I M G _ U T I L --
9 -- Copyright (C) 2020-2024, Free Software Foundation, Inc. --
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. --
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. --
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/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with System
.Img_Uns
; use System
.Img_Uns
;
34 package body System
.Img_Util
is
36 ------------------------
37 -- Set_Decimal_Digits --
38 ------------------------
40 pragma Annotate
(Gnatcheck
, Exempt_On
, "Unassigned_OUT_Parameters",
41 "the OUT parameter is assigned by component");
42 procedure Set_Decimal_Digits
43 (Digs
: in out String;
52 pragma Annotate
(Gnatcheck
, Exempt_Off
, "Unassigned_OUT_Parameters");
54 pragma Assert
(NDigs
>= 1);
55 pragma Assert
(Digs
'First = 1);
56 pragma Assert
(Digs
'First < Digs
'Last);
58 Minus
: constant Boolean := (Digs
(Digs
'First) = '-');
59 -- Set True if input is negative
61 Zero
: Boolean := (Digs
(Digs
'First + 1) = '0');
62 -- Set True if input is exactly zero (only case when a leading zero
63 -- is permitted in the input string given to this procedure). This
64 -- flag can get set later if rounding causes the value to become zero.
67 -- First digit position of digits remaining to be processed
69 LD
: Natural := NDigs
;
70 -- Last digit position of digits remaining to be processed
72 ND
: Natural := NDigs
- 1;
73 -- Number of digits remaining to be processed (LD - FD + 1)
75 Digits_Before_Point
: Integer := ND
- Scale
;
76 -- Number of digits before decimal point in the input value. This
77 -- value can be negative if the input value is less than 0.1, so
78 -- it is an indication of the current exponent. Digits_Before_Point
79 -- is adjusted if the rounding step generates an extra digit.
81 Digits_After_Point
: constant Natural := Integer'Max (1, Aft
);
82 -- Digit positions after decimal point in result string
85 -- Integer value of exponent
87 procedure Round
(N
: Integer);
88 -- Round the number in Digs. N is the position of the last digit to be
89 -- retained in the rounded position (rounding is based on Digs (N + 1)
90 -- FD, LD, ND are reset as necessary if required. Note that if the
91 -- result value rounds up (e.g. 9.99 => 10.0), an extra digit can be
92 -- placed in the sign position as a result of the rounding, this is
93 -- the case in which FD is adjusted. The call to Round has no effect
94 -- if N is outside the range FD .. LD.
96 procedure Set
(C
: Character);
98 -- Sets character C in output buffer
100 procedure Set_Blanks_And_Sign
(N
: Integer);
101 -- Sets leading blanks and minus sign if needed. N is the number of
102 -- positions to be filled (a minus sign is output even if N is zero
103 -- or negative, but for a positive value, if N is non-positive, then
104 -- the call has no effect).
106 procedure Set_Digits
(S
, E
: Natural);
107 pragma Inline
(Set_Digits
);
108 -- Set digits S through E from Digs, no effect if S > E
110 procedure Set_Zeroes
(N
: Integer);
111 pragma Inline
(Set_Zeroes
);
112 -- Set N zeroes, no effect if N is negative
118 procedure Round
(N
: Integer) is
121 pragma Assert
(NDigs
>= 1);
122 pragma Assert
(Digs
'First = 1);
123 pragma Assert
(Digs
'First < Digs
'Last);
126 pragma Annotate
(Gnatcheck
, Exempt_On
, "Improper_Returns",
127 "early returns for performance");
129 -- Nothing to do if rounding past the last digit we have
134 -- Cases of rounding before the initial digit
138 -- The result is zero, unless we are rounding just before
139 -- the first digit, and the first digit is five or more.
141 if N
= 1 and then Digs
(Digs
'First + 1) >= '5' then
142 Digs
(Digs
'First) := '1';
144 Digs
(Digs
'First) := '0';
148 Digits_Before_Point
:= Digits_Before_Point
+ 1;
153 -- Normal case of rounding an existing digit
157 pragma Assert
(LD
>= 1);
158 -- In this case, we have N < LD and N >= FD. FD is a Natural,
159 -- So we can conclude, LD >= 1
161 pragma Assert
(N
+ 1 <= Digs
'Last);
163 if Digs
(N
+ 1) >= '5' then
164 for J
in reverse Digs
'First + 1 .. Digs
'First + N
- 1 loop
165 pragma Assert
(Digs
(J
) in '0' .. '9' |
' ' |
'-');
166 -- Because it is a decimal image, we can assume that
167 -- it can only contain these characters.
168 D
:= Character'Succ (Digs
(J
));
178 -- Here the rounding overflows into the sign position. That's
179 -- OK, because we already captured the value of the sign and
180 -- we are in any case destroying the value in the Digs buffer
182 Digs
(Digs
'First) := '1';
185 Digits_Before_Point
:= Digits_Before_Point
+ 1;
189 pragma Annotate
(Gnatcheck
, Exempt_Off
, "Improper_Returns");
196 procedure Set
(C
: Character) is
198 pragma Assert
(P
>= (S
'First - 1) and P
< S
'Last and
200 -- No check is done as documented in the header : updating P to
201 -- point to the last character stored, the caller promises that the
202 -- buffer is large enough and no check is made for this.
203 -- Constraint_Error will not necessarily be raised if this
204 -- requirement is violated, since it is perfectly valid to compile
205 -- this unit with checks off.
210 -------------------------
211 -- Set_Blanks_And_Sign --
212 -------------------------
214 procedure Set_Blanks_And_Sign
(N
: Integer) is
217 for J
in 1 .. N
- 1 loop
228 end Set_Blanks_And_Sign
;
234 procedure Set_Digits
(S
, E
: Natural) is
236 pragma Assert
(S
>= Digs
'First and E
<= Digs
'Last);
237 -- S and E should be in the Digs array range
247 procedure Set_Zeroes
(N
: Integer) is
254 -- Start of processing for Set_Decimal_Digits
257 pragma Annotate
(Gnatcheck
, Exempt_On
, "Improper_Returns",
258 "early returns for performance");
260 -- Case of exponent given
263 Set_Blanks_And_Sign
(Fore
- 1);
264 Round
(Digits_After_Point
+ 2);
268 pragma Assert
(ND
>= 1);
272 if ND
>= Digits_After_Point
then
273 Set_Digits
(FD
, FD
+ Digits_After_Point
- 1);
276 Set_Zeroes
(Digits_After_Point
- ND
);
279 -- Calculate exponent. The number of digits before the decimal point
280 -- in the input is Digits_Before_Point, and the number of digits
281 -- before the decimal point in the output is 1, so we can get the
282 -- exponent as the difference between these two values. The one
283 -- exception is for the value zero, which by convention has an
286 Expon
:= (if Zero
then 0 else Digits_Before_Point
- 1);
293 Set_Image_Unsigned
(Unsigned
(Expon
), Digs
, ND
);
296 Set_Image_Unsigned
(Unsigned
(-Expon
), Digs
, ND
);
299 Set_Zeroes
(Exp
- ND
- 1);
303 -- Case of no exponent given. To make these cases clear, we use
304 -- examples. For all the examples, we assume Fore = 2, Aft = 3.
305 -- A P in the example input string is an implied zero position,
306 -- not included in the input string.
309 -- Round at correct position
310 -- Input: 4PP => unchanged
311 -- Input: 400.03 => unchanged
312 -- Input 3.4567 => 3.457
313 -- Input: 9.9999 => 10.000
314 -- Input: 0.PPP5 => 0.001
315 -- Input: 0.PPP4 => 0
316 -- Input: 0.00003 => 0
318 Round
(LD
- (Scale
- Digits_After_Point
));
320 -- No digits before point in input
321 -- Input: .123 Output: 0.123
322 -- Input: .PP3 Output: 0.003
324 if Digits_Before_Point
<= 0 then
325 Set_Blanks_And_Sign
(Fore
- 1);
330 DA
: Natural := Digits_After_Point
;
331 -- Digits remaining to output after point
333 LZ
: constant Integer := Integer'Min (DA
, -Digits_Before_Point
);
334 -- Number of leading zeroes after point. Note: there used to be
335 -- a Max of this result with zero, but that's redundant, since
336 -- we know DA is positive, and because of the test above, we
337 -- know that -Digits_Before_Point >= 0.
345 -- Note: it is definitely possible for the above condition
346 -- to be True, for example:
348 -- V => 1234, Scale => 5, Fore => 0, After => 1, Exp => 0
350 -- but in this case DA = 0, ND = 1, FD = 1, FD + DA-1 = 0
351 -- so the arguments in the call are (1, 0) meaning that no
352 -- digits are output.
354 -- No obvious example exists where the following call to
355 -- Set_Digits actually outputs some digits, but we lack a
356 -- proof that no such example exists.
358 -- So it is safer to retain this call, even though as a
359 -- result it is hard (or perhaps impossible) to create a
360 -- coverage test for the inlined code of the call.
362 Set_Digits
(FD
, FD
+ DA
- 1);
366 Set_Zeroes
(DA
- ND
);
370 -- At least one digit before point in input
373 -- Less digits in input than are needed before point
374 -- Input: 1PP Output: 100.000
376 if ND
< Digits_Before_Point
then
378 -- Special case, if the input is the single digit 0, then we
379 -- do not want 000.000, but instead 0.000.
381 if ND
= 1 and then Digs
(FD
) = '0' then
382 Set_Blanks_And_Sign
(Fore
- 1);
385 -- Normal case where we need to output scaling zeroes
388 Set_Blanks_And_Sign
(Fore
- Digits_Before_Point
);
390 Set_Zeroes
(Digits_Before_Point
- ND
);
393 -- Set period and zeroes after the period
396 Set_Zeroes
(Digits_After_Point
);
398 -- Input has full amount of digits before decimal point
401 Set_Blanks_And_Sign
(Fore
- Digits_Before_Point
);
402 pragma Assert
(FD
+ Digits_Before_Point
- 1 >= 0);
403 -- In this branch, we have Digits_Before_Point > 0. It is the
404 -- else of test (Digits_Before_Point <= 0)
405 Set_Digits
(FD
, FD
+ Digits_Before_Point
- 1);
407 Set_Digits
(FD
+ Digits_Before_Point
, LD
);
408 Set_Zeroes
(Digits_After_Point
- (ND
- Digits_Before_Point
));
413 pragma Annotate
(Gnatcheck
, Exempt_Off
, "Improper_Returns");
414 end Set_Decimal_Digits
;
416 --------------------------------
417 -- Set_Floating_Invalid_Value --
418 --------------------------------
420 pragma Annotate
(Gnatcheck
, Exempt_On
, "Unassigned_OUT_Parameters",
421 "the OUT parameter is assigned by component");
422 procedure Set_Floating_Invalid_Value
423 (V
: Floating_Invalid_Value
;
430 pragma Annotate
(Gnatcheck
, Exempt_Off
, "Unassigned_OUT_Parameters");
432 procedure Set
(C
: Character);
433 -- Sets character C in output buffer
435 procedure Set_Special_Fill
(N
: Natural);
436 -- After outputting +Inf, -Inf or NaN, this routine fills out the
437 -- rest of the field with * characters. The argument is the number
438 -- of characters output so far (either 3 or 4)
444 procedure Set
(C
: Character) is
446 pragma Assert
(P
in S
'First - 1 .. S
'Last - 1);
447 -- No check is done as documented in the header: updating P to point
448 -- to the last character stored, the caller promises that the buffer
449 -- is large enough and no check is made for this. Constraint_Error
450 -- will not necessarily be raised if this requirement is violated,
451 -- since it is perfectly valid to compile this unit with checks off.
457 ----------------------
458 -- Set_Special_Fill --
459 ----------------------
461 procedure Set_Special_Fill
(N
: Natural) is
464 for J
in N
+ 1 .. Fore
+ 1 + Aft
+ 1 + Exp
loop
469 for J
in N
+ 1 .. Fore
+ 1 + Aft
loop
473 end Set_Special_Fill
;
475 -- Start of processing for Set_Floating_Invalid_Value
479 when Minus_Infinity
=>
484 Set_Special_Fill
(4);
491 Set_Special_Fill
(4);
497 Set_Special_Fill
(3);
499 end Set_Floating_Invalid_Value
;