| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT RUN-TIME COMPONENTS -- |
| -- -- |
| -- S Y S T E M . I M A G E _ F -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2020-2022, 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 |
| |
| 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 first 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. |
| |
| -- The second prerequisite of the implementation is that the computation |
| -- of the operands does not overflow when the small is neither an integer |
| -- nor the reciprocal of an integer, which means that its numerator and |
| -- denominator must be smaller than 10**(2*Maxdigs-1) / 2**(Int'Size - 1) |
| -- if the small is larger than 1, and smaller than 2**(Int'Size - 1) / 10 |
| -- if the small is smaller than 1. |
| |
| 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. |
| |
| -- In fact this reasoning can be generalized to most S which are the ratio |
| -- of two integers with bounded magnitude. Let S = Num / Den and rewrite |
| -- case (1) above where Den = 1 into |
| |
| -- (1b) Y = -Num and Z = -Den * 10**(-D) |
| |
| -- Suppose that Num corresponds to the maximum value of -D, i.e. 18 and |
| -- 10**(-D) = 10**18. If you change Den into 10, then S becomes 10 times |
| -- smaller and, therefore, Fore is decremented by 1, which means that -D |
| -- is as well, provided that Fore was initially not larger than 37, so the |
| -- multiplication for Z still does not overflow. But you need to reach 10 |
| -- to trigger this effect, which means that a leeway of 10 is required, so |
| -- let's restrict this to a Num for which 10**(-D) <= 10**17. To summarize |
| -- this case, if S is the ratio of two integers with |
| |
| -- Den < Num <= B1 |
| |
| -- where B1 is a fixed limit, then case (1b) does not overflow. B1 can be |
| -- taken as the largest integer Small such that D = -17, i.e. Fore = 36, |
| -- which means that B1 * 2.0**63 must be smaller than 10**35. |
| |
| -- Let's continue and rewrite case (2) above when Num = 1 into |
| |
| -- (2b) Y = -Num * 10**D and Z = -Den, for D >= 0 |
| |
| -- Note that D <= 18 - (Fore - 1) and Fore >= 2 so D <= 17, thus you can |
| -- safely change Num into 10 in the product, but then S becomes 10 times |
| -- larger and, therefore, Fore is incremented by 1, which means that D is |
| -- decremented by 1 so you again have a product lesser or equal to 10**17. |
| -- To sum up, if S is the ratio of two integers with |
| |
| -- Num <= Den * S0 |
| |
| -- where S0 is the largest Small such that D >= 0, then case (2b) does not |
| -- overflow. |
| |
| -- Let's conclude and rewrite case (3) above when Num = 1 into |
| |
| -- (3b) Y = -Num and Z = -Den * 10**(-D), for D < 0 |
| |
| -- As explained above, this occurs only if both S0 < S < 1 and D = -1 and |
| -- is preserved if you scale up Num and Den simultaneously, what you can |
| -- do until Den * 10 tops the upper bound. To sum up, if S is the ratio of |
| -- two integers with |
| |
| -- Den * S0 < Num < Den <= B2 |
| |
| -- where B2 is a fixed limit, then case (3b) does not overflow. B2 can be |
| -- taken as the largest integer such that B2 * 10 is smaller than 2.0**63. |
| |
| -- The conclusion is that the algorithm works if the small is the ratio of |
| -- two integers in the range 1 .. 2**63 if either is equal to 1, or of two |
| -- integers in the range 1 .. B1 if the small is larger than 1, or of two |
| -- integers in the range 1 .. B2 if the small is smaller than 1. |
| |
| -- 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. |
| |
| ----------------- |
| -- 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) |
| |
| 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 and has a significant sign. |
| |
| Buf : String (1 .. Maxdigs); |
| Len : Natural; |
| -- Buffer for the image of the quotient |
| |
| Digs : String (1 .. 2 + N * Maxdigs); |
| Ndigs : Natural; |
| -- 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 |
| |
| if V >= 0 then |
| Digs (1) := ' '; |
| Ndigs := 1; |
| else |
| Ndigs := 0; |
| end if; |
| |
| 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 (Q, Digs, Ndigs); |
| end if; |
| |
| Scale := Scale + D; |
| |
| -- Prepare for next round, if any |
| |
| YY := 10**Maxdigs; |
| |
| else |
| pragma Assert (-10**Maxdigs < Q and then Q < 10**Maxdigs); |
| |
| Len := 0; |
| Set_Image_Integer (abs Q, Buf, Len); |
| |
| pragma Assert (1 <= Len and then Len <= Maxdigs); |
| |
| -- If no character but the space has been written, write the |
| -- minus if need be, since Set_Image_Integer did not do it. |
| |
| if Ndigs <= 1 then |
| if Q /= 0 then |
| if Ndigs = 0 then |
| Digs (1) := '-'; |
| end if; |
| |
| Digs (2 .. Len + 1) := Buf (1 .. Len); |
| Ndigs := Len + 1; |
| end if; |
| |
| -- Or else pad the output with zeroes up to Maxdigs |
| |
| else |
| 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; |