------------------------------------------------------------------------------ | |

-- -- | |

-- 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; |