Delphi - 映射函数意外输出

时间:2015-10-12 12:32:16

标签: python delphi floating-point mapping range

在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代码会产生负数作为输出,应该采取什么措施来纠正这个?

3 个答案:

答案 0 :(得分:5)

使用整数参数会导致溢出,这可以解释负值。这个子表达式:

(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位扩展精度执行算术运算。 Python代码为53位双精度。

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

因此,如果你想要算术的可移植性,我建议你坚持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;

答案 1 :(得分:0)

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

更改

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;

答案 2 :(得分:-1)

使用 Double 代替扩展,您将获得相同的结果

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;

编辑:请参阅大卫的答案以获得解释;