【问题标题】:Any fast way to check if two doubles has the same sign?有什么快速的方法来检查两个双打是否具有相同的符号?
【发布时间】:2016-06-04 04:46:35
【问题描述】:

有什么快速的方法来检查两个双打是否有相同的符号?假设两个双精度数不能为 0。

【问题讨论】:

  • 用什么语言?您对便携性有多关心?
  • 我更喜欢c#语言
  • 好的。我认为这排除了对符号位的直接访问。在 C 中,如果您弄脏了手并依赖于 fp 实现的细节,您可以进行非常便宜的按位比较。
  • 双打中的任何一个都可以是-0吗?还是 NaN?

标签: c# algorithm performance


【解决方案1】:

可能的解决方案:

  1. a*b > 0:一个浮点乘法和一个比较。
  2. (a>0) == (b>0):三个比较。
  3. Math.Sign(a) == Math.Sign(b):两个函数调用和一个比较。

速度对比:

这是你所期望的(见底部的实验设置):

  1. a*b > 0: 0.42 ± 0.02s
  2. (a>0) == (b>0): 0.49 ± 0.01s
  3. Math.Sign(a) == Math.Sign(b): 1.11 ± 0.9s

重要提示:

正如 cmets 中的 greybeard 所指出的,如果值乘以小于 Double.Epsilon 的值,则方法 1 很容易出现问题。除非你能保证倍数总是大于这个,否则你应该选择方法 2。


实验设置:

以下代码在http://rextester.com/ 上运行了 16 次。

public static void Main(string[] args)
{
    double a = 1e-273;
    double b = a;
    bool equiv = false;
    for(int i=0; i<100000000; ++i) {
        equiv = THE_COMPARISON;
        b += a;
    }
    //Your code goes here
    Console.WriteLine(equiv);
}

【讨论】:

  • 两个正数溢出回到负数会怎样?
  • @cricket_007 双打?它们溢出到inf
  • 风险 NaN/异常。
  • @greybeard 是吗? -infinf 比较正确,你不能在非零乘法上得到 NaN...
  • +/- 2^-123 怎么样?
【解决方案2】:

我所知道的IEEE 754 最简单和最快的方法是在两个数字的MSB 位上使用XOR。这是一个小的 C# 示例(注意内联以避免函数开销):

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static bool fpu_cmpsign(double a, double b)
{
    byte* aa;
    byte* bb;
    aa = (byte*)(&a); // points to the a as 8bit integral type
    bb = (byte*)(&b); // points to the b as 8bit integral type
    return ((aa[7] ^ bb[7]) & 128) != 128;
}

这里是+/- 数字组合的结果:

a b result
- - 1
- + 0
+ - 0
+ + 1

这个想法很简单。符号存储在最高位 (MSB) 中,XOR 为不相等的位返回 1,因此 XOR 将摊位号的 MSB 放在一起并否定输出。 [7] 只是以 8 位整数类型访问 double 的最高 BYTE,因此我可以使用 CPU ALU 而不是 FPU。如果您的平台颠倒了BYTES 的顺序,请改用[0]MSByte first vs. LSByte first)。

因此,您只需要 3x 8 位 XORs 用于比较和否定,以及 1x 8 位 AND 仅用于提取符号位结果。

您可以使用联合而不是指针,也可以为您的平台使用本机位宽以获得最佳性能。

【讨论】:

  • 我使用内联来避免函数开销并进行比较;在发布版本上,它需要大约 1.5 倍的时间是 (a&gt;0) == (b&gt;0)。它还需要使用/unsafe 构建标志,这会降低可移植性。
  • @Kittsil 它取决于编译器、设置、平台和许多其他东西......正如我提到的,我不会在 Python 中编码,所以我不熟悉那里的优化......有很多可能性尝试像重写指针访问......只使用谓词,......使用==......使用8/16/32/64位变量等等都归结为编译器如何转换为汇编......跨度>
  • @Kittsil 顺便说一句,您添加了 !=128 而不是谓词的直接结果,即 cmp ALU 运算,而不是简单的位 XOR,这也会影响性能
  • 我添加了!=(而不是您原来的^),因为C# 不允许从int 转换为bool。不幸的是,与C++ 不同,C# 不允许进行尽可能多的优化;它旨在成为一种更高级别的语言(它是由 Microsoft 编写的,所以... ;-)。
  • @Kittsil 然后可能更快的是(aa[7] &amp;128) == (bb[7] &amp;128),它基本上是(NOT XOR),但也编码为 cmp
【解决方案3】:

你可以使用:

if (copysign(x, y) == x)

【讨论】:

  • @kittsil:在 C99 中。
  • 你能让这个工作吗?我不能,但如果你可以包含工作 C# 代码,那么它绝对是最好的解决方案。
猜你喜欢
  • 2010-11-24
  • 2016-03-05
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
  • 2016-10-04
相关资源
最近更新 更多