aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/libgnat/s-imagef.adb
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/libgnat/s-imagef.adb')
-rw-r--r--gcc/ada/libgnat/s-imagef.adb287
1 files changed, 287 insertions, 0 deletions
diff --git a/gcc/ada/libgnat/s-imagef.adb b/gcc/ada/libgnat/s-imagef.adb
new file mode 100644
index 0000000..2328474
--- /dev/null
+++ b/gcc/ada/libgnat/s-imagef.adb
@@ -0,0 +1,287 @@
+------------------------------------------------------------------------------
+-- --
+-- GNAT RUN-TIME COMPONENTS --
+-- --
+-- S Y S T E M . I M A G E _ F --
+-- --
+-- B o d y --
+-- --
+-- Copyright (C) 2020, Free Software Foundation, Inc. --
+-- --
+-- GNAT is free software; you can redistribute it and/or modify it under --
+-- terms of the GNU General Public License as published by the Free Soft- --
+-- ware Foundation; either version 3, or (at your option) any later ver- --
+-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
+-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. --
+-- --
+-- As a special exception under Section 7 of GPL version 3, you are granted --
+-- additional permissions described in the GCC Runtime Library Exception, --
+-- version 3.1, as published by the Free Software Foundation. --
+-- --
+-- You should have received a copy of the GNU General Public License and --
+-- a copy of the GCC Runtime Library Exception along with this program; --
+-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
+-- <http://www.gnu.org/licenses/>. --
+-- --
+-- GNAT was originally developed by the GNAT team at New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc. --
+-- --
+------------------------------------------------------------------------------
+
+with System.Image_I;
+with System.Img_Util; use System.Img_Util;
+
+package body System.Image_F is
+
+ package Image_I is new System.Image_I (Int);
+
+ procedure Set_Image_Integer
+ (V : Int;
+ S : in out String;
+ P : in out Natural)
+ renames Image_I.Set_Image_Integer;
+
+ -- The following section describes a specific implementation choice for
+ -- performing base conversions needed for output of values of a fixed
+ -- point type T with small T'Small. The goal is to be able to output
+ -- all values of fixed point types with a precision of 64 bits and a
+ -- small in the range 2.0**(-63) .. 2.0**63. The reasoning can easily
+ -- be adapted to fixed point types with a precision of 32 or 128 bits.
+
+ -- The chosen algorithm uses fixed precision integer arithmetic for
+ -- reasons of simplicity and efficiency. It is important to understand
+ -- in what ways the most simple and accurate approach to fixed point I/O
+ -- is limiting, before considering more complicated schemes.
+
+ -- Without loss of generality assume T has a range (-2.0**63) * T'Small
+ -- .. (2.0**63 - 1) * T'Small, and is output with Aft digits after the
+ -- decimal point and T'Fore - 1 before. If T'Small is integer, or
+ -- 1.0 / T'Small is integer, let S = T'Small.
+
+ -- The idea is to convert a value X * S of type T to a 64-bit integer value
+ -- Q equal to 10.0**D * (X * S) rounded to the nearest integer, using only
+ -- a scaled integer divide of the form
+
+ -- Q = (X * Y) / Z,
+
+ -- where the variables X, Y, Z are 64-bit integers, and both multiplication
+ -- and division are done using full intermediate precision. Then the final
+ -- decimal value to be output is
+
+ -- Q * 10**(-D)
+
+ -- This value can be written to the output file or to the result string
+ -- according to the format described in RM A.3.10. The details of this
+ -- operation are omitted here.
+
+ -- A 64-bit value can represent all integers with 18 decimal digits, but
+ -- not all with 19 decimal digits. If the total number of requested ouput
+ -- digits (Fore - 1) + Aft is greater than 18 then, for purposes of the
+ -- conversion, Aft is adjusted to 18 - (Fore - 1). In that case, trailing
+ -- zeros can complete the output after writing the first 18 significant
+ -- digits, or the technique described in the next section can be used.
+ -- In addition, D cannot be smaller than -18, in order for 10.0**(-D) to
+ -- fit in a 64-bit integer.
+
+ -- The final expression for D is
+
+ -- D = Integer'Max (-18, Integer'Min (Aft, 18 - (Fore - 1)));
+
+ -- For Y and Z the following expressions can be derived:
+
+ -- Q = X * S * (10.0**D) = (X * Y) / Z
+
+ -- If S is an integer greater than or equal to one, then Fore must be at
+ -- least 20 in order to print T'First, which is at most -2.0**63. This
+ -- means that D < 0, so use
+
+ -- (1) Y = -S and Z = -10**(-D)
+
+ -- If 1.0 / S is an integer greater than one, use
+
+ -- (2) Y = -10**D and Z = -(1.0 / S), for D >= 0
+
+ -- or
+
+ -- (3) Y = -1 and Z = -(1.0 / S) * 10**(-D), for D < 0
+
+ -- Negative values are used for nominator Y and denominator Z, so that S
+ -- can have a maximum value of 2.0**63 and a minimum of 2.0**(-63). For
+ -- -(1.0 / S) in -1 .. -9, Fore will still be 20, and D will be negative,
+ -- as (-2.0**63) / -9 is greater than 10**18. In these cases there is room
+ -- in the denominator for the extra decimal scaling required, so case (3)
+ -- will not overflow.
+
+ -- Using a scaled divide which truncates and returns a remainder R,
+ -- another K trailing digits can be calculated by computing the value
+ -- (R * (10.0**K)) / Z using another scaled divide. This procedure
+ -- can be repeated to compute an arbitrary number of digits in linear
+ -- time and storage. The last scaled divide should be rounded, with
+ -- a possible carry propagating to the more significant digits, to
+ -- ensure correct rounding of the unit in the last place.
+
+ Maxdigs : constant Natural := Int'Width - 2;
+ -- Maximum number of decimal digits that can be represented in an Int.
+ -- The "-2" accounts for the sign and one extra digit, since we need the
+ -- maximum number of 9's that can be represented, e.g. for the 64-bit case,
+ -- Integer_64'Width is 20 since the maximum value is approximately 9.2E+18
+ -- and has 19 digits, but the maximum number of 9's that can be represented
+ -- in Integer_64 is only 18.
+
+ -- The prerequisite of the implementation is that the first scaled divide
+ -- does not overflow, which means that the absolute value of the input X
+ -- must always be smaller than 10**Maxdigs * 2**(Int'Size - 1). Otherwise
+ -- Constraint_Error is raised by the scaled divide operation.
+
+ -----------------
+ -- Image_Fixed --
+ -----------------
+
+ procedure Image_Fixed
+ (V : Int;
+ S : in out String;
+ P : out Natural;
+ Num : Int;
+ Den : Int;
+ For0 : Natural;
+ Aft0 : Natural)
+ is
+ pragma Assert (S'First = 1);
+
+ begin
+ -- Add space at start for non-negative numbers
+
+ if V >= 0 then
+ S (1) := ' ';
+ P := 1;
+ else
+ P := 0;
+ end if;
+
+ Set_Image_Fixed (V, S, P, Num, Den, For0, Aft0, 1, Aft0, 0);
+ end Image_Fixed;
+
+ ---------------------
+ -- Set_Image_Fixed --
+ ---------------------
+
+ procedure Set_Image_Fixed
+ (V : Int;
+ S : in out String;
+ P : in out Natural;
+ Num : Int;
+ Den : Int;
+ For0 : Natural;
+ Aft0 : Natural;
+ Fore : Natural;
+ Aft : Natural;
+ Exp : Natural)
+ is
+ pragma Assert (Num < 0 and then Den < 0);
+ -- Accept only negative numbers to allow -2**(Int'Size - 1)
+
+ pragma Assert (Num = -1 or else Den = -1);
+ -- Accept only integer or reciprocal of integer to control the
+ -- magnitude of the arithmetic operations below.
+
+ A : constant Natural :=
+ Boolean'Pos (Exp > 0) * Aft0 + Natural'Max (Aft, 1) + 1;
+ -- Number of digits after the decimal point to be computed. If Exp is
+ -- positive, we need to compute Aft decimal digits after the first non
+ -- zero digit and we are guaranteed there is at least one in the first
+ -- Aft0 digits (unless V is zero). In both cases, we compute one more
+ -- digit than requested so that Set_Decimal_Digits can round at Aft.
+
+ D : constant Integer :=
+ Integer'Max (-Maxdigs, Integer'Min (A, Maxdigs - (For0 - 1)));
+ Y : constant Int := Num * 10**Integer'Max (0, D);
+ Z : constant Int := Den * 10**Integer'Max (0, -D);
+ -- See the description of the algorithm above
+
+ AF : constant Natural := A - D;
+ -- Number of remaining digits to be computed after the first round. It
+ -- is larger than A if the first round does not compute all the digits
+ -- before the decimal point, i.e. (For0 - 1) larger than Maxdigs.
+
+ N : constant Natural := 1 + (AF + Maxdigs - 1) / Maxdigs;
+ -- Number of rounds of scaled divide to be performed
+
+ Q : Int;
+ -- Quotient of the scaled divide in this round. Only the first round
+ -- may yield more than Maxdigs digits. The sign is not significant.
+
+ Buf : String (1 .. Maxdigs);
+ Len : Natural;
+ -- Buffer for the image of the quotient
+
+ Digs : String (1 .. N * Maxdigs + 1);
+ Ndigs : Natural := 0;
+ -- Concatenated image of the successive quotients
+
+ Scale : Integer := 0;
+ -- Exponent such that the result is Digs (1 .. NDigs) * 10**(-Scale)
+
+ XX : Int := V;
+ YY : Int := Y;
+ -- First two operands of the scaled divide
+
+ begin
+ -- Set the first character like Image, either minus or space
+
+ Digs (1) := (if V < 0 then '-' else ' ');
+ Ndigs := 1;
+
+ for J in 1 .. N loop
+ exit when XX = 0;
+
+ Scaled_Divide (XX, YY, Z, Q, R => XX, Round => False);
+
+ if J = 1 then
+ if Q /= 0 then
+ Set_Image_Integer (abs Q, Digs, Ndigs);
+ end if;
+
+ Scale := Scale + D;
+
+ -- Prepare for next round, if any
+
+ YY := 10**Maxdigs;
+
+ else
+ Len := 0;
+ Set_Image_Integer (abs Q, Buf, Len);
+
+ if Ndigs = 1 then
+ Digs (2 .. Len + 1) := Buf (1 .. Len);
+ Ndigs := Len + 1;
+
+ else
+ -- Pad the output with zeroes up to Maxdigs
+
+ for K in 1 .. Maxdigs - Len loop
+ Digs (Ndigs + K) := '0';
+ end loop;
+
+ for K in 1 .. Len loop
+ Digs (Ndigs + Maxdigs - Len + K) := Buf (K);
+ end loop;
+
+ Ndigs := Ndigs + Maxdigs;
+ end if;
+
+ Scale := Scale + Maxdigs;
+ end if;
+ end loop;
+
+ -- If no digit was output, this is zero
+
+ if Ndigs = 1 then
+ Digs (1 .. 2) := " 0";
+ Ndigs := 2;
+ end if;
+
+ Set_Decimal_Digits (Digs, Ndigs, S, P, Scale, Fore, Aft, Exp);
+ end Set_Image_Fixed;
+
+end System.Image_F;