如何在 Delphi 中创建 TBitSet32 记录以实现高效的 32 位操作?

How can I create a TBitSet32 record in Delphi for efficient 32-bit operations?

提问人:zeus 提问时间:8/25/2023 更新时间:8/25/2023 访问量:198

问:

我正在做一个项目,我需要非常有效地操作一组 32 位。内存效率至关重要,因此使用布尔数组(将占用 32 个字节)不是一种选择(如果我有其他选择,速度是最重要的)。

这是我为 TBitSet32 设想的结构:

TBitSet32 = record 
    value: ?integer?; // I'm not sure about the datatype here. Should it be Integer or another type?

    // Gets the value associated with a particular bit index.
    function valueForBit(index: integer): boolean;

    // Returns the number of marked bits in the set.
    function count: integer;

    // Marks the specified bit.
    procedure markBit(value: boolean; index: integer);   
end;

对于值字段,我假设我需要一个 32 位整数类型。Integer 是正确的选择吗?如何实现 valueForBit、count 和 markBit 方法?

我将不胜感激此记录的任何见解或示例实现。

德尔福 ·帕斯卡 FreePascal

评论

1赞 Delphi Coder 8/25/2023
你检查过 docwiki.embarcadero.com/Libraries/Alexandria/en/ 了吗?对你的情况来说,这是不是效率低下?
0赞 Uwe Raabe 8/25/2023
TIntegerSet 呢?
1赞 USauter 8/25/2023
System.Classes.TBits 类没有您需要的什么?
1赞 SilverWarior 8/26/2023
@DelphiCoder 是的,您链接的问题提供了两个可行的解决方案。第一个被标记的答案建议循环槽元素,并在最终结果上使用按位运算,这就提出了一个问题,即为什么不在之前使用按位运算并完全跳过使用。Dicky Tamara 的第二个答案建议使用类助手,这是一个更优雅的解决方案,但它假设您始终将值分配给无符号的 32 位整数。我猜这是因为那个问题的 OP 想要什么。...TBitsTBitsTBits
1赞 SilverWarior 8/26/2023
...无论如何,如果我最终使用它,可能会从中派生出我自己的类,这将允许我在现有变量上使用所述类。这可以通过将所述变量作为 var 参数传递给类构造函数来完成。这样,在构造函数中,我可以将字段设置为指向变量位置,并将字段设置为与传递的变量的大小相匹配。所以再多几行,我会有更多可用的解决方案。但在此之前,我必须测试一下是否正确地考虑了 Little-endian或 Big-endian。TBitsFBitsFSizeTBits

答:

7赞 Remy Lebeau 8/25/2023 #1

如果您正好需要 32 位,那么比 更有意义。其余的只是使用 、 和 / 运算符进行位操作的问题,例如:(U)Int32Integerandorshlshr

type
  TBitSet32 = record 
    FSet: UInt32;

    // Gets the value associated with a particular bit index.
    function valueForBit(index: integer): boolean;

    // Returns the number of marked bits in the set.
    function count: integer;

    // Marks the specified bit.
    procedure markBit(value: boolean; index: integer);   
  end;

function TBitSet32.valueForBit(index: integer): boolean;
begin
  Result := (FSet and (1 shl index)) <> 0;
end;

function TBitSet32.count: integer;
var
  I: Integer;
  tmp: UInt32;
begin
  Result := 0;
  tmp := FSet;
  for I := 0 to 31 do begin
    Inc(Result, tmp and 1);
    tmp := tmp shr 1;
  end;
end;

procedure markBit(value: boolean; index: integer);
begin
  if value then
    FSet := FSet or (1 shl index)
  else
    FSet := FSet and not (1 shl index);
end;

也就是说,请考虑改用 a,让编译器为您处理位操作,因为 是使用位集实现的,例如:SetSet

type
  TBitSet32 = record 
    FSet: set of 0..31;

    // Gets the value associated with a particular bit index.
    function valueForBit(index: integer): boolean;

    // Returns the number of marked bits in the set.
    function count: integer;

    // Marks the specified bit.
    procedure markBit(value: boolean; index: integer);   
  end;

function TBitSet32.valueForBit(index: integer): boolean;
begin
  Result := index in FSet;
end;

function TBitSet32.count: integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to 31 do
    Inc(Result, Ord(I in FSet));
end;

procedure markBit(value: boolean; index: integer);
begin
  if value then
    Include(FSet, index)
  else
    Exclude(FSet, index);
end;

评论

1赞 zeus 8/25/2023
谢谢,FSet:0..31 集;接缝最有前途的解决方案!
2赞 Old Skull 8/25/2023
@zeus,您还可以将 public 设置为私有并实现 public 。然后使用valueForBitmarkBitproperty Bits[index: integer]: Boolean read valueForBit write markBitmyBitSet.Bits[2] := true