Delphi - 映射函数意外输出
Delphi - Mapping function unexpected output
在 Arduino 库中找到这个天才 map 函数。我写了同样的东西 Delphi:
procedure TForm1.Button1Click(Sender: TObject);
function map(x, in_min, in_max, out_min, out_max: Integer): extended;
begin
result := (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
end;
var
y, in_min, in_max, out_min, out_max: Integer;
i: integer;
m: extended;
begin
in_min := 0;
in_max := 6406963;
out_min := 0;
out_max := 474;
y := 0;
for i := in_min to in_max do begin
m := map(i, in_min, in_max, out_min, out_max);
if round(m) <> y then begin
y := round(m);
Memo1.Lines.Add(IntToStr(i) + ' = ' + FloatToStr(m));
end;
end;
end;
从中得到了一些有趣的结果,所以我在 Python 中写了同样的东西来检查和验证:
def map(x, in_min, in_max, out_min, out_max):
"Re-maps a number from one range to another."
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
if __name__ == '__main__':
in_min = 0
in_max = 6406963
out_min = 0
out_max = 474
y = 0
for i in range(in_min, in_max):
m = map(i, in_min, in_max, out_min, out_max)
if round(m) != y:
y = round(m)
print(i, ' = ', m)
这是我的结果的一个片段:
DELPI EXPECTED (Python)
6759 = 0,500044404813 6759 = 0.50004440481395
1358439 = 100,500047526418 1358439 = 100.50004752641775
2710119 = 200,500050648022 2710119 = 200.50005064802153
4061799 = 300,500053769625 4061799 = 300.5000537696253
4521370 = 334,500040034569 4521370 = 334.50004003456866
4530557 = -335,179597260043 4534887 = 335.50005486218663
5418335 = -269,499996488196 5413479 = 400.5000568912291
6405062 = -196,499949820219 6400205 = 473.50002957719596
那么,为什么我的 Delphi 代码会产生负数作为输出,应该如何纠正这个问题?
您使用整数参数会导致溢出,这解释了负值。这个子表达式:
(x - in_min) * (out_max - out_min)
仅包含整数操作数,因此使用整数运算执行。这可能会溢出。产生负输出的第一个值是 x = 4530557
。让我们通过计算更深入地挖掘:
x = 4530557
x - in_min = 4530557
out_max - out_min = 474
(x - in_min) * (out_max - out_min) = 2147484018
并且该值大于 high(Integer)
,因此溢出为负值。
你应该在函数中使用浮点参数来避免这个陷阱。
至于其他值,这些值与执行算术的精度相同。您的 Delphi 代码以 64 位扩展精度执行算术运算。 53 位双精度的 Python 代码。
在我看来,最好避免使用 64 位扩展精度。它是非标准的并且仅限于某些平台。它在 32 位 x86 上可用,但 64 位 x64 编译器将 SSE 单元用于浮点,并且该单元不支持 64 位扩展精度。最重要的是,数据类型的对齐导致 read/write 内存性能非常差。
因此,如果您想要算术的可移植性,我建议您坚持使用 53 位双精度。停止使用 Extended
类型并改用 Double
。并将浮点单元配置为以 53 位精度运行。
所以,最终结果是这个函数:
function map(const x, in_min, in_max, out_min, out_max: Double): Double;
begin
result := (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
end;
使用 Double 而不是 Extended 你会得到相同的结果
procedure TForm1.Button1Click(Sender: TObject);
function map(x, in_min, in_max, out_min, out_max: Integer): Double;
begin
result := (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
end;
var
y, in_min, in_max, out_min, out_max: Integer;
i: Integer;
m: Double;
begin
in_min := 0;
in_max := 6406963;
out_min := 0;
out_max := 474;
y := 0;
for i := in_min to in_max do
begin
m := map(i, in_min, in_max, out_min, out_max);
if round(m) <> y then
begin
y := round(m);
Memo1.Lines.Add(IntToStr(i) + ' = ' + FloatToStr(m));
end;
end;
end;
编辑: 请参阅大卫的回答以获取解释;
你的整数溢出了,你应该使用双精度数。
改变
function map(x, in_min, in_max, out_min, out_max: Integer): extended;
到
function map(x, in_min, in_max, out_min, out_max: Int64): double;
在 Arduino 库中找到这个天才 map 函数。我写了同样的东西 Delphi:
procedure TForm1.Button1Click(Sender: TObject);
function map(x, in_min, in_max, out_min, out_max: Integer): extended;
begin
result := (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
end;
var
y, in_min, in_max, out_min, out_max: Integer;
i: integer;
m: extended;
begin
in_min := 0;
in_max := 6406963;
out_min := 0;
out_max := 474;
y := 0;
for i := in_min to in_max do begin
m := map(i, in_min, in_max, out_min, out_max);
if round(m) <> y then begin
y := round(m);
Memo1.Lines.Add(IntToStr(i) + ' = ' + FloatToStr(m));
end;
end;
end;
从中得到了一些有趣的结果,所以我在 Python 中写了同样的东西来检查和验证:
def map(x, in_min, in_max, out_min, out_max):
"Re-maps a number from one range to another."
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
if __name__ == '__main__':
in_min = 0
in_max = 6406963
out_min = 0
out_max = 474
y = 0
for i in range(in_min, in_max):
m = map(i, in_min, in_max, out_min, out_max)
if round(m) != y:
y = round(m)
print(i, ' = ', m)
这是我的结果的一个片段:
DELPI EXPECTED (Python)
6759 = 0,500044404813 6759 = 0.50004440481395
1358439 = 100,500047526418 1358439 = 100.50004752641775
2710119 = 200,500050648022 2710119 = 200.50005064802153
4061799 = 300,500053769625 4061799 = 300.5000537696253
4521370 = 334,500040034569 4521370 = 334.50004003456866
4530557 = -335,179597260043 4534887 = 335.50005486218663
5418335 = -269,499996488196 5413479 = 400.5000568912291
6405062 = -196,499949820219 6400205 = 473.50002957719596
那么,为什么我的 Delphi 代码会产生负数作为输出,应该如何纠正这个问题?
您使用整数参数会导致溢出,这解释了负值。这个子表达式:
(x - in_min) * (out_max - out_min)
仅包含整数操作数,因此使用整数运算执行。这可能会溢出。产生负输出的第一个值是 x = 4530557
。让我们通过计算更深入地挖掘:
x = 4530557
x - in_min = 4530557
out_max - out_min = 474
(x - in_min) * (out_max - out_min) = 2147484018
并且该值大于 high(Integer)
,因此溢出为负值。
你应该在函数中使用浮点参数来避免这个陷阱。
至于其他值,这些值与执行算术的精度相同。您的 Delphi 代码以 64 位扩展精度执行算术运算。 53 位双精度的 Python 代码。
在我看来,最好避免使用 64 位扩展精度。它是非标准的并且仅限于某些平台。它在 32 位 x86 上可用,但 64 位 x64 编译器将 SSE 单元用于浮点,并且该单元不支持 64 位扩展精度。最重要的是,数据类型的对齐导致 read/write 内存性能非常差。
因此,如果您想要算术的可移植性,我建议您坚持使用 53 位双精度。停止使用 Extended
类型并改用 Double
。并将浮点单元配置为以 53 位精度运行。
所以,最终结果是这个函数:
function map(const x, in_min, in_max, out_min, out_max: Double): Double;
begin
result := (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
end;
使用 Double 而不是 Extended 你会得到相同的结果
procedure TForm1.Button1Click(Sender: TObject);
function map(x, in_min, in_max, out_min, out_max: Integer): Double;
begin
result := (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
end;
var
y, in_min, in_max, out_min, out_max: Integer;
i: Integer;
m: Double;
begin
in_min := 0;
in_max := 6406963;
out_min := 0;
out_max := 474;
y := 0;
for i := in_min to in_max do
begin
m := map(i, in_min, in_max, out_min, out_max);
if round(m) <> y then
begin
y := round(m);
Memo1.Lines.Add(IntToStr(i) + ' = ' + FloatToStr(m));
end;
end;
end;
编辑: 请参阅大卫的回答以获取解释;
你的整数溢出了,你应该使用双精度数。
改变
function map(x, in_min, in_max, out_min, out_max: Integer): extended;
到
function map(x, in_min, in_max, out_min, out_max: Int64): double;