“(float)integer == integer”在C#中是否保证相等?
虽然“我们都知道” 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); 但是, 由于转换中的丢失,有几个浮点数可以“等于”相同的整数 ,并且可能代表一个无声的错误:
for (int i = 0; i < int.MaxValue; i++) { float f = i; if (f != i) throw new Exception("not equal " + i); }
是的,无论int
是什么值,比较都将是真的。
int
将转换为float
以进行转换,第一次转换为float
将始终提供与第二次转换相同的结果。
考虑:
int x = [any integer value]; float y = x; float z = x;
y
和z
的值将始终相同。 如果转换失去精度,则两次转换都将以完全相同的方式失去精度。
如果你将float
转换回int
到比较,那是另一回事。
另请注意,即使转换为float
的特定int
值始终导致相同的float
值,也不意味着float
值必须对该int
值唯一。 有int
值,其中(float)x == (float)(x+1)
为true
。
比较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; }
那么精确度的损失可能在双方都不相同。 结果可能是错误的。
以下实验表明,答案是你没有那种不等式的边缘情况
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)
应遵循相同的规则。 这certificate了int – > float和float – > int转换是一个双射。
注意:实验代码实际上不测试边缘情况int.MaxValue,因为Parallel.For的参数是独占的,但我分别测试了该值,它也通过了测试。
我运行此代码时没有抛出exception:
for (int x = Int16.MinValue; x < Int16.MaxValue; x++) { float r = x; if (r != x) { throw new Exception("Failed at: " + x); } }
还在等待(没有完成这个测试,因为它花了太长时间,但在运行时从未抛出exception):
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); }
我对浮点算术计算的理解是它们由CPU处理,它只能决定你的精度。 因此,没有确定的值,浮动失去精度。
我曾经认为x86架构保证了最低限度,但我被certificate是错误的。