Al T 12 Posted August 18, 2022 (edited) If you swap them, you get error "Duplicate case label". What to do? function cntCaseTbl(Z: Int64): Int64; begin case Z of 0..9: Exit(1); 10..99 : Exit(2); 100..999: Exit(3); 1000..9999: Exit(4); 10000..99999: Exit(5); 100000..999999: Exit(6); 1000000..9999999: Exit(7); 10000000..99999999: Exit(8); 100000000..999999999: Exit(9); 1000000000..9999999999: Exit(10); 10000000000..99999999999: Exit(11); //<--------Low bound exceeds high bound 100000000000..999999999999: Exit(12); //<--------Low bound exceeds high bound 1000000000000..9999999999999: Exit(13); //<--------Duplicate case label 10000000000000..99999999999999: Exit(14); //<--------Low bound exceeds high bound 100000000000000..999999999999999: Exit(15); //<--------Low bound exceeds high bound 1000000000000000..9999999999999999: Exit(16); //<--------Duplicate case label 10000000000000000..99999999999999999: Exit(17); //<--------Low bound exceeds high bound 100000000000000000..999999999999999999: Exit(18); //<--------Low bound exceeds high bound 1000000000000000000..9223372036854775807: Exit(19); //<--------Duplicate case label end; end; Edited August 18, 2022 by Al T Share this post Link to post
David Heffernan 2345 Posted August 18, 2022 (edited) Compiler sees these as 32 bit Integer. Does it help to write Int64(10000000000) .. Int64(99999999999) Also your case statement doesn't handle negative input values. Edited August 18, 2022 by David Heffernan Share this post Link to post
Al T 12 Posted August 18, 2022 17 hours ago, David Heffernan said: Compiler sees these as 32 bit Integer. Does it help to write Int64(10000000000) .. Int64(99999999999) Also your case statement doesn't handle negative input values. See attached picture. Still Error after adding int64() and Abs(). Share this post Link to post
Brian Evans 105 Posted August 19, 2022 (edited) The help does mention the selector expression is limited to 32 bit ordinal values and case values must match that type. Declarations and Statements (Delphi) - RAD Studio (embarcadero.com) So it looks like case expression (Z in your test) is being evaluated/converted into an Int32 and the case values that can't also be converted produce errors. Edited August 19, 2022 by Brian Evans Share this post Link to post
David Heffernan 2345 Posted August 19, 2022 9 hours ago, Al T said: Still Error after adding int64() and Abs(). Abs isn't going to help at all. The problem is that the compiler is seeing these values as 32 bit integers. Which is why I suggested the Int64 cast. But Brian appears to have the answer by the excellent method of reading the documentation. I should have done that! Share this post Link to post
Brian Evans 105 Posted August 19, 2022 (edited) Would also add that here in the default Debug configuration I get a warning plus the same error for each out of range value and not all the odd errors seen in Release. [dcc32 Warning] Unit1.pas(40): W1012 Constant expression violates subrange bounds [dcc32 Error] Unit1.pas(40): E2026 Constant expression expected [dcc32 Warning] Unit1.pas(40): W1012 Constant expression violates subrange bounds [dcc32 Error] Unit1.pas(40): E2026 Constant expression expected Also Release does also get the same Warnings along with the odd errors. [dcc32 Warning] Unit1.pas(40): W1012 Constant expression violates subrange bounds [dcc32 Warning] Unit1.pas(40): W1012 Constant expression violates subrange bounds [dcc32 Error] Unit1.pas(40): E2011 Low bound exceeds high bound Edited August 19, 2022 by Brian Evans Share this post Link to post
Al T 12 Posted August 19, 2022 I assumed Delphi could handle 64-bit code, since they claim they have 64-bit compilers, but after doing some research it seems they haven't made case statements compatible with 64-bits: "where selectorExpression is any expression of an ordinal type smaller than 32 bits (string types and ordinals larger than 32 bits are invalid) and each caseList is one of the following:" from https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Declarations_and_Statements_(Delphi)#Case_Statements I have the enterprise version... Could I make the case statement use 64-bit integers? Or maybe create a 64-bit case? Share this post Link to post
Stefan Glienke 2002 Posted August 19, 2022 (edited) That code reminds me of what I have seen in SysUtils _IntToStr64 and _IntToStr32 (getting the number of digits) Also see https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 Edited August 19, 2022 by Stefan Glienke Share this post Link to post
David Heffernan 2345 Posted August 19, 2022 4 hours ago, Al T said: I assumed Delphi could handle 64-bit code, since they claim they have 64-bit compilers, but after doing some research it seems they haven't made case statements compatible with 64-bits: "where selectorExpression is any expression of an ordinal type smaller than 32 bits (string types and ordinals larger than 32 bits are invalid) and each caseList is one of the following:" from https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Declarations_and_Statements_(Delphi)#Case_Statements I have the enterprise version... Could I make the case statement use 64-bit integers? Or maybe create a 64-bit case? You can use an if statement Share this post Link to post
Nigel Thomas 35 Posted August 20, 2022 17 hours ago, David Heffernan said: But Brian appears to have the answer by the excellent method of reading the documentation. I should have done that! Like you did back in 2013 🙂 : SO 14647108 If statements were the answer for that scenario as well. 1 Share this post Link to post
Fr0sT.Brutal 900 Posted August 22, 2022 (edited) On 8/19/2022 at 7:24 PM, Al T said: Could I make the case statement use 64-bit integers? You can wrap two 32-bit `case`-s in one `if` if num <= High(UInt32) then case num else case num shr 32 Edited August 22, 2022 by Fr0sT.Brutal Share this post Link to post
DelphiUdIT 176 Posted August 22, 2022 (edited) May be you can write the function in this way ? function cntCaseTbl(Z: Int64): Int64; begin if z >= 0 then Result := Trunc(Log10(z))+1; else Result := 0; //This should be, otherwise the result will be random value end; Edited August 22, 2022 by DelphiUdIT 1 1 Share this post Link to post
David Heffernan 2345 Posted August 22, 2022 4 hours ago, DelphiUdIT said: May be you can write the function in this way ? function cntCaseTbl(Z: Int64): Int64; begin if z >= 0 then Result := Trunc(Log10(z))+1; else Result := 0; //This should be, otherwise the result will be random value end; I guess I'd be a bit concerned about floating point use here when Log10 returns an exact integer 1 Share this post Link to post
DelphiUdIT 176 Posted August 22, 2022 5 hours ago, David Heffernan said: I guess I'd be a bit concerned about floating point use here when Log10 returns an exact integer I have tried all limit values 1, 10, 100, 1000, 10000, ....., 9, 99, 999, 9999, 99999, ...... And all the returned values are correct. 1 Share this post Link to post
DelphiUdIT 176 Posted August 22, 2022 8 minutes ago, Leif Uneus said: Does not work for z = 0 though. My mistake. This is the correct code: function cntCaseTbl(Z: Int64): Int64; begin Result := 0; //This should be, otherwise the result will be random value if z > 0 then Result := Trunc(Log10(z))+1; else if z = 0 then Result := 1; end; 2 Share this post Link to post
Leif Uneus 43 Posted August 23, 2022 (edited) Almost a magnitude faster algorithm: function AltCntCaseTbl( v : Int64) : Int64; inline; begin if (v < 0) then Exit(0); if (v > MaxLongInt) then begin if (v >= UInt64(10000000000000000000)) then Exit(20); if (v >= UInt64(1000000000000000000)) then Exit(19); if (v >= UInt64(100000000000000000)) then Exit(18); if (v >= UInt64(10000000000000000)) then Exit(17); if (v >= UInt64(1000000000000000)) then Exit(16); if (v >= UInt64(100000000000000)) then Exit(15); if (v >= UInt64(10000000000000)) then Exit(14); if (v >= UInt64(1000000000000)) then Exit(13); if (v >= UInt64(100000000000)) then Exit(12); if (v >= UInt64(10000000000)) then Exit(11); end; if (v >= UInt64(1000000000)) then Exit(10); if (v >= UInt64(100000000)) then Exit(9); if (v >= UInt64(10000000)) then Exit(8); if (v >= UInt64(1000000)) then Exit(7); if (v >= UInt64(100000)) then Exit(6); if (v >= UInt64(10000)) then Exit(5); if (v >= UInt64(1000)) then Exit(4); if (v >= UInt64(100)) then Exit(3); if (v >= UInt64(10)) then Exit(2); Result := 1; end; Edited August 23, 2022 by Leif Uneus Inlining makes it even faster Share this post Link to post
Fr0sT.Brutal 900 Posted August 24, 2022 Ridiculous but two-cases algo while seeming more fast and elegant gives the same results if inlined, even for the input that gives the largest difference (5 - the worst case for Leif's algo and almost the best for mine) function AltCntCaseTbl2( v : Int64) : Int64; inline; begin if (v > MaxLongInt) then begin case v shr 32 of 0..9: Exit(11); 10..99 : Exit(12); 100..999: Exit(13); 1000..9999: Exit(14); 10000..99999: Exit(15); 100000..999999: Exit(16); 1000000..9999999: Exit(17); 10000000..99999999: Exit(18); 100000000..999999999: Exit(19); else Exit(20); end; end else case v of 0..9: Exit(1); 10..99 : Exit(2); 100..999: Exit(3); 1000..9999: Exit(4); 10000..99999: Exit(5); 100000..999999: Exit(6); 1000000..9999999: Exit(7); 10000000..99999999: Exit(8); 100000000..999999999: Exit(9); else Exit(10); end; Result := 0; end; If not inlined it's 3x faster than Leif's version Share this post Link to post
Leif Uneus 43 Posted August 24, 2022 @Fr0sT.Brutal Negative values will give the answer 10. But that is easily fixed. Share this post Link to post
Fr0sT.Brutal 900 Posted August 24, 2022 (edited) @Leif Uneus yep, in fact the check for 1000000000..High(UInt32) should be instead of those "else"s. Anyway I'm really shocked that algo gave no perf gain compared to straight set of comparisons. Looks like CPU smartness made it run fast Edited August 25, 2022 by Fr0sT.Brutal Share this post Link to post
Leif Uneus 43 Posted August 24, 2022 @Fr0sT.Brutal The algo is using floating point values, hence the bad performance. I made a mistake in my previous code. There is a gap between MaxLongInt and the next upper boundry. Your code suffers from that too. Test with AltCntCaseTbl2(Int64(MaxLongInt)+1) Here is my correction: function AltCntCaseTbl(v : Int64) : Int64; inline; begin if (v <= MaxLongInt) then begin if (v >= 1000000000) then Exit(10); if (v >= 100000000) then Exit(9); if (v >= 10000000) then Exit(8); if (v >= 1000000) then Exit(7); if (v >= 100000) then Exit(6); if (v >= 10000) then Exit(5); if (v >= 1000) then Exit(4); if (v >= 100) then Exit(3); if (v >= 10) then Exit(2); if (v >= 0) then Exit(1); Exit(0); end; if (v >= 1000000000000000000) then Exit(19); if (v >= 100000000000000000) then Exit(18); if (v >= 10000000000000000) then Exit(17); if (v >= 1000000000000000) then Exit(16); if (v >= 100000000000000) then Exit(15); if (v >= 10000000000000) then Exit(14); if (v >= 1000000000000) then Exit(13); if (v >= 100000000000) then Exit(12); if (v >= 10000000000) then Exit(11); Exit(10); end; Share this post Link to post
Stefan Glienke 2002 Posted August 25, 2022 (edited) 9 hours ago, Leif Uneus said: The algo is using floating point values, hence the bad performance. What? A case is slower than chained if statements in such a case because it uses a series of add and sub instructions combined with conditional jumps. That usually causes data dependency. Also the function using case is wrong - you must not use shr 32 and then compare that to base10 values. As for the other version: when checking if the number is in integer range, do the less than 0 check once and then do an Integer hardcast for the other checks - that avoids the full 64bit compare that needs two cmp and conditional jump instructions on 32bit. Also FWIW the check against maxlongint is unnecessary. Edited August 25, 2022 by Stefan Glienke Share this post Link to post
Fr0sT.Brutal 900 Posted August 25, 2022 1 hour ago, Stefan Glienke said: Also the function using case is wrong - you must not use shr 32 and then compare that to base10 values. Why not? 1 hour ago, Stefan Glienke said: A case is slower than chained if statements in such a case because it uses a series of add and sub instructions combined with conditional jumps. That usually causes data dependency. Fair point Share this post Link to post
Stefan Glienke 2002 Posted August 25, 2022 5 hours ago, Fr0sT.Brutal said: Why not? Because its wrong - you are handling the result of v shr 32 as if it was v div 10000000000 1 Share this post Link to post