| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT RUN-TIME COMPONENTS -- |
| -- -- |
| -- S Y S T E M . I M G _ R E A L -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2014, 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.Img_LLU; use System.Img_LLU; |
| with System.Img_Uns; use System.Img_Uns; |
| with System.Powten_Table; use System.Powten_Table; |
| with System.Unsigned_Types; use System.Unsigned_Types; |
| with System.Float_Control; |
| |
| package body System.Img_Real is |
| |
| -- The following defines the maximum number of digits that we can convert |
| -- accurately. This is limited by the precision of Long_Long_Float, and |
| -- also by the number of digits we can hold in Long_Long_Unsigned, which |
| -- is the integer type we use as an intermediate for the result. |
| |
| -- We assume that in practice, the limitation will come from the digits |
| -- value, rather than the integer value. This is true for typical IEEE |
| -- implementations, and at worst, the only loss is for some precision |
| -- in very high precision floating-point output. |
| |
| -- Note that in the following, the "-2" accounts for the sign and one |
| -- extra digits, since we need the maximum number of 9's that can be |
| -- supported, e.g. for the normal 64 bit case, Long_Long_Integer'Width |
| -- is 21, since the maximum value (approx 1.6 * 10**19) has 20 digits, |
| -- but the maximum number of 9's that can be supported is 19. |
| |
| Maxdigs : constant := |
| Natural'Min |
| (Long_Long_Unsigned'Width - 2, Long_Long_Float'Digits); |
| |
| Unsdigs : constant := Unsigned'Width - 2; |
| -- Number of digits that can be converted using type Unsigned |
| -- See above for the explanation of the -2. |
| |
| Maxscaling : constant := 5000; |
| -- Max decimal scaling required during conversion of floating-point |
| -- numbers to decimal. This is used to defend against infinite |
| -- looping in the conversion, as can be caused by erroneous executions. |
| -- The largest exponent used on any current system is 2**16383, which |
| -- is approximately 10**4932, and the highest number of decimal digits |
| -- is about 35 for 128-bit floating-point formats, so 5000 leaves |
| -- enough room for scaling such values |
| |
| function Is_Negative (V : Long_Long_Float) return Boolean; |
| pragma Import (Intrinsic, Is_Negative); |
| |
| -------------------------- |
| -- Image_Floating_Point -- |
| -------------------------- |
| |
| procedure Image_Floating_Point |
| (V : Long_Long_Float; |
| S : in out String; |
| P : out Natural; |
| Digs : Natural) |
| is |
| pragma Assert (S'First = 1); |
| |
| begin |
| -- Decide whether a blank should be prepended before the call to |
| -- Set_Image_Real. We generate a blank for positive values, and |
| -- also for positive zeroes. For negative zeroes, we generate a |
| -- space only if Signed_Zeroes is True (the RM only permits the |
| -- output of -0.0 on targets where this is the case). We can of |
| -- course still see a -0.0 on a target where Signed_Zeroes is |
| -- False (since this attribute refers to the proper handling of |
| -- negative zeroes, not to their existence). We do not generate |
| -- a blank for positive infinity, since we output an explicit +. |
| |
| if (not Is_Negative (V) and then V <= Long_Long_Float'Last) |
| or else (not Long_Long_Float'Signed_Zeros and then V = -0.0) |
| then |
| S (1) := ' '; |
| P := 1; |
| else |
| P := 0; |
| end if; |
| |
| Set_Image_Real (V, S, P, 1, Digs - 1, 3); |
| end Image_Floating_Point; |
| |
| -------------------------------- |
| -- Image_Ordinary_Fixed_Point -- |
| -------------------------------- |
| |
| procedure Image_Ordinary_Fixed_Point |
| (V : Long_Long_Float; |
| S : in out String; |
| P : out Natural; |
| Aft : Natural) |
| is |
| pragma Assert (S'First = 1); |
| |
| begin |
| -- Output space at start if non-negative |
| |
| if V >= 0.0 then |
| S (1) := ' '; |
| P := 1; |
| else |
| P := 0; |
| end if; |
| |
| Set_Image_Real (V, S, P, 1, Aft, 0); |
| end Image_Ordinary_Fixed_Point; |
| |
| -------------------- |
| -- Set_Image_Real -- |
| -------------------- |
| |
| procedure Set_Image_Real |
| (V : Long_Long_Float; |
| S : out String; |
| P : in out Natural; |
| Fore : Natural; |
| Aft : Natural; |
| Exp : Natural) |
| is |
| NFrac : constant Natural := Natural'Max (Aft, 1); |
| Sign : Character; |
| X : aliased Long_Long_Float; |
| -- This is declared aliased because the expansion of X'Valid passes |
| -- X by access and JGNAT requires all access parameters to be aliased. |
| -- The Valid attribute probably needs to be handled via a different |
| -- expansion for JGNAT, and this use of aliased should be removed |
| -- once Valid is handled properly. ??? |
| Scale : Integer; |
| Expon : Integer; |
| |
| Field_Max : constant := 255; |
| -- This should be the same value as Ada.[Wide_]Text_IO.Field'Last. |
| -- It is not worth dragging in Ada.Text_IO to pick up this value, |
| -- since it really should never be necessary to change it. |
| |
| Digs : String (1 .. 2 * Field_Max + 16); |
| -- Array used to hold digits of converted integer value. This is a |
| -- large enough buffer to accommodate ludicrous values of Fore and Aft. |
| |
| Ndigs : Natural; |
| -- Number of digits stored in Digs (and also subscript of last digit) |
| |
| procedure Adjust_Scale (S : Natural); |
| -- Adjusts the value in X by multiplying or dividing by a power of |
| -- ten so that it is in the range 10**(S-1) <= X < 10**S. Includes |
| -- adding 0.5 to round the result, readjusting if the rounding causes |
| -- the result to wander out of the range. Scale is adjusted to reflect |
| -- the power of ten used to divide the result (i.e. one is added to |
| -- the scale value for each division by 10.0, or one is subtracted |
| -- for each multiplication by 10.0). |
| |
| procedure Convert_Integer; |
| -- Takes the value in X, outputs integer digits into Digs. On return, |
| -- Ndigs is set to the number of digits stored. The digits are stored |
| -- in Digs (1 .. Ndigs), |
| |
| procedure Set (C : Character); |
| -- Sets character C in output buffer |
| |
| procedure Set_Blanks_And_Sign (N : Integer); |
| -- Sets leading blanks and minus sign if needed. N is the number of |
| -- positions to be filled (a minus sign is output even if N is zero |
| -- or negative, but for a positive value, if N is non-positive, then |
| -- the call has no effect). |
| |
| procedure Set_Digs (S, E : Natural); |
| -- Set digits S through E from Digs buffer. No effect if S > E |
| |
| procedure Set_Special_Fill (N : Natural); |
| -- After outputting +Inf, -Inf or NaN, this routine fills out the |
| -- rest of the field with * characters. The argument is the number |
| -- of characters output so far (either 3 or 4) |
| |
| procedure Set_Zeros (N : Integer); |
| -- Set N zeros, no effect if N is negative |
| |
| pragma Inline (Set); |
| pragma Inline (Set_Digs); |
| pragma Inline (Set_Zeros); |
| |
| ------------------ |
| -- Adjust_Scale -- |
| ------------------ |
| |
| procedure Adjust_Scale (S : Natural) is |
| Lo : Natural; |
| Hi : Natural; |
| Mid : Natural; |
| XP : Long_Long_Float; |
| |
| begin |
| -- Cases where scaling up is required |
| |
| if X < Powten (S - 1) then |
| |
| -- What we are looking for is a power of ten to multiply X by |
| -- so that the result lies within the required range. |
| |
| loop |
| XP := X * Powten (Maxpow); |
| exit when XP >= Powten (S - 1) or else Scale < -Maxscaling; |
| X := XP; |
| Scale := Scale - Maxpow; |
| end loop; |
| |
| -- The following exception is only raised in case of erroneous |
| -- execution, where a number was considered valid but still |
| -- fails to scale up. One situation where this can happen is |
| -- when a system which is supposed to be IEEE-compliant, but |
| -- has been reconfigured to flush denormals to zero. |
| |
| if Scale < -Maxscaling then |
| raise Constraint_Error; |
| end if; |
| |
| -- Here we know that we must multiply by at least 10**1 and that |
| -- 10**Maxpow takes us too far: binary search to find right one. |
| |
| -- Because of roundoff errors, it is possible for the value |
| -- of XP to be just outside of the interval when Lo >= Hi. In |
| -- that case we adjust explicitly by a factor of 10. This |
| -- can only happen with a value that is very close to an |
| -- exact power of 10. |
| |
| Lo := 1; |
| Hi := Maxpow; |
| |
| loop |
| Mid := (Lo + Hi) / 2; |
| XP := X * Powten (Mid); |
| |
| if XP < Powten (S - 1) then |
| |
| if Lo >= Hi then |
| Mid := Mid + 1; |
| XP := XP * 10.0; |
| exit; |
| |
| else |
| Lo := Mid + 1; |
| end if; |
| |
| elsif XP >= Powten (S) then |
| |
| if Lo >= Hi then |
| Mid := Mid - 1; |
| XP := XP / 10.0; |
| exit; |
| |
| else |
| Hi := Mid - 1; |
| end if; |
| |
| else |
| exit; |
| end if; |
| end loop; |
| |
| X := XP; |
| Scale := Scale - Mid; |
| |
| -- Cases where scaling down is required |
| |
| elsif X >= Powten (S) then |
| |
| -- What we are looking for is a power of ten to divide X by |
| -- so that the result lies within the required range. |
| |
| loop |
| XP := X / Powten (Maxpow); |
| exit when XP < Powten (S) or else Scale > Maxscaling; |
| X := XP; |
| Scale := Scale + Maxpow; |
| end loop; |
| |
| -- The following exception is only raised in case of erroneous |
| -- execution, where a number was considered valid but still |
| -- fails to scale up. One situation where this can happen is |
| -- when a system which is supposed to be IEEE-compliant, but |
| -- has been reconfigured to flush denormals to zero. |
| |
| if Scale > Maxscaling then |
| raise Constraint_Error; |
| end if; |
| |
| -- Here we know that we must divide by at least 10**1 and that |
| -- 10**Maxpow takes us too far, binary search to find right one. |
| |
| Lo := 1; |
| Hi := Maxpow; |
| |
| loop |
| Mid := (Lo + Hi) / 2; |
| XP := X / Powten (Mid); |
| |
| if XP < Powten (S - 1) then |
| |
| if Lo >= Hi then |
| XP := XP * 10.0; |
| Mid := Mid - 1; |
| exit; |
| |
| else |
| Hi := Mid - 1; |
| end if; |
| |
| elsif XP >= Powten (S) then |
| |
| if Lo >= Hi then |
| XP := XP / 10.0; |
| Mid := Mid + 1; |
| exit; |
| |
| else |
| Lo := Mid + 1; |
| end if; |
| |
| else |
| exit; |
| end if; |
| end loop; |
| |
| X := XP; |
| Scale := Scale + Mid; |
| |
| -- Here we are already scaled right |
| |
| else |
| null; |
| end if; |
| |
| -- Round, readjusting scale if needed. Note that if a readjustment |
| -- occurs, then it is never necessary to round again, because there |
| -- is no possibility of such a second rounding causing a change. |
| |
| X := X + 0.5; |
| |
| if X >= Powten (S) then |
| X := X / 10.0; |
| Scale := Scale + 1; |
| end if; |
| |
| end Adjust_Scale; |
| |
| --------------------- |
| -- Convert_Integer -- |
| --------------------- |
| |
| procedure Convert_Integer is |
| begin |
| -- Use Unsigned routine if possible, since on many machines it will |
| -- be significantly more efficient than the Long_Long_Unsigned one. |
| |
| if X < Powten (Unsdigs) then |
| Ndigs := 0; |
| Set_Image_Unsigned |
| (Unsigned (Long_Long_Float'Truncation (X)), |
| Digs, Ndigs); |
| |
| -- But if we want more digits than fit in Unsigned, we have to use |
| -- the Long_Long_Unsigned routine after all. |
| |
| else |
| Ndigs := 0; |
| Set_Image_Long_Long_Unsigned |
| (Long_Long_Unsigned (Long_Long_Float'Truncation (X)), |
| Digs, Ndigs); |
| end if; |
| end Convert_Integer; |
| |
| --------- |
| -- Set -- |
| --------- |
| |
| procedure Set (C : Character) is |
| begin |
| P := P + 1; |
| S (P) := C; |
| end Set; |
| |
| ------------------------- |
| -- Set_Blanks_And_Sign -- |
| ------------------------- |
| |
| procedure Set_Blanks_And_Sign (N : Integer) is |
| begin |
| if Sign = '-' then |
| for J in 1 .. N - 1 loop |
| Set (' '); |
| end loop; |
| |
| Set ('-'); |
| |
| else |
| for J in 1 .. N loop |
| Set (' '); |
| end loop; |
| end if; |
| end Set_Blanks_And_Sign; |
| |
| -------------- |
| -- Set_Digs -- |
| -------------- |
| |
| procedure Set_Digs (S, E : Natural) is |
| begin |
| for J in S .. E loop |
| Set (Digs (J)); |
| end loop; |
| end Set_Digs; |
| |
| ---------------------- |
| -- Set_Special_Fill -- |
| ---------------------- |
| |
| procedure Set_Special_Fill (N : Natural) is |
| F : Natural; |
| |
| begin |
| F := Fore + 1 + Aft - N; |
| |
| if Exp /= 0 then |
| F := F + Exp + 1; |
| end if; |
| |
| for J in 1 .. F loop |
| Set ('*'); |
| end loop; |
| end Set_Special_Fill; |
| |
| --------------- |
| -- Set_Zeros -- |
| --------------- |
| |
| procedure Set_Zeros (N : Integer) is |
| begin |
| for J in 1 .. N loop |
| Set ('0'); |
| end loop; |
| end Set_Zeros; |
| |
| -- Start of processing for Set_Image_Real |
| |
| begin |
| -- We call the floating-point processor reset routine so that we can |
| -- be sure the floating-point processor is properly set for conversion |
| -- calls. This is notably need on Windows, where calls to the operating |
| -- system randomly reset the processor into 64-bit mode. |
| |
| System.Float_Control.Reset; |
| |
| Scale := 0; |
| |
| -- Deal with invalid values first, |
| |
| if not V'Valid then |
| |
| -- Note that we're taking our chances here, as V might be |
| -- an invalid bit pattern resulting from erroneous execution |
| -- (caused by using uninitialized variables for example). |
| |
| -- No matter what, we'll at least get reasonable behaviour, |
| -- converting to infinity or some other value, or causing an |
| -- exception to be raised is fine. |
| |
| -- If the following test succeeds, then we definitely have |
| -- an infinite value, so we print Inf. |
| |
| if V > Long_Long_Float'Last then |
| Set ('+'); |
| Set ('I'); |
| Set ('n'); |
| Set ('f'); |
| Set_Special_Fill (4); |
| |
| -- In all other cases we print NaN |
| |
| elsif V < Long_Long_Float'First then |
| Set ('-'); |
| Set ('I'); |
| Set ('n'); |
| Set ('f'); |
| Set_Special_Fill (4); |
| |
| else |
| Set ('N'); |
| Set ('a'); |
| Set ('N'); |
| Set_Special_Fill (3); |
| end if; |
| |
| return; |
| end if; |
| |
| -- Positive values |
| |
| if V > 0.0 then |
| X := V; |
| Sign := '+'; |
| |
| -- Negative values |
| |
| elsif V < 0.0 then |
| X := -V; |
| Sign := '-'; |
| |
| -- Zero values |
| |
| elsif V = 0.0 then |
| if Long_Long_Float'Signed_Zeros and then Is_Negative (V) then |
| Sign := '-'; |
| else |
| Sign := '+'; |
| end if; |
| |
| Set_Blanks_And_Sign (Fore - 1); |
| Set ('0'); |
| Set ('.'); |
| Set_Zeros (NFrac); |
| |
| if Exp /= 0 then |
| Set ('E'); |
| Set ('+'); |
| Set_Zeros (Natural'Max (1, Exp - 1)); |
| end if; |
| |
| return; |
| |
| else |
| -- It should not be possible for a NaN to end up here. |
| -- Either the 'Valid test has failed, or we have some form |
| -- of erroneous execution. Raise Constraint_Error instead of |
| -- attempting to go ahead printing the value. |
| |
| raise Constraint_Error; |
| end if; |
| |
| -- X and Sign are set here, and X is known to be a valid, |
| -- non-zero floating-point number. |
| |
| -- Case of non-zero value with Exp = 0 |
| |
| if Exp = 0 then |
| |
| -- First step is to multiply by 10 ** Nfrac to get an integer |
| -- value to be output, an then add 0.5 to round the result. |
| |
| declare |
| NF : Natural := NFrac; |
| |
| begin |
| loop |
| -- If we are larger than Powten (Maxdigs) now, then |
| -- we have too many significant digits, and we have |
| -- not even finished multiplying by NFrac (NF shows |
| -- the number of unaccounted-for digits). |
| |
| if X >= Powten (Maxdigs) then |
| |
| -- In this situation, we only to generate a reasonable |
| -- number of significant digits, and then zeroes after. |
| -- So first we rescale to get: |
| |
| -- 10 ** (Maxdigs - 1) <= X < 10 ** Maxdigs |
| |
| -- and then convert the resulting integer |
| |
| Adjust_Scale (Maxdigs); |
| Convert_Integer; |
| |
| -- If that caused rescaling, then add zeros to the end |
| -- of the number to account for this scaling. Also add |
| -- zeroes to account for the undone multiplications |
| |
| for J in 1 .. Scale + NF loop |
| Ndigs := Ndigs + 1; |
| Digs (Ndigs) := '0'; |
| end loop; |
| |
| exit; |
| |
| -- If multiplication is complete, then convert the resulting |
| -- integer after rounding (note that X is non-negative) |
| |
| elsif NF = 0 then |
| X := X + 0.5; |
| Convert_Integer; |
| exit; |
| |
| -- Otherwise we can go ahead with the multiplication. If it |
| -- can be done in one step, then do it in one step. |
| |
| elsif NF < Maxpow then |
| X := X * Powten (NF); |
| NF := 0; |
| |
| -- If it cannot be done in one step, then do partial scaling |
| |
| else |
| X := X * Powten (Maxpow); |
| NF := NF - Maxpow; |
| end if; |
| end loop; |
| end; |
| |
| -- If number of available digits is less or equal to NFrac, |
| -- then we need an extra zero before the decimal point. |
| |
| if Ndigs <= NFrac then |
| Set_Blanks_And_Sign (Fore - 1); |
| Set ('0'); |
| Set ('.'); |
| Set_Zeros (NFrac - Ndigs); |
| Set_Digs (1, Ndigs); |
| |
| -- Normal case with some digits before the decimal point |
| |
| else |
| Set_Blanks_And_Sign (Fore - (Ndigs - NFrac)); |
| Set_Digs (1, Ndigs - NFrac); |
| Set ('.'); |
| Set_Digs (Ndigs - NFrac + 1, Ndigs); |
| end if; |
| |
| -- Case of non-zero value with non-zero Exp value |
| |
| else |
| -- If NFrac is less than Maxdigs, then all the fraction digits are |
| -- significant, so we can scale the resulting integer accordingly. |
| |
| if NFrac < Maxdigs then |
| Adjust_Scale (NFrac + 1); |
| Convert_Integer; |
| |
| -- Otherwise, we get the maximum number of digits available |
| |
| else |
| Adjust_Scale (Maxdigs); |
| Convert_Integer; |
| |
| for J in 1 .. NFrac - Maxdigs + 1 loop |
| Ndigs := Ndigs + 1; |
| Digs (Ndigs) := '0'; |
| Scale := Scale - 1; |
| end loop; |
| end if; |
| |
| Set_Blanks_And_Sign (Fore - 1); |
| Set (Digs (1)); |
| Set ('.'); |
| Set_Digs (2, Ndigs); |
| |
| -- The exponent is the scaling factor adjusted for the digits |
| -- that we output after the decimal point, since these were |
| -- included in the scaled digits that we output. |
| |
| Expon := Scale + NFrac; |
| |
| Set ('E'); |
| Ndigs := 0; |
| |
| if Expon >= 0 then |
| Set ('+'); |
| Set_Image_Unsigned (Unsigned (Expon), Digs, Ndigs); |
| else |
| Set ('-'); |
| Set_Image_Unsigned (Unsigned (-Expon), Digs, Ndigs); |
| end if; |
| |
| Set_Zeros (Exp - Ndigs - 1); |
| Set_Digs (1, Ndigs); |
| end if; |
| |
| end Set_Image_Real; |
| |
| end System.Img_Real; |