From: Eric Botcazou <ebotca...@adacore.com> The extra digit returned by the function is supposed to be rounded, either by Scan_Integral_Digits or by Scan_Decimal_Digits, but that is not the case when it is the last digit read by Scan_Integral_Digits.
The problem is fixed by rounding it in Scan_Decimal_Digits in this case. gcc/ada/ChangeLog: * libgnat/s-valuer.adb (Scan_Decimal_Digits): Also pretend that the precision limit was just reached if it was already reached. (Scan_Integral_Digits): Add Extra_Rounded out parameter, set it to False on entry and to True when Extra is rounded. (Scan_Raw_Real): New Extra_Rounded local variable. Pass it in the calls to Scan_Integral_Digits. If it is True, pass a dummy extra digit to Scan_Decimal_Digits. Tested on x86_64-pc-linux-gnu, committed on master. --- gcc/ada/libgnat/s-valuer.adb | 47 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/gcc/ada/libgnat/s-valuer.adb b/gcc/ada/libgnat/s-valuer.adb index 46f85e11159..faedb884a6a 100644 --- a/gcc/ada/libgnat/s-valuer.adb +++ b/gcc/ada/libgnat/s-valuer.adb @@ -84,6 +84,7 @@ package body System.Value_R is Scale : out Scale_Array; N : out Positive; Extra : out Char_As_Digit; + Extra_Rounded : out Boolean; Base_Violation : in out Boolean); -- Scan the integral part of a real (i.e. before decimal separator) -- @@ -93,7 +94,7 @@ package body System.Value_R is -- For each digit parsed, either Value := Value * Base + Digit or Scale -- is incremented by 1 if precision limit is reached, in which case the -- remaining digits are still parsed but ignored, except for the first - -- which is stored in Extra. + -- which is stored in Extra, rounded if Extra_Rounded is True. -- -- Base_Violation is set to True if a digit found is not part of the Base -- @@ -207,18 +208,22 @@ package body System.Value_R is -- Number of trailing zeros at a given point begin - -- If initial Scale is not 0 then it means that Precision_Limit was + -- If initial Scale is not 0, then this means that Precision_Limit was -- reached during scanning of the integral part. if Scale (Data_Index'Last) > 0 then Precision_Limit_Reached := True; + + if Round then + Precision_Limit_Just_Reached := True; + end if; else Extra := 0; Precision_Limit_Reached := False; - end if; - if Round then - Precision_Limit_Just_Reached := False; + if Round then + Precision_Limit_Just_Reached := False; + end if; end if; -- Initialize trailing zero counter @@ -373,6 +378,7 @@ package body System.Value_R is Scale : out Scale_Array; N : out Positive; Extra : out Char_As_Digit; + Extra_Rounded : out Boolean; Base_Violation : in out Boolean) is pragma Assert (Base in 2 .. 16); @@ -398,12 +404,13 @@ package body System.Value_R is -- Temporary begin - -- Initialize N, Value, Scale and Extra + -- Initialize N, Value, Scale, Extra and Extra_Rounded N := 1; Value := (others => 0); Scale := (others => 0); Extra := 0; + Extra_Rounded := False; Precision_Limit_Reached := False; @@ -443,6 +450,7 @@ package body System.Value_R is if Round and then Precision_Limit_Just_Reached then Round_Extra (Digit, Base, Value (N), Scale (N), Extra); + Extra_Rounded := True; Precision_Limit_Just_Reached := False; end if; @@ -536,6 +544,9 @@ package body System.Value_R is -- If True some digits where not in the base. The real is still scanned -- till the end even if an error will be raised. + Extra_Rounded : Boolean; + -- True if Extra has been rounded + N : Positive; -- Index number of the current part @@ -585,7 +596,7 @@ package body System.Value_R is Scan_Integral_Digits (Str, Index, Max, Base, False, Value, Scale, N, - Char_As_Digit (Extra), Base_Violation); + Char_As_Digit (Extra), Extra_Rounded, Base_Violation); -- A dot is allowed only if followed by a digit (RM 3.5(39.8)) @@ -599,6 +610,7 @@ package body System.Value_R is Value := (others => 0); Scale := (others => 0); Extra := 0; + Extra_Rounded := False; else Bad_Value (Str); @@ -648,7 +660,7 @@ package body System.Value_R is Scan_Integral_Digits (Str, Index, Max, Base, Base_Char /= ASCII.NUL, Value, Scale, - N, Char_As_Digit (Extra), Base_Violation); + N, Char_As_Digit (Extra), Extra_Rounded, Base_Violation); end if; -- Do we have a dot? @@ -673,9 +685,22 @@ package body System.Value_R is if After_Point then pragma Assert (Index <= Max); - Scan_Decimal_Digits - (Str, Index, Max, Base, Base_Char /= ASCII.NUL, Value, Scale, - N, Char_As_Digit (Extra), Base_Violation); + -- If Extra has been rounded, we are done with it + + if Extra_Rounded then + declare + Dummy : Unsigned := 0; + begin + Scan_Decimal_Digits + (Str, Index, Max, Base, Base_Char /= ASCII.NUL, Value, Scale, + N, Dummy, Base_Violation); + end; + + else + Scan_Decimal_Digits + (Str, Index, Max, Base, Base_Char /= ASCII.NUL, Value, Scale, + N, Char_As_Digit (Extra), Base_Violation); + end if; end if; -- If an explicit base was specified ensure that the delimiter is found -- 2.43.0