在C++中,有很多方法可以编写编译的代码,但会产生未定义的行为(维基百科).C#中有类似的东西吗?我们可以用编译的C#编写代码,但是有未定义的行为吗?
该表达式int.Minvalue / -1
根据C#规范导致实现定义的行为:
7.8.2分部运营商
如果左操作数是最小的可表示的int或long值而右操作数是-1,则发生溢出.在已检查的上下文中,这会导致抛出System.ArithmeticException(或其子类).在未经检查的上下文中,它是实现定义的,是否抛出System.ArithmeticException(或其子类)或溢出未报告,结果值是左操作数的值.
测试程序:
var x = int.MinValue;
var y = -1;
Console.WriteLine(unchecked(x / y));
Run Code Online (Sandbox Code Playgroud)
这会引发OverflowException
.NET 4.5 32位,但它没有.
为什么规范会保留结果实现定义?这是反对这样做的情况:
idiv
在这种情况下,x86 指令总是会导致异常.同样有趣的是,如果x / y
是一个编译时常量我们确实得到unchecked(int.MinValue / -1) == int.MinValue
:
Console.WriteLine(unchecked(int.MinValue / -1)); //-2147483648
Run Code Online (Sandbox Code Playgroud)
这意味着,x / y
可以有根据使用的语法形式在不同的行为(而不是只依赖于的值x
和y
).这是规范允许的,但它似乎是一个不明智的选择.为什么C#这样设计?
一个类似的问题指出,在说明书这一确切行为被规定,但它并没有(足够的)回答为什么语言是这样设计的.不讨论替代选择.
我认为这看起来像C#编译器中的一个错误.
考虑这段代码(在方法内):
const long dividend = long.MinValue;
const long divisor = -1L;
Console.WriteLine(dividend % divisor);
Run Code Online (Sandbox Code Playgroud)
它编译时没有错误(或警告).好像是一个bug.运行时,0
在控制台上打印.
然后没有const
,代码:
long dividend = long.MinValue;
long divisor = -1L;
Console.WriteLine(dividend % divisor);
Run Code Online (Sandbox Code Playgroud)
当它运行时,它正确地导致OverflowException
被抛出.
C#语言规范专门提到了这个案例,并说System.OverflowException
将抛出一个.它不依赖于上下文checked
或unchecked
似乎(也是与余数运算符的编译时常量操作数的错误与checked
和相同unchecked
).
int
(System.Int32
),而不仅仅是long
(System.Int64
)发生同样的错误.
相比之下,编译器处理dividend / divisor
与const
操作数比要好得多dividend % divisor
.
我的问题:
我是对的,这是一个错误吗?如果是,它是一个众所周知的错误,他们不希望修复(因为向后兼容性,即使使用% -1
编译时常量相当愚蠢-1
)?或者我们应该报告它,以便他们可以在即将推出的C#编译器版本中修复它?
在 C# 中是否有一种简单、高效和正确(即不涉及到/从 double 转换)的方法来进行地板整数除法(例如Python 提供)。
换句话说,以下的有效版本,不会遭受长/双转换损失。
(long)(Math.Floor((double) a / b))
Run Code Online (Sandbox Code Playgroud)
还是必须自己实现它,例如
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 / …
Run Code Online (Sandbox Code Playgroud) 如果我将 Int32.MinValue 和 -1 传递到Divide()
方法中,System.OverflowException
尽管块发生在unchecked
块中,我还是得到 a 。
private static int Divide(int n, int d)
{
return unchecked (n / d);
}
Run Code Online (Sandbox Code Playgroud)
这让我感到惊讶 - 除非我错误地阅读了已检查/未检查的文档,否则我希望它只会给我一个溢出的输出(因为 Int32.MinValue / -1 = 2^31 = Int32.MaxValue + 1,我期待溢出到 Int32.MinValue 的值)。相反,它抛出了一个OverflowException
.
这是一个显示问题的DotNetFiddle。
我得到OverflowException异常的扔给我时,我不希望他们(或因此我认为).我正在执行一些奇怪的计算,我希望值溢出,丢弃溢出的位.看来我不能让这个工作正常.基本上这是一对i和j,当我迭代大集合(int.MinValue到int.MaxValue)时发生.
// i and j are ints
// i is -2147483648
// j is -1
var x = i / j;
// I also tried using unchecked keyword, but it doesn't help
var x = unchecked(i / j);
Run Code Online (Sandbox Code Playgroud)
更新:
预期的数学值-2147483648/-1是2147483648.但是,这个特定的代码并没有真正尝试找到这个数字.这是一系列比特操纵事情的一部分,有点难以理解.说实话,我甚至不知道自己的目的是什么,因为我没有真正记录这个方法,而且只需要一天时间就可以在我脑海中引发严重的WTF泡沫.所有我知道它的工作原理与设计用于处理案例的特殊代码.
关于预期价值:
因为int只能保持2147483647的最大值,所以我希望丢弃溢出值Y值.
如果我对此有所了解,那么对于模糊方法来说,这可能是文档的重要性.