【问题标题】:Computing the high bits of a multiplication in C#在 C# 中计算乘法的高位
【发布时间】:2015-04-18 19:56:02
【问题描述】:

我正在尝试将开源库从 .Net 4.0 转换为 3.5,但无法轻松转换以下长乘法代码:

    /// <summary>
    /// Calculate the most significant 64 bits of the 128-bit
        product x * y, where x and y are 64-bit integers.
    /// </summary>
    /// <returns>Returns the most significant 64 bits of the product x * y.</returns>
    public static long mul64hi(long x, long y)
    {
 #if !NET35
        BigInteger product = BigInteger.Multiply(x, y);
        product = product >> 64;
        long l = (long)product;
        return l;
 #else
        throw new NotSupportedException(); //TODO!
 #endif
    }

如您所见,作者没有找到方法来做到这一点。 BigInteger 在 .NET 3.5 中不存在。

如何在 .NET 3.5 上计算 64*64 乘法的高位 64 位?

【问题讨论】:

  • 感谢您提供的链接,使用 J# 库我可能会使其工作!我现在正在尝试...
  • hm 不适合我,MDSN 说它仅适用于 VS 5 或更低版本,我需要使用 VS2010 解决其他问题(默认参数)
  • 由于您要求的是一个众所周知的问题(高位乘法),因此存在现有解决方案:stackoverflow.com/questions/28868367/… 我没有找到 C# 解决方案,但 C 代码应该可以按原样工作。
  • @usr 谢谢,我会看看那个,目前正在遵循一个解决方案,该解决方案使用 Mono 源代码中的 System.Numeric 命名空间Answer 它看起来很有希望:)跨度>

标签: c# .net biginteger multiplication


【解决方案1】:

您可以从多个 N 位乘法器构建一个 2N 位乘法器。

public static ulong mul64hi(ulong x, ulong y)
{
    ulong accum = ((ulong)(uint)x) * ((ulong)(uint)y);
    accum >>= 32;
    ulong term1 = (x >> 32) * ((ulong)(uint)y);
    ulong term2 = (y >> 32) * ((ulong)(uint)x);
    accum += (uint)term1;
    accum += (uint)term2;
    accum >>= 32;
    accum += (term1 >> 32) + (term2 >> 32);
    accum += (x >> 32) * (y >> 32);
    return accum;
}

这只是小学的长乘法,真的。

对于带符号的数字,这有点困难,因为如果中间结果带入符号位,一切都会出错。 long 无法保存 32 位乘 32 位乘法的结果而不发生这种情况,因此我们必须以更小的块进行:

public static long mul64hi(long x, long y)
{
    const long thirtybitmask = 0x3FFFFFFF;
    const long fourbitmask = 0x0F;
    long accum = (x & thirtybitmask) * (y & thirtybitmask);
    accum >>= 30;
    accum += ((x >> 30) & thirtybitmask) * (y & thirtybitmask);
    accum += ((y >> 30) & thirtybitmask) * (x & thirtybitmask);
    accum >>= 30;
    accum += ((x >> 30) & thirtybitmask) * ((y >> 30) & thirtybitmask);
    accum += (x >> 60) * (y & fourbitmask);
    accum += (y >> 60) * (x & fourbitmask);
    accum >>= 4;
    accum += (x >> 60) * (y >> 4);
    accum += (y >> 60) * (x >> 4);
    return accum;
}

受 harold 关于 Hacker's Delight 评论的启发,通过仔细控制中间结果是否签名,可以使签名版本与其他版本一样高效:

public static long mul64hi(long x, long y)
{
    ulong u = ((ulong)(uint)x) * ((ulong)(uint)y);
    long s = u >> 32;
    s += (x >> 32) * ((long)(uint)y);
    s += (y >> 32) * ((long)(uint)x);
    s >>= 32;
    s += (x >> 32) * (y >> 32);
    return s;
}

【讨论】:

  • 哦,老实说,我没想到这会起作用;)而且我无法真正控制解决方案,它有点 4 万亿(最大值是 9 万亿?)非常感谢!!
  • @Seneral:确保运行一堆单元测试。因为你有带符号的数字,一些中间结果可以携带到符号位中,我不确定这是否会破坏最终结果。
  • @Seneral:我想我有一个版本也可以处理负数。已添加。
  • 是否可以通过使用 Hacker's Delight 中的“High-Order Product Signed from/to Unsigned”来改进签名版本,还是会更好?
  • @harold:那部分没有帮助,但是上面关于使用有符号和无符号的混合作为中间结果的注释会。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-25
相关资源
最近更新 更多