【问题标题】:More elegant way to determine whether two floats have same signs in C#?在 C# 中确定两个浮点数是否具有相同符号的更优雅的方法?
【发布时间】:2018-09-24 14:00:42
【问题描述】:

有什么方法可以在 C# 中快速比较两个浮点数的符号?我怀疑一次又一次地将浮点数与 0 进行比较的性能。但是,在C#中似乎禁止对float进行位操作,所以我们不能使用(a^b)&0x80000000或((a >> 31) == (b >> 31))之类的方法来确定(忽略大小写0 这里)。

从浮点数转换为整数(无指针)的问题是四舍五入,因为每个小于 1 的数字都会被四舍五入为 0。

从float转换为int(带指针)的问题只有两个可能的返回值,无法识别0。

现在是时候测试0预判和指针转换组合的性能了。

(删除我在这里犯的错误)

【问题讨论】:

  • Math.Sign(a) == Math.Sign(b)?但是Sign 只是比较< 0> 0== 0
  • 如果您怀疑“一次又一次比较”的性能,请将值存储在布尔值中。当然,这可能是您正在进行微优化的情况。可能是为了速度咆哮:ericlippert.com/2012/12/17/performance-rant
  • @john:在这种情况下,我相信框架程序员和 JiT 的能力。看看对数组边界检查所做的优化:blogs.msdn.microsoft.com/clrcodegeneration/2009/08/13/…
  • @Christopher 我没有怀疑他们,我只是stating a fact
  • (a) 浮点数有 +0 和 -0。比较 -0 到 +3 或 -0 到 +0 时,您想要什么结果? (b) 如果位测试或其他高性能代码在语义上等价,您为什么认为编译器不会优化比较?

标签: c# floating-point compare signed


【解决方案1】:

如果您使用unsafe,您可以使用指针将位转换为int,然后您可以进行按位运算

*(int*)(&val1) >> 31 == *(int*)&val2 >> 31;

如果您担心性能问题,可以使用MethodImplOptions.AggressiveInlining

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool IsSameSign(float val1, float val2)
{
   return *(int*)(&val1) >> 31 == *(int*)&val2 >> 31;
}

我针对您的原始版本做了一些基准测试,供您娱乐。我将每个测试运行 50 次,比较 100,000 次,分别进行 1,000,000 次

Test Framework : .NET Framework 4.7.1

Scale : 100000
Name          |       Time |     Delta |  Deviation |      Cycles
-----------------------------------------------------------------
MySameSign    |   1.045 ms |  0.052 ms |       0.20 |   3,498,252
BaseSameSign  |  15.391 ms |  0.548 ms |       0.81 |  52,181,763


Scale : 1000000
Name          |        Time |     Delta |  Deviation |       Cycles
-------------------------------------------------------------------
MySameSign    |   10.000 ms |  0.232 ms |       0.31 |   33,995,263
BaseSameSign  |  146.250 ms |  1.311 ms |       1.13 |  498,064,434 

示例数据

static float NextFloat()
{
   double mantissa = (_rand.NextDouble() * 2.0) - 1.0;
   double exponent = Math.Pow(2.0, _rand.Next(-126, 128));
   return (float)(mantissa * exponent);
}
public static List<Tuple<float,float>> GenerateSignInput(int scale)
{
   return Enumerable.Range(1, scale)
                     .Select(x => new Tuple<float, float>(NextFloat(), NextFloat()))
                     .ToList();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多