| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT RUN-TIME COMPONENTS -- |
| -- -- |
| -- S Y S T E M . I M A G E _ R -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-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.Double_Real; |
| with System.Float_Control; |
| with System.Img_Util; use System.Img_Util; |
| |
| package body System.Image_R is |
| |
| -- The following defines the maximum number of digits that we can convert |
| -- accurately. This is limited by the precision of the Num type, and also |
| -- by the number of digits that can be held in the Uns type, which is the |
| -- integer type we use as an intermediate in the computation. But, in both |
| -- cases, we can work with a double value in these types. |
| |
| -- Note that in the following, the "-2" accounts for the space and one |
| -- extra digit, since we need the maximum number of 9's that can be |
| -- represented, e.g. for the 64-bit case, Long_Long_Unsigned'Width is |
| -- 21, since the maximum value (approx 1.8E+19) has 20 digits, but the |
| -- maximum number of 9's that can be represented is only 19. |
| |
| Maxdigs : constant Natural := 2 * Natural'Min (Uns'Width - 2, Num'Digits); |
| |
| 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 |
| |
| package Double_Real is new System.Double_Real (Num); |
| use type Double_Real.Double_T; |
| |
| subtype Double_T is Double_Real.Double_T; |
| -- The double floating-point type |
| |
| function From_Unsigned is new Double_Real.From_Unsigned (Uns); |
| function To_Unsigned is new Double_Real.To_Unsigned (Uns); |
| -- Convert betwwen a double Num and a single Uns |
| |
| function Is_Negative (V : Num) return Boolean; |
| -- Return True if V is negative for the purpose of the output, i.e. return |
| -- True for negative zeros only if Signed_Zeros is True. |
| |
| ----------------------- |
| -- Image_Fixed_Point -- |
| ----------------------- |
| |
| procedure Image_Fixed_Point |
| (V : Num; |
| 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_Fixed_Point; |
| |
| -------------------------- |
| -- Image_Floating_Point -- |
| -------------------------- |
| |
| procedure Image_Floating_Point |
| (V : Num; |
| 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 zeros. For negative zeros, we generate a |
| -- blank only if Signed_Zeros is False (the RM only permits the |
| -- output of -0.0 when Signed_Zeros is True). We do not generate |
| -- a blank for positive infinity, since we output an explicit +. |
| |
| if not Is_Negative (V) and then V <= Num'Last then |
| pragma Annotate (CodePeer, False_Positive, "condition predetermined", |
| "CodePeer analysis ignores NaN and Inf values"); |
| pragma Assert (S'Last > 1); |
| -- The caller is responsible for S to be large enough for all |
| -- Image_Floating_Point operation. |
| S (1) := ' '; |
| P := 1; |
| else |
| P := 0; |
| end if; |
| |
| Set_Image_Real (V, S, P, 1, Digs - 1, 3); |
| end Image_Floating_Point; |
| |
| ----------------- |
| -- Is_Negative -- |
| ----------------- |
| |
| function Is_Negative (V : Num) return Boolean is |
| begin |
| if V < 0.0 then |
| return True; |
| |
| elsif V > 0.0 then |
| return False; |
| |
| elsif not Num'Signed_Zeros then |
| return False; |
| |
| else |
| return Num'Copy_Sign (1.0, V) < 0.0; |
| end if; |
| end Is_Negative; |
| |
| -------------------- |
| -- Set_Image_Real -- |
| -------------------- |
| |
| procedure Set_Image_Real |
| (V : Num; |
| S : in out String; |
| P : in out Natural; |
| Fore : Natural; |
| Aft : Natural; |
| Exp : Natural) |
| is |
| Powten : constant array (0 .. Maxpow) of Double_T; |
| pragma Import (Ada, Powten); |
| for Powten'Address use Powten_Address; |
| |
| NFrac : constant Natural := Natural'Max (Aft, 1); |
| -- Number of digits after the decimal point |
| |
| Digs : String (1 .. 3 + Maxdigs); |
| -- Array used to hold digits of converted integer value |
| |
| Ndigs : Natural; |
| -- Number of digits stored in Digs (and also subscript of last digit) |
| |
| Scale : Integer := 0; |
| -- Exponent such that the result is Digs (1 .. NDigs) * 10**(-Scale) |
| |
| X : Double_T; |
| -- Current absolute value of the input after scaling |
| |
| 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. 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 multiplication by |
| -- 10.0 and one is subtracted for each division by 10.0. |
| |
| ------------------ |
| -- Adjust_Scale -- |
| ------------------ |
| |
| procedure Adjust_Scale (S : Natural) is |
| Lo, Mid, Hi : Natural; |
| XP : Double_T; |
| |
| 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; |
| end Adjust_Scale; |
| |
| -- Start of processing for Set_Image_Real |
| |
| begin |
| -- We call the floating-point processor reset routine so we can be sure |
| -- that the processor is properly set for conversions. This is notably |
| -- needed on Windows, where calls to the operating system randomly reset |
| -- the processor into 64-bit mode. |
| |
| if Num'Machine_Mantissa = 64 then |
| System.Float_Control.Reset; |
| end if; |
| |
| -- 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 behavior, |
| -- converting to infinity or some other value, or causing an |
| -- exception to be raised is fine. |
| |
| -- If the following two tests succeed, then we definitely have |
| -- an infinite value, so we print +Inf or -Inf. |
| |
| if V > Num'Last then |
| pragma Annotate (CodePeer, False_Positive, "dead code", |
| "CodePeer analysis ignores NaN and Inf values"); |
| pragma Annotate (CodePeer, False_Positive, "test always true", |
| "CodePeer analysis ignores NaN and Inf values"); |
| |
| Set_Floating_Invalid_Value (Infinity, S, P, Fore, Aft, Exp); |
| |
| elsif V < Num'First then |
| Set_Floating_Invalid_Value (Minus_Infinity, S, P, Fore, Aft, Exp); |
| |
| -- In all other cases we print NaN |
| |
| else |
| Set_Floating_Invalid_Value (Not_A_Number, S, P, Fore, Aft, Exp); |
| end if; |
| |
| return; |
| end if; |
| |
| -- Set the first character like Image |
| |
| Digs (1) := (if Is_Negative (V) then '-' else ' '); |
| Ndigs := 1; |
| |
| X := Double_Real.To_Double (abs (V)); |
| |
| -- If X is zero, we are done |
| |
| if X = 0.0 then |
| Digs (2) := '0'; |
| Ndigs := 2; |
| |
| -- Otherwise, scale X and convert it to an integer |
| |
| else |
| -- In exponent notation, we need exactly NFrac + 1 digits and always |
| -- round the last one. |
| |
| if Exp > 0 then |
| Adjust_Scale (Natural'Min (NFrac + 1, Maxdigs)); |
| X := X + 0.5; |
| |
| -- In straight notation, we compute the maximum number of digits and |
| -- compare how many of them will be put after the decimal point with |
| -- Nfrac, in order to find out whether we need to round the last one |
| -- here or whether the rounding is performed by Set_Decimal_Digits. |
| |
| else |
| Adjust_Scale (Maxdigs); |
| if Scale <= NFrac then |
| X := X + 0.5; |
| end if; |
| end if; |
| |
| -- If X fits in an Uns, do the conversion directly. Note that this is |
| -- always the case for the Image attribute. |
| |
| if X <= Num (Uns'Last) then |
| Set_Image_Unsigned (To_Unsigned (X), Digs, Ndigs); |
| |
| -- Otherwise, do the conversion in two steps |
| |
| else pragma Assert (X <= 10.0 ** Num'Digits * Num (Uns'Last)); |
| declare |
| Y : constant Uns := To_Unsigned (X / Powten (Num'Digits)); |
| |
| Buf : String (1 .. Num'Digits); |
| Len : Natural; |
| |
| begin |
| Set_Image_Unsigned (Y, Digs, Ndigs); |
| |
| X := X - From_Unsigned (Y) * Powten (Num'Digits); |
| |
| Len := 0; |
| Set_Image_Unsigned (To_Unsigned (X), Buf, Len); |
| |
| for J in 1 .. Num'Digits - Len loop |
| Digs (Ndigs + J) := '0'; |
| end loop; |
| |
| for J in 1 .. Len loop |
| Digs (Ndigs + Num'Digits - Len + J) := Buf (J); |
| end loop; |
| |
| Ndigs := Ndigs + Num'Digits; |
| end; |
| end if; |
| end if; |
| |
| Set_Decimal_Digits (Digs, Ndigs, S, P, Scale, Fore, Aft, Exp); |
| end Set_Image_Real; |
| |
| end System.Image_R; |