Jump to content
Sign in to follow this  
Al T

I Broke Delphi ->> Low bound exceeds high bound & Duplicate case label if you swap'em (Case Z of) Table error

Recommended Posts

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 by Al T

Share this post


Link to post

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 by David Heffernan

Share this post


Link to post
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().

LowBoundExceedsHighBoundErrorPicture.JPG

Share this post


Link to post

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 by Brian Evans

Share this post


Link to post
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

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 by Brian Evans

Share this post


Link to post

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
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
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.

  • Haha 1

Share this post


Link to post
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 by Fr0sT.Brutal

Share this post


Link to post

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 by DelphiUdIT
  • Like 1
  • Thanks 1

Share this post


Link to post
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

  • Like 1

Share this post


Link to post
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.

  • Like 1

Share this post


Link to post
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;

 

  • Like 2

Share this post


Link to post

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 by Leif Uneus
Inlining makes it even faster

Share this post


Link to post

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

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 by Fr0sT.Brutal

Share this post


Link to post

@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
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 by Stefan Glienke

Share this post


Link to post
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
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

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×