Having too many special cases can be counter-productive as shown here.

Tested on x86-64/Linux, applied on the mainline and 16 branch.

RMs, I'd like to backport the fixlet for the 15.3 release, any objections?


2026-06-09  Eric Botcazou  <[email protected]>

        PR ada/125695
        * libgnat/s-genbig.adb ("**"): Do not drop the sign on the floor.
        (Big_Exp): Take into account the parity of the exponent for -2.


2026-06-09  Eric Botcazou  <[email protected]>

        * gnat.dg/bigint1.adb: New  test.

-- 
Eric Botcazou
diff --git a/gcc/ada/libgnat/s-genbig.adb b/gcc/ada/libgnat/s-genbig.adb
index f5ed11c06a4..50f724b060a 100644
--- a/gcc/ada/libgnat/s-genbig.adb
+++ b/gcc/ada/libgnat/s-genbig.adb
@@ -262,7 +262,7 @@ package body System.Generic_Bignums is
          --  X ** 1 is X
 
          when 1 =>
-            return Normalize (X.D);
+            return Normalize (X.D, X.Neg);
 
          --  X ** 2 is X * X
 
@@ -331,14 +331,14 @@ package body System.Generic_Bignums is
       elsif Y.Len > 1 then
          raise Storage_Error with "exponentiation result is too large";
 
-      --  Special case (+/-)2 ** K, where K is 1 .. 31 using a shift
+      --  Special case (+/-)2 ** K, where K is in 1 .. 31, using a left shift
 
       elsif X.Len = 1 and then X.D (1) = 2 and then Y.D (1) < 32 then
          declare
             D : constant Digit_Vector (1 .. 1) :=
                   [Shift_Left (SD'(1), Natural (Y.D (1)))];
          begin
-            return Normalize (D, X.Neg);
+            return Normalize (D, X.Neg and then (Y.D (1) and 1) = 1);
          end;
 
       --  Remaining cases have right operand of one word
-- { dg-do run }
-- { dg-options "-gnat2022 -gnata" }

with Ada.Numerics.Big_Numbers.Big_Integers;
use Ada.Numerics.Big_Numbers.Big_Integers;

procedure Bigint1 is

  Minus_One   : constant Big_Integer := To_Big_Integer (-1);
  Minus_Two   : constant Big_Integer := To_Big_Integer (-2);
  Minus_Three : constant Big_Integer := To_Big_Integer (-3);

begin

  pragma Assert (Minus_One ** 1 = Minus_One);
  pragma Assert (Minus_One ** 2 = To_Big_Integer (1));
  pragma Assert (Minus_One ** 3 = Minus_One);

  pragma Assert (Minus_Two ** 1 = Minus_Two);
  pragma Assert (Minus_Two ** 2 = To_Big_Integer (4));
  pragma Assert (Minus_Two ** 3 = To_Big_Integer (-8));

  pragma Assert (Minus_Three ** 1 = Minus_Three);
  pragma Assert (Minus_Three ** 2 = To_Big_Integer (9));
  pragma Assert (Minus_Three ** 3 = To_Big_Integer (-27));

end;

Reply via email to