Python Delphi-映射函数意外输出

Python Delphi-映射函数意外输出,python,delphi,floating-point,mapping,range,Python,Delphi,Floating Point,Mapping,Range,在Arduino库中找到此genius函数。我在德尔福写了同样的东西: 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

在Arduino库中找到此genius函数。我在德尔福写了同样的东西:

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;
以下是我的结果中的一个片段:

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
该值大于高(整数),因此溢出为负值

您应该在函数中使用浮点参数来避免这个陷阱

至于其他值,它们与执行算术的精度相同。Delphi代码以64位扩展精度执行算术。Python代码为53位双精度

在我看来,最好避免64位扩展精度。它是非标准的,仅限于某些平台。它在32位x86上可用,但64位x64编译器将SSE单元用于浮点,并且该单元不支持64位扩展精度。最重要的是,数据类型的对齐会导致非常差的读/写内存性能

所以,若你们想要算术的可移植性,我建议你们坚持53位双精度。停止使用扩展的类型,改用双精度。并将浮点单元配置为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;

使用整型参数会导致溢出,这解释了负值的原因。此子表达式:

(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
该值大于高(整数),因此溢出为负值

您应该在函数中使用浮点参数来避免这个陷阱

至于其他值,它们与执行算术的精度相同。Delphi代码以64位扩展精度执行算术。Python代码为53位双精度

在我看来,最好避免64位扩展精度。它是非标准的,仅限于某些平台。它在32位x86上可用,但64位x64编译器将SSE单元用于浮点,并且该单元不支持64位扩展精度。最重要的是,数据类型的对齐会导致非常差的读/写内存性能

所以,若你们想要算术的可移植性,我建议你们坚持53位双精度。停止使用扩展的类型,改用双精度。并将浮点单元配置为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;
procedure TForm1.按钮1点击(发送方:TObject);
函数映射(x,in_min,in_max,out_min,out_max:整数):双精度;
开始
结果:=(x-输入最小值)*(输出最大值-输出最小值)/(输入最大值-输入最小值)+输出最小值;
终止
变量
y、 in_min、in_max、out_min、out_max:整数;
i:整数;
m:双倍;
开始
在_min中:=0;
in_max:=6406963;
out_min:=0;
out_max:=474;
y:=0;
对于i:=in_min到in_max do
开始
m:=映射(i,in_min,in_max,out_min,out_max);
如果圆(m)y那么
开始
y:=圆形(m);
备忘录1.Lines.Add(IntToStr(i)+'='+FloatToStr(m));
终止
终止
终止

编辑:有关解释,请参见david的答案

使用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;
procedure TForm1.按钮1点击(发送方:TObject);
函数映射(x,in_min,in_max,out_min,out_max:整数):双精度;
开始
结果:=(x-输入最小值)*(输出最大值-输出最小值)/(输入最大值-输入最小值)+输出最小值;
终止
变量
y、 in_min、in_max、out_min、out_max:整数;
i:整数;
m:双倍;
开始
在_min中:=0;
in_max:=6406963;
out_min:=0;
out_max:=474;
y:=0;
对于i:=in_min到in_max do
开始
m:=映射(i,in_min,in_max,out_min,out_max);
如果圆(m)y那么
开始
y:=圆形(m);
备忘录1.Lines.Add(IntToStr(i)+'='+FloatToStr(m));
终止
终止
终止

编辑:有关解释,请参见david的答案

整数溢出,应该使用Double

改变

function map(x, in_min, in_max, out_min, out_max: Integer): extended;


您的整数溢出,应该使用Double

改变

function map(x, in_min, in_max, out_min, out_max: Integer): extended;


这只是把问题推得更远,但它仍然存在。应该使用浮点参数。是的,你的答案更好。这只是把问题推得更远,但它仍然存在。应该使用浮点参数。是的,你的答案更好。这仍然返回负值,因为整数溢出。在哪个输入值?
(x-in\u min)*(out\u max-out\u min)
可以溢出带符号的32位整数,并在他的测试中溢出。我可以对你的答案发表评论吗。你说,看看大卫的答案,来解释。我觉得这不太令人满意。如果你还没有准备好解释一个答案,那么最好不要麻烦。如果你想简明扼要的话,也许发表评论会更好。在我看来,如果你准备回答,你应该准备好解释。如果已经存在另一个答案,这并不重要。如果您认为可以更好地解释它,那么重叠也没关系。@DavidHeffernan您没有什么事情要做。这仍然返回负值,因为整数溢出。在