将整数转换为二进制-如何转换';s在Android(vs Windows)上完成-XE5

将整数转换为二进制-如何转换';s在Android(vs Windows)上完成-XE5,android,delphi,binary,delphi-xe5,Android,Delphi,Binary,Delphi Xe5,所以,昨天我打开了一个对我来说有点不清楚的,关于它是如何工作的(转换操作)。 现在,我有另一个问题要问disguss:它是如何在android上实现的,为什么它的结果不同于在windows上实现的,以及如何获得实际的二进制结果,而不是我现在得到的结果(关于这个问题,我还不清楚它是什么;不知何故,它的十六进制值是错误的) 我在firemonkey移动项目中有这段代码,该项目的目标是android和windows平台。 通过这种方式,代码测试可以通过针对windows来完成,这样它可以更快地编译,并

所以,昨天我打开了一个对我来说有点不清楚的,关于它是如何工作的(转换操作)。 现在,我有另一个问题要问disguss:它是如何在android上实现的,为什么它的结果不同于在windows上实现的,以及如何获得实际的二进制结果,而不是我现在得到的结果(关于这个问题,我还不清楚它是什么;不知何故,它的十六进制值是错误的)

我在firemonkey移动项目中有这段代码,该项目的目标是android和windows平台。 通过这种方式,代码测试可以通过针对windows来完成,这样它可以更快地编译,并作为虚拟移动表单进行预览;好吧,至少目的是这样做,但看起来它不是那么可靠的错误来源

虽然在Win32上运行的同一个项目运行得很好,但在android上运行它不会从函数中得到二进制结果

我使用此代码进行转换:

function IntToBin(Value: Integer): string;
var
  i: Integer;
begin
  SetLength(Result, 9);
  for i := 1 to 9 do begin
    if (Value shr (9-i)) and 1 = 0 then begin
      Result[i] := '0'
    end else begin
      Result[i] := '1';
    end;
  end;
end;
当尝试转换值“1”时,调试器显示以下内容:

Result: 0x6d09d738
   *Result: #0'00000000'                      [expanded from Result]
Value: 1
现在,我知道我之所以(在调用这个转换函数的主过程完成之后)得到它应该是的值的一半(1而不是3,2而不是4,8是16的整数,6而不是12…),是因为在*结果中,很明显,当我需要9位结果时,它只有8位(我正在做从整数(1..512)到二进制的转换…,因此,除了把2读作0010,我得到001,也就是1,而不是6(0110),我得到3(011),8(1000)我得到4(100)等等…)

现在我真的想知道,为什么一开始,它甚至将结果作为0x6d09d738/0x6d09d738抛出,而不仅仅是二进制,为什么它不是9bit,因为我设置了结果字符串的长度

还有一件有趣的事,对我来说是无法理解的:

这是我触发转换的过程:

procedure TForm1.CalculateAddress1;
var SetDip: string;
    C: integer;
begin
  SetDip := IntToBin(StrToInt(MergeAddr));
  //Text1.Text:=SetDip;
  for C := 1 to 9 do
    begin
      if Copy(SetDip, b, 1) = '1' then
      DipSW[10-b].IsChecked:=True
      else DipSW[10-b].IsChecked:=False;
    end;
end;
//(我使用它将dmx地址转换为dip开关配置…)

SetDip得到类似于#0'00000000'的值(它不同;当转换的整数为1时是这种情况;对于11,我得到#0'00000101')

这导致了应有的精确的一半值(正如我上面提到的;我理解为什么会发生这种情况,因为缺少一点结果…)

但是,如果我将//从
Text1.Text:=SetDip;
(我将其放在那里以便在手机屏幕上看到结果)中删除,我将得到下一个错误,仅在手机上!(同样,目标windows运行正常):

'字符串索引超出范围(-1)。必须大于等于0,如果不是那么长,我不知道它如何复制索引9处的字符串;我知道这正是它抛出错误的原因,但再次说明:为什么不总是这样

[[Ps:+,发现Firemonkey会自动处理固定数组,如果代码试图创建超出定义大小的条目。有趣的…:)这可能意味着它也会自己处理字符串长度和复制索引。]

在Android(和其他移动平台)上,字符串中的第一个字符会被索引为值0,而在桌面版本中,它的索引值为1

所以,你需要改变

Result[i] := '0'

在为移动平台编译时。要自动执行此操作,您可以使用以下命令:

{$IFDEF CPUARM }
  Result[i-1] := '0'
{$ELSE }
  Result[i] := '0'
{$ENDIF }
但请注意,只有索引(即使用[]语法)是不同的。Copy、Pos等函数仍然将1视为字符串中的第一个字符

谈论不一致性(以及程序员的头痛)

我使用以下助手函数:

FUNCTION GetChar(CONST S : STRING ; OneBasedIndex : Cardinal) : CHAR;
  BEGIN
    IF LOW(STRING)=0 THEN Result:=S[PRED(OneBasedIndex)] ELSE Result:=S[OneBasedIndex]
  END;

PROCEDURE SetChar(VAR S : STRING ; OneBasedIndex : Cardinal ; C : CHAR);
  BEGIN
    IF LOW(STRING)=0 THEN S[PRED(OneBasedIndex)]:=C ELSE S[OneBasedIndex]:=C
  END;

获取/设置字符,而不考虑编译目标。它还具有“未来证明”的优势,因为它不使用编译器的{$DEFINE},而是检查字符串类型的低索引。

您的问题是因为在移动平台上,字符串索引是基于零的,而在桌面平台上默认是基于一的。有很多方法可以解决这个问题,当然不需要平台切换ifdef

最简单的解决方案是使桌面编译器的行为方式与移动编译器相同

当您将其添加到代码中时,您需要习惯字符串的零基索引。您应该停止使用诸如
Pos
之类的传统函数,而是使用
TStringHelper
方法

您的函数将按如下方式重新编写:

{$ZEROBASEDSTRINGS ON}
function IntToBin(Value: Integer): string;
var
  i: Integer;
begin
  SetLength(Result, 9);
  for i := 0 to 8 do begin
    if (Value shr (8-i)) and 1 = 0 then begin
      Result[i] := '0'
    end else begin
      Result[i] := '1';
    end;
  end;
end;
{$ZEROBASEDSTRINGS ON}
function IntToBin(Value: Integer): string;
var
  i: Integer;
begin
  SetLength(Result, 9);
  for i := 0 to 8 do
    Result[i] := Chr(ord('0') + (Value shr (8-i)) and 1));
end;
在我看来,如果这样编写,代码会更干净一些,并避免分支:

{$ZEROBASEDSTRINGS ON}
function IntToBin(Value: Integer): string;
var
  i: Integer;
begin
  SetLength(Result, 9);
  for i := 0 to 8 do begin
    if (Value shr (8-i)) and 1 = 0 then begin
      Result[i] := '0'
    end else begin
      Result[i] := '1';
    end;
  end;
end;
{$ZEROBASEDSTRINGS ON}
function IntToBin(Value: Integer): string;
var
  i: Integer;
begin
  SetLength(Result, 9);
  for i := 0 to 8 do
    Result[i] := Chr(ord('0') + (Value shr (8-i)) and 1));
end;
对字符串的另一个有争议的变化(尚未发生)是使它们不可变。该更改将使字符串索引运算符为只读。这种变化可能永远不会发生。如果是这样的话,我可能会重新编写代码,如下所示:

function IntToBin(Value: Integer): string;
var
  i: Integer;
  buff: array [0..8] of Char;
begin
  for i := 0 to 8 do
    buff[i] := Chr(ord('0') + (Value shr (8-i)) and 1));
  SetString(Result, PChar(@Buffer), Length(buff));
end;
您当然应该阅读有关此主题的文档:


您必须做的另一件事,至少对于调试构建,是启用。如果启用了该功能,编译器将编写出能够立即显示错误的代码。最好通过项目配置全局控制此功能

这里有一个解决方案,它不取决于字符串以哪个索引开头(即独立于平台):


哦。。。这就是问题所在--所以我必须使用ifdef操作系统定义,使它在两种平台上都可用,对吗?现在,我用
SetLength(结果,9)解决了这个问题;对于i:=1到9,执行开始if(值shr(8-i))和1=0,然后开始结果[i]:=“0”结束else开始结果[i]:=“1”if Value>255,然后是DipSw[9]。IsChecked:=True else DipSw[9]。IsChecked:=False
其中Value是发送给函数的整数;通过这种方式,我实际上执行了与Windows 8位相同的操作,并手动处理最后一位。这并不完美,但至少是可行的。我不知道索引的事。我会尽快检查