【问题标题】:Converting floating point ">=" to ">" and "<=" to "<"将浮点“>=”转换为“>”,将“<=”转换为“<”
【发布时间】:2015-03-13 18:12:36
【问题描述】:

我正在 Delphi 中寻找一种方法来获得最小的单双浮点值,我可以添加或减去或添加到我的数字中,以使浮点比较的数字不同。或者,如果我能得到比我的数字更小和更大的下一个浮点数。从浮点的角度来看,我想将其转换为:

if (A >= B) or (C <= D) then

if (A > newnumber1) or (C < newnumber2) then

它们在浮点中产生相同的结果。 newnumber1newnumber2 对于单打和双打显然是不同的。我要么需要一些可以从 A 中减去并添加到 C 值中的值来获得 newnumber1 和 newnumber2,要么我需要一种从 B 和 D 获取这些数字的方法。

在 C++11 中有一个方法 std::nextafter 在这个问题中被引用,看起来就足够了。

Finding the closest floating point value less than a specific integer value in C++?

上下文

我正在做向量运算,我需要做一个大于或等于的等价物。完成此操作的最简单方法是取一个稍小的数字并将其与大于操作一起使用。如果可能的话,我宁愿不要用拇指吮吸一个似乎有效的值。

我使用的向量运算是 ippsThreshold_LTValGTVal_32s 来自:

https://software.intel.com/en-us/node/502143

库显然不支持 >= 操作。这在浮点意义上是不切实际的。要创建等效函数,我需要增加和减少比较值来解决这个问题,然后使用大于运算和小于运算。

举例

如果我有一个包含 5 个值的数组 [99.4, 20, 19.9, 99, 80],ippsThreshold_LTValGTVal_32s 向量操作将让我用我自己的替换值替换向量中的特定值。在此示例中,我想将所有值 >= 99 和

函数签名如下所示:

ippsThreshold_LTValGTVal_32s(..., ..., ..., levelLT, valueLT, levelGT, valueGT);

我的电话会是这样的:

ippsThreshold_LTValGTVal_32s(..., ..., ..., 20.00000001, 0, 98.99999, 0);

这将包括小于运算的 20 和大于运算的 99,并给我一个看起来像 [0, 0, 0, 0, 80] 的向量。

我需要找出 20.0000001 和 98.999999 的用途。我希望这些值与原始值之间的差异尽可能小,同时仍然足够显着以包含 > 和

【问题讨论】:

  • 我认为您的分析不正确。为什么要允许下一个最接近的值,但不允许之后的值?导致您得出这个结论的数字推理是什么?
  • @DavidHeffernan 相反。如果我有一个值 5。我需要包含这个值以及所有大于 5 的值。向量运算不支持 ">= 5" 所以我需要使用 "> X" 其中 X 是最大值在大于运算中包含 5。这意味着 X 需要略小于 5。这是我正在使用的操作 (software.intel.com/en-us/node/502143)
  • 我认为这些信息对这个问题很有用。然后你可以问你真正想知道的是如何在这个库之上实现大于或等于。也就是说,我无法理解您的操作是什么。据我所知 > 和 >= 在这种情况下是相同的。
  • @DavidHeffernan 谢谢,我已经用更多细节更新了这个问题。
  • 上升。我很抱歉。我理解了一些别的东西,我认为这是为了找到机器 epsilon 的价值。我的帖子没有意义。我会抹去的。

标签: delphi simd delphi-xe4


【解决方案1】:

根据设计,对于 IEEE754 数据类型,您可以简单地将值视为整数并递增该值。或者如果值为负,则减少它。

function NextDoubleGreater(const D: Double): Double;
var
  SpecialType: TFloatSpecial;
  I: Int64;
begin
  SpecialType := D.SpecialType;
  case SpecialType of
  fsZero,fsNZero:
    // special handling needed around 0 and -0
    I := 1;
  fsInf, fsNInf, fsNaN:
    I := PInt64(@D)^; // return the original value
  fsDenormal, fsNDenormal, fsPositive, fsNegative:
    begin
      I := PInt64(@D)^;
      if I >= 0 then begin
        inc(I);
      end else begin
        dec(I);
      end;
    end;
  end;
  Result := PDouble(@I)^;
end;

同样在相反的方向:

function NextDoubleLess(const D: Double): Double;
var
  SpecialType: TFloatSpecial;
  I: Int64;
begin
  SpecialType := D.SpecialType;
  case SpecialType of
  fsZero,fsNZero:
    // special handling needed around 0 and -0
    I := $8000000000000001;
  fsInf, fsNInf, fsNaN:
    I := PInt64(@D)^; // return the original value
  fsDenormal, fsNDenormal, fsPositive, fsNegative:
    begin
      I := PInt64(@D)^;
      if I >= 0 then begin
        dec(I);
      end else begin
        inc(I);
      end;
    end;
  end;
  Result := PDouble(@I)^;
end;

这种格式并非巧合。由于这种设计,浮点比较运算符的实现很简单。

参考:How to alter a float by its smallest increment (or close to it)?

【讨论】:

  • TDoubleRec.BuildUpTDoubleHelper.BuildUp: var LValA, LValB: Double; LValB.BuildUp( LValA.Sign, LValA.Mantissa+1,LValA.Exponent );
  • @Sir 然后你必须处理尾数溢出。很简单,但效率较低。
  • 太完美了。测试并正常工作。 NaN 和 INF 在我的情况下不是问题,但即使它们是问题,在这种情况下检查 NaN/INF 并返回原始值也是有意义的,因为它们没有上一个和下一个值。
  • 是的,我不想涵盖所有边缘情况,因为这相对容易,如果平凡的话。 -0 情况是棘手的情况。 FWIW,链接问题中没有人处理这个问题。我自己发现了那个。谢谢你的好问题。今晚我学到了一些东西。
  • 只是一个提示:var I: Int64 absolute Result; 然后Result := D; 允许简化代码(Int64 和 Double 的大小都是 8 字节)。
猜你喜欢
  • 2011-02-28
  • 2020-08-25
  • 2020-05-14
  • 2012-05-25
  • 2014-02-20
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多