Mic*_*ael 8 delphi overriding rounding
我刚刚发现我必须重新实现的软件广泛使用System.Round().问题是这个函数使用"Bankers rounding",并且不能像Math.RoundTo()(rmDown,rmUp,rmNearest,rmTruncate)那样改变行为.
我必须将行为更改为"正常舍入"(12.5 - > 13 NOT 12.5 - > 12)...所以我想全局覆盖System.Round().我想这样做,因为Round()被使用了很多次,我不想手动更改它们.
这怎么可能?
Dav*_*nan 14
警告:虽然下面的答案解决了所提出的问题,但我建议没有人使用它.如果您想以不同的方式执行舍入Round,请编写并调用专用函数.
您可以使用运行时代码钩子来更改实现Round.
皱纹是抓住Round函数的地址有点棘手,因为它是一个内在的.您还必须小心遵循使用的调用约定.输入值在x87堆栈寄存器中传递,ST(0)返回值为64位整数EDX:EAX.
这是怎么做的.
procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
OldProtect: DWORD;
begin
if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
begin
Move(NewCode, Address^, Size);
FlushInstructionCache(GetCurrentProcess, Address, Size);
VirtualProtect(Address, Size, OldProtect, @OldProtect);
end;
end;
type
PInstruction = ^TInstruction;
TInstruction = packed record
Opcode: Byte;
Offset: Integer;
end;
procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
NewCode: TInstruction;
begin
NewCode.Opcode := $E9;//jump relative
NewCode.Offset :=
NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;
function System_Round: Pointer;
asm
MOV EAX, offset System.@Round
end;
procedure _ROUND;
asm
{ -> FST(0) Extended argument }
{ <- EDX:EAX Result }
// your implementation goes here
end;
initialization
RedirectProcedure(System_Round, @_ROUND);
Run Code Online (Sandbox Code Playgroud)
如果您宁愿在Pascal中实现您的版本而不是asm,那么您需要使非标准调用约定适应_ROUND标准的Delphi调用约定.像这样:
function MyRound(x: Extended): Int64;
begin
// your implementation goes here
end;
procedure _ROUND;
var
x: Extended;
asm
{ -> FST(0) Extended argument }
{ <- EDX:EAX Result }
FSTP TBYTE PTR [x]
CALL MyRound
end;
Run Code Online (Sandbox Code Playgroud)
请注意,我假设您的程序的目标是32位.如果您需要以64位为目标,则原则大致相同,但细节明显不同.
UNIT MathRound;
INTERFACE
FUNCTION ROUND(X : Extended) : Int64;
IMPLEMENTATION
FUNCTION ROUND(X : Extended) : Int64;
BEGIN
Result:=TRUNC(X+0.5)
END;
END.
Run Code Online (Sandbox Code Playgroud)
如果在MathRound.PAS中保存上面的项目目录,然后将此单元包含在源文件中,您将拥有一个数学ROUND函数,而不是默认情况下实现的银行家舍入.
它会在-12.5到-12之间舍入(即,对于.5值始终向零舍入)和-12.1到-11.如果你想要一个更"逻辑"的舍入,你应该使用这一行代替:
IF X<0.0 THEN Result:=-TRUNC(ABS(X)+0.5) ELSE Result:=TRUNC(X+0.5)
Run Code Online (Sandbox Code Playgroud)
作为功能体.
这将导致
ROUND(12.5) = 13
ROUND(12.1) = 12
ROUND(-12.5)= -13
ROUND(-12.1)= -12
Run Code Online (Sandbox Code Playgroud)