是";(浮点)整数==整数“;保证在C#中相等?
虽然“我们都知道”是";(浮点)整数==整数“;保证在C#中相等?,c#,floating-point,equality,C#,Floating Point,Equality,虽然“我们都知道”x==y可能有问题,其中x和y是浮点值,但这个问题更具体一些: int x = random.Next(SOME_UPPER_LIMIT); float r = x; // Is the following ALWAYS true? r == x 现在,由于的浮点范围比整数的浮点范围大得多(但精度不足以在边缘唯一表示整数),如果对这个问题的回答也能解决上述x的哪些值可以得到保证(如果可以得到保证的话),那就好了 目前,我的代码正在做这个假设(对于相对较小的x值)-我
x==y
可能有问题,其中x
和y
是浮点值,但这个问题更具体一些:
int x = random.Next(SOME_UPPER_LIMIT);
float r = x;
// Is the following ALWAYS true?
r == x
现在,由于的浮点范围比整数的浮点范围大得多(但精度不足以在边缘唯一表示整数),如果对这个问题的回答也能解决上述x
的哪些值可以得到保证(如果可以得到保证的话),那就好了
目前,我的代码正在做这个假设(对于相对较小的x值)-我想确保我不会被咬:)
这将以“不相等:16777217”(强制浮点->整数)失败:
for(int i=0;i
类似的代码不会失败(只有int->float);但是,由于转换中的丢失,有几个浮点数可以“等于”同一个整数,并且可能表示一个静默错误:
for (int i = 0; i < int.MaxValue; i++) {
float f = i;
if (f != i) throw new Exception("not equal " + i);
}
for(int i=0;i
下面的实验表明,答案是你没有相等不成立的边缘情况
static void Main(string[] args)
{
Parallel.For(int.MinValue, int.MaxValue, (x) =>
{
float r = x;
// Is the following ALWAYS true?
bool equal = r == x;
if (!equal) Console.WriteLine("Unequal: " + x);
});
Console.WriteLine("Done");
Console.ReadKey();
return;
}
这似乎是合理的转换
float f = i;
及
应该遵循同样的规则。这证明了int->float和float->int转换是一个双射
注意:实验代码实际上没有测试edge case int.MaxValue,因为Parallel.For的to参数是独占的,但我单独测试了该值,它也通过了测试。我运行此代码时没有引发异常:
for (int x = Int16.MinValue; x < Int16.MaxValue; x++)
{
float r = x;
if (r != x)
{
throw new Exception("Failed at: " + x);
}
}
for(intx=Int16.MinValue;x
仍在等待(未完成此测试,因为它花费的时间太长,但在运行时从未引发异常):
for(长x=Int64.MinValue;x
返回并运行您的示例,附带一条警告,这是输出:
[Exception: not equal 16777217 ?= 1.677722E+07 ?= 16777216]
for (int i = 0; i < int.MaxValue; i++)
{
float f = i;
if ((int)f != i) throw new Exception("not equal " + i + " ?= " + f + " ?= " + (int)f);
}
[例外:不等于16777217?=1.677722E+07?=16777216]
对于(int i=0;i
我对浮点算术计算的理解是,它们是由CPU处理的,CPU只决定精度。因此,没有确定的值,超过该值,浮点将失去精度
例如,我原以为x86体系结构保证了最小值,但事实证明我错了。是的,无论int的值是多少,比较总是正确的
int
将被转换为float
进行转换,第一次转换为float
将始终得到与第二次转换相同的结果
考虑:
int x = [any integer value];
float y = x;
float z = x;
y
和z
的值将始终相同。如果转换失去精度,两个转换将以完全相同的方式失去精度
如果将float
转换回int
以进行比较,则这是另一回事
另外,请注意,即使特定的
int
值转换为float
始终会产生相同的float
值,这并不意味着float
值对于该int
值必须是唯一的。有int
值,其中(float)x==(float)(x+1)
将为true
比较int和float时,int隐式转换为float。这确保了同样的精度损失,因此比较总是正确的。只要不干扰隐式强制转换或进行算术运算,等式就应该成立。例如,如果您编写以下内容:
bool AlwaysTrue(int i) {
return i == (float)i;
}
bool SometimesTrue(int i) {
return i == (int)(float)i;
}
bool SometimesTrue(int i) {
return 1 + i == 1 + (float)i;
}
有一个隐式强制转换,因此它相当于此函数,该函数应始终返回true:
bool AlwaysTrue(int i) {
return (float)i == (float)i;
}
但如果你写这封信:
bool AlwaysTrue(int i) {
return i == (float)i;
}
bool SometimesTrue(int i) {
return i == (int)(float)i;
}
bool SometimesTrue(int i) {
return 1 + i == 1 + (float)i;
}
这样就没有更多的隐式强制转换,精度损失只发生在右侧。结果可能是错误的。类似地,如果您编写以下内容:
bool AlwaysTrue(int i) {
return i == (float)i;
}
bool SometimesTrue(int i) {
return i == (int)(float)i;
}
bool SometimesTrue(int i) {
return 1 + i == 1 + (float)i;
}
那么,双方的精度损失可能并不相等。结果可能是错误的。通过
Int32.MinValue
循环到Int32.MaxValue
,每次比较强制转换的结果。收集那些比较结果是错误的案例,然后你就有了答案(至少对你的架构是这样)。@pst:老实说,不要认为在这个问题上有任何通用的正确答案。假设“总是”不会在不同的机器上工作,所以它永远不会总是。如果,很自然,我们在这里谈论的是肯定的答案。啊,我明白了,我把我的头脑搞砸了——方向。谢谢。这正是我头脑中缺少的步骤/逻辑:y=x
,z=x
,因此y==z
。现在它更有意义了。请注意,这是特定于语言的。某些语言可能不要求将整数转换为浮点的结果保持一致。(例如,一个可以在具有额外精度的寄存器中完成,另一个可以在内存中完成。当获取内存结果并将其增加到寄存器长度时,丢失的精度可能会导致错误比较。)+1。可以查看详细的浮点格式。我的理解是int
->float
转换它大致上是(value&0xFFFFFD00
),所以int将被分组为~512个值的簇,这些值等于相同的float值。@AlexeiLevenkov:对于非常大的整数值也是如此。最多七位的整数可以精确地表示为一个浮点,然后随着整数值的增大,它们被分组在更大的簇中。是的,谢谢你指出倒数不是