我有一个问题(或更可能是一个错误报告)关于位移位行为在Delphi(测试在Borland德尔福7)。

目标:对任意数字执行“算术”向右移位。

这意味着符号位必须被扩展 – 如果数字的最高有效位被设置,二进制数将从1开始填充,而不是0。

因此,在算术移位之后的数字“-1”必须保持相同的数(所有位= 1),但是“逻辑移位”(其总是用零填充数)必须给出最大正整数(最大正有符号整数,要正确)

我测试它只在32位系统(Windows);此外,我需要它明确地工作与32位整数。

看起来在Delphi中有一个内部错误,当源数字存储在一个变量中时为“shr”。

我的示例代码:

program bug;

{$APPTYPE CONSOLE}

var
I:Integer;
C:Cardinal;

begin
I := -1;  // we’ll need that later
C := $FFFFFFFF;

(这只是开始)。接下来,让我们尝试一些“shr”:

Writeln('0) ',-1 shr 1 );
Writeln('1) ',$FFFFFFFF shr 1 );

“-1”是等价于“$ FFFFFFFF”的签名。似乎“shr”行为(算术或逻辑)是基于源数字是有符号还是不是(整数或基数)的事实。

输出为:

0) -1
1) 2147483647

非常正确。然后我需要尝试手动将这些数字转换为整数或红雀:

Writeln('2) ',Integer(-1) shr 1 );
Writeln('3) ',Integer($FFFFFFFF) shr 1 );
Writeln('4) ',Cardinal(-1) shr 1 );
Writeln('5) ',Cardinal($FFFFFFFF) shr 1 );

结果:

2) -1
3) -1
4) 2147483647
5) 2147483647

仍然正确。所以,我认为如果我需要算术移位,我可以把任何东西转换为“整数”;或者当我想要逻辑移位时转换为“基数”。可是等等!带变量的示例(如上所述):

Writeln('6) ',I shr 1 );
Writeln('7) ',C shr 1 );

突然:

6) 2147483647
7) 2147483647

不正确。我的“I”是一个有符号整数,我期待算术移位!所以,也许铸造可以帮助?

Writeln('8) ',Integer(I) shr 1 );
Writeln('9) ',Cardinal(I) shr 1 );
Writeln('A) ',Integer(C) shr 1 );
Writeln('B) ',Cardinal(C) shr 1 );

不,还是一样的…

8) 2147483647
9) 2147483647
A) 2147483647
B) 2147483647

如果我尝试创建一个函数“a shr b”并使用它,情况会更糟:

// Simple shift right with signed integers
function shrI(a,b:Integer):Integer;
begin
Result := a shr b;
end;

// Simple shift right with unsigned integers
function shrC(a,b:Cardinal):Cardinal;
begin
Result := a shr b;
end;

现在:

Writeln('C) ',shrI(-1,1) );
Writeln('D) ',shrC($FFFFFFFF,1) );

– 它停止工作,即使使用常量表达式:(这是有道理的,因为数字再次存储在函数内的变量)

C) 2147483647
D) 2147483647

因为我需要进行正确的算术移位,我写了这些公式来做这个(将“a”右移“b”位)。首先是逻辑转移:

(a shr b) and ((1 shl (32-b))-1)

我只需要按位,结果与“32 – b”(从右)清除“b”左位,如果“shr”失败,我做了算术移位(没有例子说明这一点,但只是为了当然)。然后算术移位:

(a shr b) or (( 0-((a shr 31) and 1)) shl (32-b))

我需要按位或结果从左边的“b”,但只有当最高有效位被设置;首先我使用“(a shr 31)和1”符号位,然后否定这个数字以获得“-1”(或$ FFFFFFFF – 所有位= 1),如果源为负,否则为0否则“0-x”而不是“-x”,因为在我的C端口在某些情况下bcc32 C编译器报告绑定以取消无符号整数的警告);最后我把它移到“32 – b”左边,所以我得到了我想要的,即使“shr”失败,并给出零。我做了两个版本的每个函数来处理整数和红衣主教(也可以共享名称和“超载”他们为我,但在这里我不会这样做,以保持示例清晰):

// Logical shift right with signed integers
function srlI(a,b:Integer):Integer;
begin
Result := (a shr b) and ((1 shl (32-b))-1);
end;

// Arithmetic shift right with signed integers
function sraI(a,b:Integer):Integer;
begin
Result := (a shr b) or (( 0-((a shr 31) and 1)) shl (32-b));
end;

// Logical shift right with unsigned integers
function srlC(a,b:Cardinal):Cardinal;
begin
Result := (a shr b) and ((1 shl (32-b))-1);
end;

// Arithmetic shift right with unsigned integers
function sraC(a,b:Cardinal):Cardinal;
begin
Result := (a shr b) or (( 0-((a shr 31) and 1)) shl (32-b));
end;

测试:

Writeln('E) ',sraI(-1,1) );
Writeln('F) ',srlI(-1,1) );
Writeln('G) ',sraC($FFFFFFFF,1) );
Writeln('H) ',srlC($FFFFFFFF,1) );

并得到完美的结果:

E) -1
F) 2147483647
G) 4294967295
H) 2147483647

(G-case仍然是正确的,因为“4294967295”是“-1”的无符号版本)

使用变量的最终检查:

Writeln('K) ',sraI(I,1) );
Writeln('L) ',srlI(I,1) );
Writeln('M) ',sraC(C,1) );
Writeln('N) ',srlC(C,1) );

完善:

K) -1
L) 2147483647
M) 4294967295
N) 2147483647

对于这个bug,我也试图改变第二个数字(移位量)到一个变量和/或尝试不同的铸造它 – 相同的bug存在,看起来像它不与第二个参数相关。并试图把结果(整数或基数)之前输出没有改善任何东西。

为了确保我不只是一个有错误的人,我试图运行我的整个例子在http://codeforces.com/(有一个注册用户可以编译和执行一段代码在不同的语言和服务器端的编译器)看到输出。

“Delphi 7”编译器给了我什么我有 – bug存在。替代选项,“Free Pascal 2”显示更多错误的输出:

0) 9223372036854775807
1) 2147483647
2) 9223372036854775807
3) 9223372036854775807
4) 2147483647
5) 2147483647
6) 2147483647
7) 2147483647
8) 2147483647
9) 2147483647
A) 2147483647
B) 2147483647
C) 2147483647
D) 2147483647
E) -1
F) 2147483647
G) 4294967295
H) 2147483647
K) -1
L) 2147483647
M) 4294967295
N) 2147483647

在情况0-2-3(有“-1”,“Integer(-1)”和“Integer($ FFFFFFFF)”不记得)中奇怪“9223372036854775807”。

这里是我在Delphi的整个例子:

program bug;

{$APPTYPE CONSOLE}

// Simple shift right with signed integers
function shrI(a,b:Cardinal):Cardinal;
begin
Result := a shr b;
end;

// Logical shift right with signed integers
function srlI(a,b:Cardinal):Cardinal;
begin
Result := (a shr b) or (( 0-((a shr 31) and 1)) shl (32-b));
end;

var
I:Integer;
C:Cardinal;

begin
I := -1;
C := $FFFFFFFF;

Writeln('0) ',$FFFFFFFF shr 1 );
// 0) -1           - correct
// 1) 2147483647   - correct

Writeln('2) ',Integer($FFFFFFFF) shr 1 );
// 2) -1           - correct
// 3) -1           - correct

Writeln('4) ',Cardinal($FFFFFFFF) shr 1 );
// 4) 2147483647   - correct
// 5) 2147483647   - correct

Writeln('6) ',C shr 1 );
// 6) 2147483647   - INCORRECT!
// 7) 2147483647   - correct

Writeln('8) ',Cardinal(I) shr 1 );
// 8) 2147483647   - INCORRECT!
// 9) 2147483647   - correct

Writeln('A) ',Cardinal(C) shr 1 );
// A) 2147483647   - INCORRECT!
// B) 2147483647   - correct

Writeln('C) ',1) );
// C) 2147483647   - INCORRECT!
// D) 2147483647   - correct

Writeln('E) ',1) );
// E) -1           - correct
// F) 2147483647   - correct

Writeln('G) ',1) );
// G) 4294967295   - correct
// H) 2147483647   - correct

Writeln('K) ',1) );
// K) -1           - correct
// L) 2147483647   - correct

Writeln('M) ',1) );
// M) 4294967295   - correct
// N) 2147483647   - correct

end.

然后我是curios,这个bug也存在于C吗?我写了一个端口到C和使用(Borland!)bcc32.exe来编译它。

结果:

0) -1
1) 2147483647
2) -1
3) -1
4) 2147483647
5) 2147483647
6) -1
7) 2147483647
8) -1
9) 2147483647
A) -1
B) 2147483647
C) -1
D) 2147483647
E) -1
F) 2147483647
G) 4294967295
H) 2147483647
K) -1
L) 2147483647
M) 4294967295
N) 2147483647

一切都好。这里是C版本,以防万一还想看看:

#include <iostream>
using namespace std;

// Simple shift right with signed integers
int shrI(int a,int b){
return a >> b;
}

// Simple shift right with unsigned integers
unsigned int shrC(unsigned int a,unsigned int b){
return a >> b;
}

// Logical shift right with signed integers
int srlI(int a,int b){
return (a >> b) & ((1 << (32-b))-1);
}

// Arithmetic shift right with signed integers
int sraI(int a,int b){
return (a >> b) | (( 0-((a >> 31) & 1)) << (32-b));
}

// Logical shift right with unsigned integers
unsigned int srlC(unsigned int a,unsigned int b){
return (a >> b) & ((1 << (32-b))-1);
}

// Arithmetic shift right with unsigned integers
unsigned int sraC(unsigned int a,unsigned int b){
return (a >> b) | (( 0-((a >> 31) & 1)) << (32-b));
}

int I;
unsigned int C;

int main(){
I = -1;
C = 0xFFFFFFFF;

cout<<"0) "<<( -1 >> 1 )<<endl;
cout<<"1) "<<( 0xFFFFFFFF >> 1 )<<endl;
// 0) -1           - correct
// 1) 2147483647   - correct

cout<<"2) "<<( ((int)(-1)) >> 1 )<<endl;
cout<<"3) "<<( ((int)(0xFFFFFFFF)) >> 1 )<<endl;
// 2) -1           - correct
// 3) -1           - correct

cout<<"4) "<<( ((unsigned int)(-1)) >> 1 )<<endl;
cout<<"5) "<<( ((unsigned int)(0xFFFFFFFF)) >> 1 )<<endl;
// 4) 2147483647   - correct
// 5) 2147483647   - correct

cout<<"6) "<<( I >> 1 )<<endl;
cout<<"7) "<<( C >> 1 )<<endl;
// 6) -1           - correct
// 7) 2147483647   - correct

cout<<"8) "<<( ((int)(I)) >> 1 )<<endl;
cout<<"9) "<<( ((unsigned int)(I)) >> 1 )<<endl;
// 8) -1           - correct
// 9) 2147483647   - correct

cout<<"A) "<<( ((int)(C)) >> 1 )<<endl;
cout<<"B) "<<( ((unsigned int)(C)) >> 1 )<<endl;
// A) -1           - correct
// B) 2147483647   - correct

cout<<"C) "<<( shrI(-1,1) )<<endl;
cout<<"D) "<<( shrC(0xFFFFFFFF,1) )<<endl;
// C) -1           - correct
// D) 2147483647   - correct

cout<<"E) "<<( sraI(-1,1) )<<endl;
cout<<"F) "<<( srlI(-1,1) )<<endl;
// E) -1           - correct
// F) 2147483647   - correct

cout<<"G) "<<( sraC(0xFFFFFFFF,1) )<<endl;
cout<<"H) "<<( srlC(0xFFFFFFFF,1) )<<endl;
// G) 4294967295   - correct
// H) 2147483647   - correct

cout<<"K) "<<( sraI(I,1) )<<endl;
cout<<"L) "<<( srlI(I,1) )<<endl;
// K) -1           - correct
// L) 2147483647   - correct

cout<<"M) "<<( sraC(C,1) )<<endl;
cout<<"N) "<<( srlC(C,1) )<<endl;
// M) 4294967295   - correct
// N) 2147483647   - correct

}

在发布之前,我试图搜索这个问题,并没有发现任何提到这个bug。我也看了这里:What is the behaviour of shl and shr for non register sized operands?和这里:Arithmetic Shift Right rather than Logical Shift Right – 但有其他问题讨论(编译器内部转换任何类型到32位数字之前做实际的移位;或移位超过31位),但不是我的错误。

但等等,这里是我的问题:http://galfar.vevb.net/wp/2009/shift-right-delphi-vs-c/!

有一句话:他们说 –

In Delphi the SHR is always a SHR operation: it never takes into account the sign.

但是我的例子表明,Delphi确实考虑到符号,但只有当源号是一个常量表达式,而不是一个变量。因此,“-10 shr 2”等于“-3”,但是当“x:= – 10”时,“x shr 2”等于“1073741821”。

所以我认为这是一个bug,而不是一个“行为”,“shr”总是逻辑。你看,不总是。
尝试启用/禁用任何编译器选项,如范围检查或优化没有更改任何内容。

此外,在这里我发布了如何绕过这个问题的例子,并有正确的算术移位权。我的主要问题是:我是对吗?

看起来在Delphi中左移总是好的(它从来不使用原始符号位,而不是“未定义”:对于有符号整数,它在转换之前表现为转换为基数,并将结果转换回整数 – 数字可能突然变为负数课程)。但现在我不知道,在Delphi中有没有其他类似的错误?这是我在Delphi 7中发现的第一个真正重要的错误。我喜欢Delphi超过C,因为我总是确保我的代码是每次做我想要的,没有调试测试每一个新的不寻常的代码,我即将写入(IMHO)。

P.S。这里有一些有用的链接,StackOverflow系统建议我,当我在打印这个问题之前输入我的标题。再次,有趣的信息,但不是关于这个bug:

Arithmetic bit-shift on a signed integer
Signed right shift = strange result?
Bitwise shift operators on signed types
Should you always use ‘int’ for numbers in C,even if they are non-negative?
Bitwise operation on signed integer
Verifying that C/C++++ signed right shift is arithmetic for a particular compiler?
Emulating variable bit-shift using only constant shifts?

P.P.S.非常感谢Stack Exchange Team在发布这篇文章方面的帮助。伙计们,你摇滚!

解决方法

有一个bug,但它不是你的想法。这里是shr的 documentation:

If x is a negative integer,the shl and shr operations are made clear
in the following example:

06000

因此,shr和shl总是逻辑移位而不是算术移位。

缺陷实际上是处理负的真实常数:

Writeln('0) ',-1 shr 1 );

这里,-1是有符号值。它实际上有Shortint类型,一个有符号的8位整数。但移位运算符对32位值进行运算,因此符号扩展为32位值。这意味着这段摘录应该产生具有相同输出的两行:

var
  i: Integer;
....
i := -1;
Writeln(-1 shr 1);
Writeln( i shr 1);

并且输出应该是:

2147483647
2147483647

在现代版本的Delphi,肯定从版本2010和更高版本,但可能甚至更早的版本,这是case。

但是根据你的问题,在Delphi 7中,-1 shr 1评估为-1,这是错误的,因为shr是逻辑移位。

我们可以猜测缺陷的来源。编译器评估-1 shr 1,因为它是一个常量值,编译器简单地使用算术移位而不是逻辑移位不正确。

顺便说一句,文档包含另一个错误。它说:

The operations x shl y and x shr y shift the value of x to the left or right by y bits,which (if x is an unsigned integer) is equivalent to multiplying or dividing x by 2^y; the result is of the same type as x.

最后一部分不是真的。如果x是8,16或32位类型,则表达式x shl y是32位类型,否则是64位类型。

因为你的实际目标是实现算术移位,所以这些都不重要。你不能使用shl或shr。你必须自己实现算术移位。我建议你这样使用内联汇编程序,因为我怀疑,最终可能更容易阅读和验证。

算术按位右移“a shr b”,带有存储在变量中的有符号整数 – 错误的结果!内部Delphi的错误?的更多相关文章

  1. ios – 我在哪里可以找到用于创建IPad应用程序的Delphi资源?

    我之前一直在使用Delphi并且一直都是Windows家伙.我的妻子为我的生日买了一台新的iPad,我昨晚第一次使用它.哇!…

  2. 如何从命令行部署OSX或IOS Delphi项目?

    我正在使用像这样的脚本构建我的Delphi应用程序现在我想添加一个选项将应用程序部署到OSX系统修改这样的脚本,那么可以从命令行部署OSX或IOSDelphi项目吗?

  3. 如何检查Android和iOS上的网络是否可用(Delphi XE5)

    解决方法试试这个:

  4. 使用嵌套线程错误终止应用程序

    如果我使用一个启动了嵌套线程的线程,那么终止应用程序时会遇到问题。如果最深的嵌套线程卡住,则应用程序终止时会发生错误。在其他情况下,问题不会出现。据我所知,tSync.Free不会等待TCP.Connect完成并自行关闭。TCP.Connection在超时结束后无处返回。

  5. 通过gsdll32.dll与Delphi 11组合PDF

    我已经使用这个代码10多年了,它突然停止工作。该程序读取一个pdf文件列表,并输出一个包含列表中所有文件的pdf文件。它使用gsapi.pas包装单元。多年来,我只需要从Ghostscript.t下载一个更新版本的gsdll32.dll文件,就可以兼容更新版本的pdf文件格式。我发现他们有了一个新的翻译,也许这与此有关?请注意,在Ghostscript中使用等效的命令行本身效果良好。在我看来,他们改变了调用API的方式。如果有任何帮助,我将不胜感激。我确实尝试了添加开关“-dNEWPDF=false”,但

  6. Windows 10中的内存泄漏Delphi Seattle中的TNotification?

    我正在我的应用程序中实现Windows10通知.但是,下面的代码显然会给出1个TNotification对象和2个字符串的备忘录泄漏,但我在块的末尾释放了对象:我做了一些愚蠢的事情,或者在通知的实施中是否存在内存泄漏?

  7. 如何处理Windows 7的125%或150%放大率(Delphi)

    Windows7已添加其控制面板,外观和个性化,显示设置,放大文本和其他项目125%或150%的功能.完成后,我的Delphi2009程序的表单和对话框的一些内容被切断.我需要做些哪些更改才能确保在Windows7的任何放大设置下,我的所有表单和对话框都能正常显示.这听起来像是指文本大小设置,它在Windows中以各种形式存在多年,Delphi形成了与此设置的交互以及它们的自动缩放.我没有在这里检

  8. 是默认情况下能够处理vista / win7 UAC的delphi 2010程序

    或者你必须自己包括它?如果是,给你什么级别的特权?我的程序修改了一个注册密钥,它可以在没有对.res文件进行任何修改的情况下这样做吗?一个附带问题:是否有一个组件/专家可以像delphi2010一样自动生成/包含.res文件在我的delphi7和delphi2007项目中?

  9. windows – 我需要避免尝试更新连接到TSQLQuery的Delphi TClientDataset中的非物理字段

    Precis:我的代码试图更新DelphiXETClientDataset中的非物理字段(连接到带有sql属性集的TsqlQuery),这些字段是作为运行时Open命令创建的.我有一个连接到连接到TsqlConnection的TsqlQuery的TDatasetProvider的TClientDataset.这些对象中的前3个被封装在库中的几个类中,我在几个项目的许多地方使用它们.这些类在运行时创

  10. windows – Delphi中的跨应用程序拖放

    我想为WindowsXP创建一个Delphi应用程序,它允许删除从Windows资源管理器或其他支持此操作的应用程序拖动的文件.反之亦然?当用户从我的应用程序中拖动图标时,我应该能够将拖动对象的内容设置为自定义数据.我的目标是创建一个自定义应用程序工具栏,我可以在其上删除应用程序并显示其图标或从中拖动应用程序或其他实体.如何才能做到这一点?

随机推荐

  1. delphi – 主窗口按进程名称处理

    DelphiXe,Win7x64如何从进程名称(exe文件的完整路径)获取主窗口句柄,或至少一个类或窗口名称(如果该进程只有一个窗口).例:解决方法我同意Petesh的说法,你需要枚举顶级窗口并检查创建它的进程的模块文件名.为了帮助您开始枚举顶级窗口,这是一个delphi实现.首先,当你回调给你时,你需要一些与EnumWindows方法通信的方式.为此声明一条记录,该记录将保存您要查找的模块的文件

  2. 如何在Delphi中纯粹通过RTTI信息(即不使用任何实际对象实例)获取TObjectList的子项类型?

    我正在使用RTTI实现用于流式传输任意Delphi对象的通用代码,并且为了使其工作(更具体地说,为了使加载部分工作),我需要以某种方式获得TObjectList的子项类型<T>不使用任何实际对象实例的字段.要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用–我宁愿只能访问相关类的纯RTTI数据.我希望能

  3. inno-setup – Inno Setup – 安装程序背景图片

    图像作为安装程序背景如何用inno5.5.9做到这一点?

  4. inno-setup – Inno Setup – 如何添加多个arc文件进行解压缩?

    使用InnoSetup解压缩弧文件.我希望有可能解压缩多个arc文件以从组件选择中安装文件(例如).但仍然显示所有提取的整体进度条.这可能吗?的回答的修改预备是相同的,参考其他答案.在ExtractArc中,为要提取的每个存档调用AddArchive.

  5. delphi – 如何在DataSet的帮助下在TAdvStringGrid中显示数据库中的BLOB图像

    解决方法CreateBlobStream正在创建一个TStream对象,而不是TMemoryStream.由于您不想将JPG写入数据库,因此应使用bmRead而不是bmReadWrite.我不习惯sqlite,但你必须确保使用合适的二进制日期类型.为了确保存储的图像真的是JPG,您应该编写JPG以进行测试,例如:

  6. inno-setup – 在Inno Setup的Code部分下载程序后运行程序

    如何运行我通过Internet下载的应用程序,在代码部分中使用,并等待该应用程序完成运行.我有,使用InnoTools下载程序,下载这两个文件,我想,在第二个完成下载后运行该下载,或jdk-8u111-windows-x64.exe,然后继续安装.解决方法使用其他下载插件,而不是ITD(请参阅下面的原因).例如,InnoDownloadPlugin.当您包含idp.iss时,它定义了一个全局IDP

  7. progress-bar – Inno Setup Run部分的简单进度页面

    我的安装程序非常简单,它基本上是:>欢迎页面>进展页面>最终页面欢迎页面和最终页面是标准页面.在Progress页面,我正在静默安装一堆其他程序.实际的脚本是在[Run]部分中安装每个程序.问题是酒吧达到100%然后停留在那里.我只能更改消息文本.我想要实现的是使用Pascal脚本显示进度,例如:这样我就可以显示更准确的进度条.这就是我所拥有的:问题是,当我构建安装程序时,它不显示欢迎页面.我做错了什么?

  8. delphi – 如何使“显示/隐藏桌面图标”设置生效?

    下面的代码调用SHGetSetSettings函数来隐藏桌面图标但它只是从视图菜单中取消选中“显示桌面图标”.我打电话给SHChangeNotify;更新桌面,但这不起作用?解决方法isa,要刷新桌面,您可以将F5键发送到progman窗口隐藏桌面图标的另一种方法是再次显示

  9. inno-setup – Inno Setup – 避免显示子安装程序的文件名

    我试图使用InnoSetup–Howtohidecertainfilenameswhileinstalling?(FilenameLabel)的想法Theonlysuresolutionistoavoidinstallingthefiles,youdonotwanttoshow,usingthe[Files]section.Installthemusingacodeinstead.UsetheEx

  10. inno-setup – Inno Setup磁力链接下载实施

    我目前正在使用InnoDownloadPlugin为我的安装程序下载文件,这个问题最大的问题是faila正确下载文件.因为连接不良等诸多原因.我想添加一种替代方法来下载文件,因此用户可以选择是否需要常规方式或torrent方式.我知道我可以使用aria2c.exe应用程序(https://aria2.github.io/),有人可以帮我实现它的inno设置代码吗?我需要的是使用torrent(ar

返回
顶部