是";(浮点)整数==整数“;保证在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:对于非常大的整数值也是如此。最多七位的整数可以精确地表示为一个浮点,然后随着整数值的增大,它们被分组在更大的簇中。是的,谢谢你指出倒数不是