18

虽然“我们都知道”可能有问题,浮点值x == y在哪里x和是,这个问题更具体一点:y

int x = random.Next(SOME_UPPER_LIMIT);
float r = x;
// Is the following ALWAYS true?    
r == x

现在,由于float 的范围比整数的范围大得多(但精度不足以在边缘唯一地呈现整数),如果对这个问题的回答解决了上面的哪些值x可以保证,如果可以保证的话。


目前我的代码正在做这个假设(对于相对较小的 x 值) - 我想确保我不会被咬 :)


这将失败并显示“不等于:16777217”(cast float -> int):

for (int i = 0; i < int.MaxValue; i++) {
   float f = i;
   if ((int)f != i) throw new Exception("not equal " + i);
}

这个类似的代码不会失败(只有 int -> float);但是,由于转换中的损失,有几个浮点数可以“等于”相同的 integer,并且可能代表一个静默错误:

for (int i = 0; i < int.MaxValue; i++) {
   float f = i;
   if (f != i) throw new Exception("not equal " + i);
}
4

5 回答 5

13

是的,无论值是什么,比较总是正确的int

int转换为 afloat进行转换,第一次转换为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

于 2012-09-27T20:07:24.983 回答
5

下面的实验表明答案是你不存在相等不成立的极端情况

    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;

if ((int)f != i)

应该遵循同样的规则。这证明 int -> float 和 float -> int 转换是双射。

注意:实验代码实际上并没有测试边缘情况 int.MaxValue,因为 Parallel.For 的to参数是独占的,但我单独测试了该值并且它也通过了测试。

于 2012-09-27T20:04:47.573 回答
5

在比较 int 和 float 时,int 被隐式转换为 float。这确保了同样的精度损失发生,因此比较总是会发生。只要您不干扰隐式转换或进行算术运算,等式就应该成立。例如,如果你这样写:

bool AlwaysTrue(int i) {
    return i == (float)i;
}

有一个隐式转换,所以它等价于这个应该总是返回 true 的函数:

bool AlwaysTrue(int i) {
    return (float)i == (float)i;
}

但如果你这样写:

bool SometimesTrue(int i) {
    return i == (int)(float)i;
}

那么就没有更多的隐式转换,精度的损失只发生在右侧。结果可能是错误的。同样,如果你这样写:

bool SometimesTrue(int i) {
    return 1 + i == 1 + (float)i;
}

那么双方的精度损失可能不相等。结果可能是错误的。

于 2012-09-27T20:07:35.140 回答
3

我运行了这段代码,没有抛出异常:

for (int x = Int16.MinValue; x < Int16.MaxValue; x++)
{
 float r = x;
 if (r != x)
 {
  throw new Exception("Failed at: " + x);
 }
}

仍在等待(未完成此测试,因为它花费了太长时间,但在运行时从未抛出异常):

for (long x = Int64.MinValue; x < Int64.MaxValue; x++)
{
 float r = x;
 if (r != x)
 {
  throw new Exception("Failed at: " + 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);
}
于 2012-09-27T20:05:07.213 回答
0

我对浮点算术计算的理解是它们由 CPU 处理,这完全决定了你的精度。因此,没有确定的值超过该值,浮点数会失去精度。

例如,我曾认为 x86 架构可以保证最低限度,但事实证明我错了。

于 2012-09-27T20:05:10.957 回答