From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by sourceware.org (Postfix) with ESMTPS id 59D383858421 for ; Thu, 5 Jan 2023 14:39:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 59D383858421 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=adacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=adacore.com Received: by mail-wm1-x32b.google.com with SMTP id i17-20020a05600c355100b003d99434b1cfso1474884wmq.1 for ; Thu, 05 Jan 2023 06:39:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=adacore.com; s=google; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=ZMtsb6wOXD8VyPoOpw2dUiXILueMimS9L2lfeh4U2EA=; b=MJrX+co/lceodBFWsRJFBgHOXbYOZxyMNgyT6x4J1QeEhPLRgTLFzT8RLmjmN00ilc 9DWKd79gzqsJMHzmQc7x+cGHjaTHouR4NxzgUD5dA0/xgNSGHyD4m0GihajNUrfX3I9w 0mZc3LzshpxLnLGTZjc0Rc1dZ4EFBpiC67fdYZa0yBCiaN+eVpt2AAg+5d5yB4a+md1N XVH+ANSN3+2KV6xj9T3eNVZTjsn446+E8glvpIMBaTOqz/Cvm6Sobs53sc7MoVsZXibH BtlFP19CYdfutwRuoWmP3h6P/E8ODasvba2GGPFuTFewplw+8WL1mWVFjW2rQp9VVMvz bLXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=ZMtsb6wOXD8VyPoOpw2dUiXILueMimS9L2lfeh4U2EA=; b=qiuh68pcy1V99mevpTTZGVIWAXc5h6uGKMgEfNm4HOzgVqhKZeLo3ElVoZ8K3YpbSS mWPF0EIMc3A6xYR2MVB/bdKN4I/PJ9Da1zcqzhyyefyC8ZMG9u7syblPle4K8EBnEuen C3kU0KGawygbF6bgeMR6mJzMscPM1Init/KHX++FyQgE3klar5ahIiquZlyq/P4jW2Ev LUFbchnPP9fVfuGLbWF2jHPZdrhLx0FBz/VNDNmDh0mwmTEoxLLkTbGd5p4rZ5E5QdvE t64gUMlt/wm+G2cfeCnK0LIg89y8hNcBlc6ClUkc+WVVBE20digl7u6vcV8+oyBy32dD UgKg== X-Gm-Message-State: AFqh2kpOlrmBy7KlSyoNogJrgSHvy2yTzhBrp4MSsI4YMG+U/7RVtCZh yv1WEYhI1jGjUrGpz4XzMuZe0QCTa+lvsIlrPuo= X-Google-Smtp-Source: AMrXdXs5LcTU4vTUsqokuBMldPXRVSXN7f3cwh4XTNJbvKIyrl38fUr4mzhxpQQ9VVcMYm3SmsUdww== X-Received: by 2002:a05:600c:54c2:b0:3d3:3c74:dbd0 with SMTP id iw2-20020a05600c54c200b003d33c74dbd0mr36197370wmb.13.1672929567078; Thu, 05 Jan 2023 06:39:27 -0800 (PST) Received: from poulhies-Precision-5550.lan (static-176-191-105-132.ftth.abo.bbox.fr. [176.191.105.132]) by smtp.gmail.com with ESMTPSA id q187-20020a1c43c4000000b003c6c182bef9sm3114421wma.36.2023.01.05.06.39.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Jan 2023 06:39:26 -0800 (PST) From: =?UTF-8?q?Marc=20Poulhi=C3=A8s?= To: gcc-patches@gcc.gnu.org Cc: Ronan Desplanques Subject: [COMMITTED] ada: Adjust handling of "%g" in GNAT.Formatted_String Date: Thu, 5 Jan 2023 15:39:24 +0100 Message-Id: <20230105143924.155757-1-poulhies@adacore.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-13.2 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_ASCII_DIVIDERS,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: From: Ronan Desplanques The way the "%g" specifier was handled by GNAT.Formatted_String before this patch was very different from the behavior of C's printf. This patch makes the handling of "%g" in GNAT.Formatted_String closer to the behavior described in the specification of the C language. gcc/ada/ * libgnat/g-forstr.adb (F_Kind): Rename enumeration literal. (P_Flt_Format): Adjust handling of "%g". (Determine_Notation_And_Aft): New procedure. (Decimal_Exponent): New function. (Increment_Integral_Part): New procedure. (Remove_Extraneous_Decimal_Digit): New procedure. (Trim_Fractional_Part): New procedure. * libgnat/g-forstr.ads: Change description of "%g" specifier. Tested on x86_64-pc-linux-gnu, committed on master. --- gcc/ada/libgnat/g-forstr.adb | 341 +++++++++++++++++++++++++++++++---- gcc/ada/libgnat/g-forstr.ads | 6 +- 2 files changed, 313 insertions(+), 34 deletions(-) diff --git a/gcc/ada/libgnat/g-forstr.adb b/gcc/ada/libgnat/g-forstr.adb index 2179818bba4..a58b1702eca 100644 --- a/gcc/ada/libgnat/g-forstr.adb +++ b/gcc/ada/libgnat/g-forstr.adb @@ -34,6 +34,7 @@ with Ada.Float_Text_IO; with Ada.Integer_Text_IO; with Ada.Long_Float_Text_IO; with Ada.Long_Integer_Text_IO; +with Ada.Strings; with Ada.Strings.Fixed; with Ada.Unchecked_Deallocation; @@ -49,8 +50,8 @@ package body GNAT.Formatted_String is Decimal_Float, -- %f %F Decimal_Scientific_Float, -- %e Decimal_Scientific_Float_Up, -- %E - Shortest_Decimal_Float, -- %g - Shortest_Decimal_Float_Up, -- %G + G_Specifier, -- %g + G_Specifier_Up, -- %G Char, -- %c Str, -- %s Pointer -- %p @@ -58,7 +59,7 @@ package body GNAT.Formatted_String is type Sign_Kind is (Neg, Zero, Pos); - subtype Is_Number is F_Kind range Decimal_Int .. Shortest_Decimal_Float_Up; + subtype Is_Number is F_Kind range Decimal_Int .. G_Specifier_Up; type F_Sign is (If_Neg, Forced, Space) with Default_Value => If_Neg; @@ -77,6 +78,8 @@ package body GNAT.Formatted_String is Value_Needed : Natural range 0 .. 2 := 0; end record; + type Notation is (Decimal, Scientific); + procedure Advance_And_Accumulate_Until_Next_Specifier (Format : Formatted_String); -- Advance Format.D.Index until either the next format specifier is @@ -90,12 +93,30 @@ package body GNAT.Formatted_String is -- Parse the next format specifier, a format specifier has the following -- syntax: %[flags][width][.precision][length]specifier + procedure Determine_Notation_And_Aft + (Exponent : Integer; + Precision : Text_IO.Field; + Nota : out Notation; + Aft : out Text_IO.Field); + -- Determine whether to use scientific or decimal notation and the value of + -- Aft given the exponent and precision of a real number, as described in + -- the C language specification, section 7.21.6.1. + function Get_Formatted (F_Spec : F_Data; Value : String; Len : Positive) return String; -- Returns Value formatted given the information in F_Spec + procedure Increment_Integral_Part + (Buffer : in out String; + First_Non_Blank : in out Positive; + Last_Digit_Position : Positive); + -- Buffer must contain the textual representation of a number. + -- Last_Digit_Position must be the position of the rightmost digit of the + -- integral part. Buffer must have at least one padding blank. Increment + -- the integral part. + procedure Raise_Wrong_Format (Format : Formatted_String) with No_Return; -- Raise the Format_Error exception which information about the context @@ -128,6 +149,18 @@ package body GNAT.Formatted_String is Var : Int) return Formatted_String; -- Generic routine which handles all the integer numbers + procedure Remove_Extraneous_Decimal_Digit + (Textual_Rep : in out String; + First_Non_Blank : in out Positive); + -- Remove the unique digit to the right of the point in Textual_Rep + + procedure Trim_Fractional_Part + (Textual_Rep : in out String; + First_Non_Blank : in out Positive); + -- Remove trailing zeros from Textual_Rep, which must be the textual + -- representation of a real number. If the fractional part only contains + -- zeros, also remove the point. + --------- -- "+" -- --------- @@ -335,6 +368,30 @@ package body GNAT.Formatted_String is end loop; end Advance_And_Accumulate_Until_Next_Specifier; + -------------------------------- + -- Determine_Notation_And_Aft -- + -------------------------------- + + procedure Determine_Notation_And_Aft + (Exponent : Integer; + Precision : Text_IO.Field; + Nota : out Notation; + Aft : out Text_IO.Field) + is + -- The constants use the same names as those from the C specification + -- in order to match the description of the predicate. + P : constant Text_IO.Field := (if Precision /= 0 then Precision else 1); + X : constant Integer := Exponent; + begin + if P > X and X >= -4 then + Nota := Decimal; + Aft := P - (X + 1); + else + Nota := Scientific; + Aft := P - 1; + end if; + end Determine_Notation_And_Aft; + -------------------- -- Decimal_Format -- -------------------- @@ -462,6 +519,35 @@ package body GNAT.Formatted_String is end; end Get_Formatted; + ----------------------------- + -- Increment_Integral_Part -- + ----------------------------- + + procedure Increment_Integral_Part + (Buffer : in out String; + First_Non_Blank : in out Positive; + Last_Digit_Position : Positive) + is + Cursor : Natural := Last_Digit_Position; + begin + while Buffer (Cursor) = '9' loop + Buffer (Cursor) := '0'; + Cursor := Cursor - 1; + end loop; + + pragma Assert (Cursor > 0); + + if Buffer (Cursor) in '0' .. '8' then + Buffer (Cursor) := Character'Succ (Buffer (Cursor)); + else + Ada.Strings.Fixed.Insert + (Buffer, + Cursor + 1, + "1"); + First_Non_Blank := First_Non_Blank - 1; + end if; + end Increment_Integral_Part; + ---------------- -- Int_Format -- ---------------- @@ -639,8 +725,8 @@ package body GNAT.Formatted_String is when 'f' | 'F' => F_Spec.Kind := Decimal_Float; when 'e' => F_Spec.Kind := Decimal_Scientific_Float; when 'E' => F_Spec.Kind := Decimal_Scientific_Float_Up; - when 'g' => F_Spec.Kind := Shortest_Decimal_Float; - when 'G' => F_Spec.Kind := Shortest_Decimal_Float_Up; + when 'g' => F_Spec.Kind := G_Specifier; + when 'G' => F_Spec.Kind := G_Specifier_Up; when 'o' => F_Spec.Kind := Unsigned_Octal; when 'x' => F_Spec.Kind := Unsigned_Hexadecimal_Int; when 'X' => F_Spec.Kind := Unsigned_Hexadecimal_Int_Up; @@ -677,12 +763,156 @@ package body GNAT.Formatted_String is (Format : Formatted_String; Var : Flt) return Formatted_String is + procedure Compute_Exponent + (Var : Flt; + Valid : out Boolean; + Exponent : out Integer); + -- If Var is invalid (for example, a NaN of an inf), set Valid False and + -- set Exponent to 0. Otherwise, set Valid True, and store the exponent + -- of the scientific notation representation of Var in Exponent. The + -- exponent can also be defined as: + -- - If Var = 0, 0. + -- - Otherwise, Floor (Log_10 (Abs (Var))). + + procedure Format_With_Notation + (Var : Flt; + Nota : Notation; + Aft : Text_IO.Field; + Buffer : out String); + -- Fill buffer with the formatted value of Var following the notation + -- specified through Nota. + + procedure Handle_G_Specifier + (Buffer : out String; + First_Non_Blank : out Positive; + Aft : Text_IO.Field); + -- Fill Buffer with the formatted value of Var according to the rules of + -- the "%g" specifier. Buffer is right-justified and padded with blanks. + + ---------------------- + -- Compute_Exponent -- + ---------------------- + + procedure Compute_Exponent + (Var : Flt; + Valid : out Boolean; + Exponent : out Integer) + is + -- The way the exponent is computed is convoluted. It is not possible + -- to use the logarithm in base 10 of Var and floor it, because the + -- math functions for this are not available for fixed point types. + -- Instead, use the generic Put procedure to produce a scientific + -- representation of Var, and parse the exponent part of that back + -- into an Integer. + Scientific_Rep : String (1 .. 50); + + E_Position : Natural; + begin + Put (Scientific_Rep, Var, Aft => 1, Exp => 1); + + E_Position := Ada.Strings.Fixed.Index (Scientific_Rep, "E"); + + if E_Position = 0 then + Valid := False; + Exponent := 0; + else + Valid := True; + Exponent := + Integer'Value + (Scientific_Rep (E_Position + 1 .. Scientific_Rep'Last)); + end if; + end Compute_Exponent; + + -------------------------- + -- Format_With_Notation -- + -------------------------- + + procedure Format_With_Notation + (Var : Flt; + Nota : Notation; + Aft : Text_IO.Field; + Buffer : out String) + is + Exp : constant Text_IO.Field := + (case Nota is when Decimal => 0, when Scientific => 3); + begin + Put (Buffer, Var, Aft, Exp); + end Format_With_Notation; + + ------------------------ + -- Handle_G_Specifier -- + ------------------------ + + procedure Handle_G_Specifier + (Buffer : out String; + First_Non_Blank : out Positive; + Aft : Text_IO.Field) + is + -- There is nothing that is directly equivalent to the "%g" specifier + -- in the standard Ada functionality provided by Ada.Text_IO. The + -- procedure Put will still be used, but significant postprocessing + -- will be performed on the output of that procedure. + + -- The following code is intended to match the behavior of C's printf + -- for %g, as described by paragraph "7.21.6.1 The fprintf function" + -- of the C language specification. + + -- As explained in the C specification, we're going to have to make a + -- choice between decimal notation and scientific notation. One of + -- the elements we need in order to make that choice is the value of + -- the exponent in the decimal representation of Var. We will store + -- that value in Exponent. + Exponent : Integer; + Valid : Boolean; + + Nota : Notation; + + -- The value of the formal Aft comes from the precision specifier in + -- the format string. For %g, the precision specifier corresponds to + -- the number of significant figures desired, whereas the formal Aft + -- in Put corresponds to the number of digits after the point. + -- Effective_Aft is what will be passed to Put as Aft in order to + -- respect the semantics of %g. + Effective_Aft : Text_IO.Field; + + Textual_Rep : String (Buffer'Range); + begin + Compute_Exponent (Var, Valid, Exponent); + + Determine_Notation_And_Aft + (Exponent, Aft, Nota, Effective_Aft); + + Format_With_Notation (Var, Nota, Effective_Aft, Textual_Rep); + + First_Non_Blank := Strings.Fixed.Index_Non_Blank (Textual_Rep); + + if not Valid then + null; + elsif Effective_Aft = 0 then + -- Special case: it is possible at this point that Effective_Aft + -- is zero. But when Put is passed zero through Aft, it still + -- outputs one digit after the point. See the reference manual, + -- A.10.9.25. + + Remove_Extraneous_Decimal_Digit (Textual_Rep, First_Non_Blank); + else + Trim_Fractional_Part + (Textual_Rep, First_Non_Blank); + end if; + + Buffer := Textual_Rep; + end Handle_G_Specifier; + + -- Local variables + F : F_Data; Buffer : String (1 .. 50); S, E : Positive := 1; Start : Positive; Aft : Text_IO.Field; + -- Start of processing for P_Flt_Format + begin Next_Format (Format, F, Start); @@ -716,36 +946,13 @@ package body GNAT.Formatted_String is Characters.Handling.To_Lower (Buffer (S .. E)); end if; - when Shortest_Decimal_Float - | Shortest_Decimal_Float_Up + when G_Specifier + | G_Specifier_Up => - -- Without exponent - - Put (Buffer, Var, Aft, Exp => 0); - S := Strings.Fixed.Index_Non_Blank (Buffer); + Handle_G_Specifier (Buffer, S, Aft); E := Buffer'Last; - -- Check with exponent - - declare - Buffer2 : String (1 .. 50); - S2, E2 : Positive; - - begin - Put (Buffer2, Var, Aft, Exp => 3); - S2 := Strings.Fixed.Index_Non_Blank (Buffer2); - E2 := Buffer2'Last; - - -- If with exponent it is shorter, use it - - if (E2 - S2) < (E - S) then - Buffer := Buffer2; - S := S2; - E := E2; - end if; - end; - - if F.Kind = Shortest_Decimal_Float then + if F.Kind = G_Specifier then Buffer (S .. E) := Characters.Handling.To_Lower (Buffer (S .. E)); end if; @@ -988,4 +1195,74 @@ package body GNAT.Formatted_String is & Positive'Image (Format.D.Current); end Raise_Wrong_Format; + ------------------------------------- + -- Remove_Extraneous_Decimal_Digit -- + ------------------------------------- + + procedure Remove_Extraneous_Decimal_Digit + (Textual_Rep : in out String; + First_Non_Blank : in out Positive) + is + Point_Position : constant Positive := Ada.Strings.Fixed.Index + (Textual_Rep, + ".", + First_Non_Blank); + + Integral_Part_Needs_Increment : constant Boolean := + Textual_Rep (Point_Position + 1) in '5' .. '9'; + begin + Ada.Strings.Fixed.Delete + (Textual_Rep, + Point_Position, + Point_Position + 1, + Ada.Strings.Right); + + First_Non_Blank := First_Non_Blank + 2; + + if Integral_Part_Needs_Increment then + Increment_Integral_Part + (Textual_Rep, + First_Non_Blank, + Last_Digit_Position => Point_Position + 1); + end if; + end Remove_Extraneous_Decimal_Digit; + + -------------------------- + -- Trim_Fractional_Part -- + -------------------------- + + procedure Trim_Fractional_Part + (Textual_Rep : in out String; + First_Non_Blank : in out Positive) + is + Cursor : Positive := + Ada.Strings.Fixed.Index (Textual_Rep, ".", First_Non_Blank); + + First_To_Trim : Positive; + Fractional_Part_Last : Positive; + begin + while Cursor + 1 <= Textual_Rep'Last + and then Textual_Rep (Cursor + 1) in '0' .. '9' loop + Cursor := Cursor + 1; + end loop; + + Fractional_Part_Last := Cursor; + + while Textual_Rep (Cursor) = '0' loop + Cursor := Cursor - 1; + end loop; + + if Textual_Rep (Cursor) = '.' then + Cursor := Cursor - 1; + end if; + + First_To_Trim := Cursor + 1; + + Ada.Strings.Fixed.Delete + (Textual_Rep, First_To_Trim, Fractional_Part_Last, Ada.Strings.Right); + + First_Non_Blank := + First_Non_Blank + (Fractional_Part_Last - First_To_Trim + 1); + end Trim_Fractional_Part; + end GNAT.Formatted_String; diff --git a/gcc/ada/libgnat/g-forstr.ads b/gcc/ada/libgnat/g-forstr.ads index c0f3aa6e354..423d7794c34 100644 --- a/gcc/ada/libgnat/g-forstr.ads +++ b/gcc/ada/libgnat/g-forstr.ads @@ -70,8 +70,10 @@ -- F Decimal floating point, uppercase -- e Scientific notation (mantissa/exponent), lowercase -- E Scientific notation (mantissa/exponent), uppercase --- g Use the shortest representation: %e or %f --- G Use the shortest representation: %E or %F +-- g Interpret the precision as the number of significant figures, +-- choose the most adequate of decimal or scientific notation, and +-- trim trailing zeroes and point. Letters are in lowercase. +-- G Same as g, except that letters are in uppercase -- c Character -- s String of characters -- p Pointer address -- 2.34.1