【问题标题】:Floored integer division下限整数除法
【发布时间】:2015-01-21 04:31:41
【问题描述】:

在 C# 中是否有一种简单、高效且正确(即不涉及到/从 double 的转换)的方法来进行 整数除法(例如 Python offers)。

换句话说,以下的有效版本,不会遭受长/双转换损失。

(long)(Math.Floor((double) a / b))

或者是否必须自己实现它,例如

static long FlooredIntDiv(long a, long b)
{
    if (a < 0)
    {
        if (b > 0)
            return (a - b + 1) / b;
        // if (a == long.MinValue && b == -1) // see *) below
        //    throw new OverflowException();
    }
    else if (a > 0)
    {
        if (b < 0)
            return (a - b - 1) / b;
    }
    return a / b;
}

*) 尽管除法运算符 leaves it open 的 C# 4 规范是否在 OverflowException 内部引发了 unchecked,但实际上它确实抛出(在我的系统上)并且 Visual Studio .NET 2003 version 甚至强制它抛出:

如果左操作数是最小的可表示的 int 或 long 值,而右操作数是 –1,则在这种情况下总是抛出 [..] System.OverflowException,无论操作发生在已检查或未检查的上下文中.

编辑

关于checkedunchecked 的划掉的语句都很好,但是checked 实际上只是一个compile time concept,所以我的函数应该环绕还是抛出不管调用函数的代码是否在checked 中,这都取决于我。

【问题讨论】:

  • 您的意思是,将结果传递给Math.Floor?
  • 整数除法已经在这样做了,不是字面上调用Math.Floor,但结果是一样的,它会切断整个小数部分。 Math.Floor 在这种情况下是多余的。
  • @maremp:仅用于阳性结果。有关与 C# / 运算符实现不同的“地板”否定结果示例,请参阅 OP 的表。
  • 哦,是的@PeterDuniho,我忘了...
  • 不是解决方案,但我相信您的代码在某些极端情况下可能会失败。 var a = long.MinValue; var b = -1L;var a = long.MaxValue; var b = -1L;.

标签: c# .net math integer-arithmetic


【解决方案1】:

你可以试试这个:

if (((a < 0) ^ (b < 0)) && (a % b != 0))
{
   return (a/b - 1);
}
else
{
   return (a/b);
}

编辑(在下面的 cmets 中进行了一些讨论后):

不使用 if-else,我会这样:

return (a/b - Convert.ToInt32(((a < 0) ^ (b < 0)) && (a % b != 0)));

注意:Convert.ToIn32(bool value)也需要跳转,见implemention的方法:

return value? Boolean.True: Boolean.False;

理论上,不可能计算a = long.MinValueb = -1L 的除法,因为预期的结果是a/b = abs(long.MinValue) = long.MaxValue + 1 &gt; long.MaxValue。 (long 的范围是–9,223,372,036,854,775,8089,223,372,036,854,775,807。)

【讨论】:

  • PS:^是异或运算符
  • 感谢 L16H7。在实现我上面的FlooredIntDiv 时,我开始与您的解决方案类似,它和我一样有效,而且我相信,和我的一样正确(我还没有考虑过您的解决方案是否涵盖了所有0 极端情况),但是我选择了if/else 路线,因为我认为它更容易阅读。所以它相当于我的示例实现,但既不简单也不高效。
  • 通常,“高效”公式不应使用任何“if”语句等。它应该纯粹基于低级算术运算符。
  • 如果我必须删除if-else,我会这样:{ return (a/b - Convert.ToInt32(((a &lt; 0) ^ (b &lt; 0)) &amp;&amp; (a % b != 0))); }。当 bool 转换为 int 时,我使用的是 true = 1 和 false = 0。
  • @L16H7 我只是想说(正如我的问题现在澄清的那样) - 你和我的实现都不是“不正确的” - 很抱歉扰乱了applecart。但是,我仍然对任何简单有效的解决方案感兴趣,如果有的话。
【解决方案2】:

它在任何理智的编程语言(遵循我们正常操作顺序的语言)中的工作方式是-1.0/3.0 等价于-(1.0/3.0),即-0.3333...。因此,如果您希望将其转换为 int,那么您需要考虑的实际上是 cast/floor 运算符,而不是除法。因此,如果您想要这种行为,您必须使用(int)Math.Floor(a/b),或自定义代码。

【讨论】:

  • 谢谢,我删除了那部分答案。我在计算物理学中学到的一些概念已因滥用而损坏,并以没有意义的方式重新组合。 :) 有关于应该执行哪些操作部分的规则/指南,目标是由于浮点不精确而丢失最少的信息,但我从大学开始就没有真正使用过这些,所以忘记了细节。
【解决方案3】:

你不需要自己实现这个,Math 类提供了一个用于进行欧几里德除法的内置方法:Math.DivRem()

唯一的缺点是你必须为余数提供一个可赋值的变量:

long remainder;
long quotient = Math.DivRem(a, b, out remainder);

【讨论】:

  • Math.DivRem的返回值和a / b一样,所以不需要调用Math.DivRem。至于a / b 有什么问题:如果 a 或 b 为负数,它不会给出下限结果。
猜你喜欢
  • 1970-01-01
  • 2019-05-22
  • 2016-03-16
  • 2018-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多