]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ada: Fix various issues in System.Value_F.Integer_To_Fixed function
authorEric Botcazou <ebotcazou@adacore.com>
Wed, 2 Apr 2025 08:02:18 +0000 (10:02 +0200)
committerMarc Poulhiès <dkm@gcc.gnu.org>
Thu, 12 Jun 2025 08:37:53 +0000 (10:37 +0200)
The first issue is that the function would wrongly raise Constraint_Error
on the edge case where Val = 2**(Int'Size - 1) and Minus is not set.

The second issue is that some runtimes are compiled with -gnatp and would
fail to raise Constraint_Error when the sum of the terms overflows an Int.

The third issue is that the function takes a long time to deal with huge
negative exponents.

gcc/ada/ChangeLog:

* libgnat/s-valuef.adb (Integer_To_Fixed): Enable overflow checks.
Deal specifically with Val = 2**(Int'Size - 1) if Minus is not set.
Exit the loop when V saturates to 0 in the case of (huge) negative
exponents.

gcc/ada/libgnat/s-valuef.adb

index 993074041af08d1f5ba3d418ea13bbbe52414ae8..7baa3b31ff420111ef23347a918edf2d242ac751 100644 (file)
@@ -156,6 +156,9 @@ package body System.Value_F is
       pragma Assert (Num < 0 and then Den < 0);
       --  Accept only negative numbers to allow -2**(Int'Size - 1)
 
+      pragma Unsuppress (Overflow_Check);
+      --  Use overflow check to catch bad values
+
       function Safe_Expont
         (Base   : Int;
          Exp    : in out Natural;
@@ -224,38 +227,52 @@ package body System.Value_F is
 
       B : constant Int := Int (Base);
 
-      V : Uns := Val;
-      E : Uns := Uns (Extra);
+      V : Uns     := Val;
+      S : Integer := ScaleB;
+      E : Uns     := Uns (Extra);
 
       Y, Z, Q1, R1, Q2, R2 : Int;
 
    begin
+      --  The implementation of Value_R uses fully symmetric arithmetics
+      --  but here we cannot handle 2**(Int'Size - 1) if Minus is not set.
+
+      if V = 2**(Int'Size - 1) and then not Minus then
+         E := V rem Uns (B);
+         V := V / Uns (B);
+         S := S + 1;
+      end if;
+
       --  We will use a scaled divide operation for which we must control the
       --  magnitude of operands so that an overflow exception is not unduly
       --  raised during the computation. The only real concern is the exponent.
 
-      --  If ScaleB is too negative, then drop trailing digits, but preserve
-      --  the last dropped digit.
+      --  If S is too negative, then drop trailing digits, but preserve the
+      --  last dropped digit, until V saturates to 0.
 
-      if ScaleB < 0 then
+      if S < 0 then
          declare
-            LS : Integer := -ScaleB;
+            LS : Integer := -S;
 
          begin
             Y := Den;
             Z := Safe_Expont (B, LS, Num);
 
             for J in 1 .. LS loop
+               if V = 0 then
+                  E := 0;
+                  exit;
+               end if;
                E := V rem Uns (B);
                V := V / Uns (B);
             end loop;
          end;
 
-      --  If ScaleB is too positive, then scale V up, which may then overflow
+      --  If S is too positive, then scale V up, which may then overflow
 
-      elsif ScaleB > 0 then
+      elsif S > 0 then
          declare
-            LS : Integer := ScaleB;
+            LS : Integer := S;
 
          begin
             Y := Safe_Expont (B, LS, Den);
@@ -271,7 +288,7 @@ package body System.Value_F is
             end loop;
          end;
 
-      --  If ScaleB is zero, then proceed directly
+      --  If S is zero, then proceed directly
 
       else
          Y := Den;