Recent StrToInt change and bug? lazarus / 62-General ---------------------------------------------------------------------- MISV · 2022-01-02 15:48 I upgraded Lazarus/FPC some weeks ago I am seeing something very odd StrToInt('$CED4DCE3') gives 1 I would have expected -824910621 (from Delphi) I noticed it because my  code suddenly stopped working after I updated as well. ---------------------------------------------------------------------- Bart · 2022-01-02 16:39 It gives me  EConvertError: "$CED4DCE3" is an invalid integer with fpc trunk. commit 56c1e1a5f2e59a7fe53ca8d02a8f4ac34856b0af (HEAD -> main, origin/main, origin/HEAD) Author: Florian Klämpfl Date:   Tue Dec 7 22:23:43 2021 +0100 Bart ---------------------------------------------------------------------- Bart · 2022-01-02 17:07 See this note in bugreport 39406. Bart ---------------------------------------------------------------------- marcov · 2022-01-02 19:04 Quote from: jamie on January 02, 2022, 07:36:59 pmI am always interested in reports like this since I have two projects off the top of my head that work find with 3.0.4 but nothing newer without any reported errors, just bad generated data and they do use the StrToInt all over the place. Try to run with all checks on, and if that passes, try valgrind. ---------------------------------------------------------------------- MISV · 2022-01-02 20:27 It appears Bart could reproduce my problem on FPC trunk. You can also see here: https://gitlab.com/freepascal.org/fpc/source/- /issues/39406#note_719992897 FPH writes two months ago QuoteI have changed the StrTo* functions so they use now the TryStrTo* functions. There is on problem remaining though: before, (Try)StrToInt accepted hex constants in the range $7fffffff..$ffffffff, now it doesn't anymore. I think those should be accepted. Bart writes two hour ago QuoteIndeed, not accepting those values is backwards incompatible and also not compatible with Delphi (as reported here: https://forum.lazarus.freepascal.org/index.php/topic,57711.0.html) If anyone here have an idea for the best workaround? I need something that gives same results as Delphi because I have many users with data that will be affected if suddenly trying to load. (i.e. it will be a huge mess in many ways) ---------------------------------------------------------------------- Bart · 2022-01-02 21:24 For the time being you could: Implement your own StrToInt (use the implementation from fpc 3.2.2)Use Val() directly instead of StrToIntUse StrToInt64() insteadUse StrToDWord() and cast back to Integer (untested approach) You may take notice that TryStrToInt in trunk is also affected (and the cause of this regression). Bart ---------------------------------------------------------------------- MISV · 2022-01-02 21:50 Quote from: Bart on January 02, 2022, 10:24:40 pmFor the time being you could: Implement your own StrToInt (use the implementation from fpc 3.2.2)Use Val() directly instead of StrToIntUse StrToInt64() insteadUse StrToDWord() and cast back to Integer (untested approach) You may take notice that TryStrToInt in trunk is also affected (and the cause of this regression). Bart I already tried StrToInt64 and... I see now I *ucked* testing that... Sigh. Thank you for your help For reference: MySignedInt64 := StrToInt64('$CED4DCE3'); // 3470056675 MySignedInt32 := Integer(MySignedInt64); // -824910621 like Delphi ---------------------------------------------------------------------- Thaddy · 2022-01-05 12:40 For reference: Delphi uses just val() for strtoint and family, like the older FPC implementation did too. So maybe the changes should be reverted? But the workaround that Marco suggested will also work as long as  - when you put it in a unit - it should be before sysutils in the uses clause.. Sidenote: I think that that also does not draw in exception handling for the routine and thus leads to less code. {$I-} state makes any error still catchable - old school style- and is much cheaper than using exceptions. ---------------------------------------------------------------------- Seenkao · 2022-01-05 14:29 https://forum.lazarus.freepascal.org/index.php/topic,52565.msg423318.h tml#msg423318 Вы можете использовать код из этого топика. По моим проверкам этот код работает быстрее и не должен выдавать ошибок. Минус в том, что вам придётся делать дополнительную переменную, а результат функции будет булева переменная (false/true). Если переменная False - то результата нет, а в переменной для числа будет содержаться нуль. Реализовано на чистом паскале. google translate: You can use the code from this topic. According to my tests, this code is faster and should not generate errors. The downside is that you have to make an additional variable, and the result of the function will be a boolean variable (false/true). If the variable is False, then there is no result, and the variable for the number will contain zero. Implemented in pure pascal. QuoteNow something that is clearly not optimized. StrToInt example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i const  isByte      = 0;                 // len = 3                0..255  isShortInt  = 4;                 // len = 4                -128..127  isWord      = 1;                 // len = 5                0..65535  isSmallInt  = 5;                 // len = 6                -32768..32767  isLongWord  = 2;                 // len = 10             0..4294967295  isInteger   = 6;                 // len = 11             -2147483648..2147483647  {$If defined(CPUX86_64) or defined(aarch64)}  isQWord     = 3;                 // len = 20             0..18446744073709551615  isQInt      = 7;                 // len = 20               -9223372036854775808..9223372036854775807  {$IfEnd} type  geUseParametr = record    maxLen: LongWord;    {$If defined(CPUX86_64) or defined(aarch64)}    maxNumDiv10: QWord;    maxNumeric: QWord;    {$Else}    maxNumDiv10: LongWord;    maxNumeric: LongWord;    {$IfEnd}      var  resInt64: Int64;  // integer ??? procedure SetNumberParametr;      //call at the very beginning. This is to speed up translation work.function geStrToInt(Str: String; Size: LongWord = isInteger): Boolean; implementation var  allUseParametr: array[0..7] of geUseParametr; procedure SetNumberParametr;begin  allUseParametr[isByte].maxLen := 3;  allUseParametr[isByte].maxNumeric := 255;  allUseParametr[isByte].maxNumDiv10 := 25;  allUseParametr[isShortInt].maxLen := 4;  allUseParametr[isShortInt].maxNumeric := 127;  allUseParametr[isShortInt].maxNumDiv10 := 12;  allUseParametr[isWord].maxLen := 5;  allUseParametr[isWord].maxNumeric := 65535;  allUseParametr[isWord].maxNumDiv10 := 6553;  allUseParametr[isSmallInt].maxLen := 6;  allUseParametr[isSmallInt].maxNumeric := 32767;  allUseParametr[isSmallInt].maxNumDiv10 := 3276;  allUseParametr[isLongWord].maxLen := 10;  allUseParametr[isLongWord].maxNumeric := 4294967295;  allUseParametr[isLongWord].maxNumDiv10 := 429496729;  allUseParametr[isInteger].maxLen := 11;  allUseParametr[isInteger].maxNumeric := 2147483647;  allUseParametr[isInteger].maxNumDiv10 := 214748364;  {$If defined(CPUX86_64) or defined(aarch64)}  allUseParametr[isQWord].maxLen := 20;  allUseParametr[isQWord].maxNumeric := 18446744073709551615;  allUseParametr[isQWord].maxNumDiv10 := 1844674407370955161;  allUseParametr[isQInt].maxLen := 20;  allUseParametr[isQInt].maxNumeric := 9223372036854775807;  allUseParametr[isQInt].maxNumDiv10 := 922337203685477580;  {$IfEnd}end;                          // проверки на печатаемые символы не производитсяfunction geStrToInt(Str: String; Size: LongWord = isInteger): Boolean;var  lenStr, i: LongWord;  m, n, z: QWord;begin  Result := False;  if (Size < 4) or (Size > 7) then    Exit;  // обнуляем, и флаг изначально указан, что не действителен  resInt64 := 0;  IntMinus := False;  lenStr := Length(Str);  if lenStr = 0 then    exit;  i := 1;  m := Byte(Str[1]);  if m = 45 then  begin    if lenStr = 1 then      exit;    IntMinus := True;    inc(i);    m := Byte(Str[2]);//      dec(lenStr);  end;  inc(i);  m := m - 48;  // проверяем на установленную длину. Но сначала проверим на знак.  if lenStr > allUseParametr[Size].maxLen then    Exit;  while i < lenStr do  begin    m := m * 10 + (Byte(Str[i]) - 48);    inc(i);  end;  // Если уже превысили, то выходим  if m > allUseParametr[Size].maxNumDiv10 then    exit;  m := m * 10;  z := Byte(Str[i]) - 48;   // обработку размерностей и Word и Int надо разделить  if IntMinus then    n := allUseParametr[Size].maxNumeric + 1 - m  else    n := allUseParametr[Size].maxNumeric - m;  if z > n then    exit;   if IntMinus then    resInt64 := - m - z  else    resInt64 := m + z;  Result := true;end;             ускорение достаточное для платформы x86 и по моему слишком большое для ARM (у меня изменения скорости показывало на Android в 3-12 раз!). Следует учесть, что ни каких исключений в коде не произойдёт. Я от них попросту избавился. А StrToInt-FPC может вызвать исключения. В данном коде если число не будет переведено, то мы об этом узнаем по результату функции Boolean. И можем считать данное число из resInt64, если результат был вычислен. Использовать можно для любых платформ. Google translate: acceleration is sufficient for the x86 platform and, in my opinion, is too large for ARM (my speed changes showed 3-12 times on Android!). Please note that no exceptions will occur in the code. I just got rid of them. StrToInt-FPC can throw exceptions. In this code, if the number is not translated, then we will find out about it by the result of the Boolean function. And we can read the given number from resInt64, if the result was calculated. Can be used for any platform. ---------------------------------------------------------------------- Bart · 2022-01-05 16:54 I think the problem is in Val(), more specific in the fpc_Val_SInt_ShortStr() procedure. I'm working on a patch. Bart ---------------------------------------------------------------------- Bart · 2022-01-05 17:25 Quote from: Bart on January 05, 2022, 05:54:38 pmI think the problem is in Val(), more specific in the fpc_Val_SInt_ShortStr() procedure. I'm working on a patch. Posted a possible fix in the bugtracker. Now waiting for feedback from the fpc devels. Bart ---------------------------------------------------------------------- Seenkao · 2022-01-06 05:38 Я посмотрел ваш код. Почему для 64-х битной системы у вас переменная "L" - longint, а не Int64? google translate: I looked at your code. Why for a 64-bit system you have variable "L" - longint, not Int64? ---------------------------------------------------------------------- Thaddy · 2022-01-06 09:06 ptrint can solve that and adapts to 32/64? https://www.freepascal.org/docs-html/rtl/system/ptrint.html Note that in this case it would not be harmful. Also note the documentation is not fully correct here. It is always the size of a pointer. When in doubt you can always use NativeInt. ---------------------------------------------------------------------- PascalDragon · 2022-01-06 12:32 Quote from: Seenkao on January 06, 2022, 06:38:17 amWhy for a 64-bit system you have variable "L" - longint, not Int64? Quote from: Thaddy on January 06, 2022, 10:06:19 amWhen in doubt you can always use NativeInt. The point of this bug is that a number that fits into a 32-bit value is not correctly converted to such on a 64-bit platform. This isn't about questioning why someone needs this, but about solving a bug in the RTL that shouldn't be there. So please don't suggest work arounds or question the use of a 32-bit signed integer, both of you. ---------------------------------------------------------------------- Bart · 2022-01-06 18:30 I attached a patch to the bugtracker issue resolving the bug in 2 separate Val() compiler procs (at least I hope it fixes it). It needs reviewing by the fpc devels. Bart