简体   繁体   English

C#int存储在double“==”精度问题中

[英]C# int stored in double “==” precision problem

Here is the simplified code: 这是简化的代码:

int i = 1;
double a = i;
double b = i;

Is it guarantied that a == b is true ? 保证a == b真的吗?

Yes. 是。 32-bit integers can be represented exactly as 64-bit floating point numbers. 32位整数可以完全表示为64位浮点数。

Is it guarantied that a == b is true? 保证a == b是真的吗?

Yes. 是。 This is because you perform the same conversion twice and given its deterministic behavior you will end up with the same values regardless of the rounding problems. 这是因为您执行两次相同的转换并给定其确定性行为,无论舍入问题如何,您都将得到相同的值。

We can generalize your question though, to: 我们可以将您的问题概括为:

Can we perform arithmetic operations on 32-bit integer values encoded in double type without precision loose? 我们可以对double精度编码的32位整数值执行算术运算而不会出现精度松散吗?

The answer for such question is yes as well. 这个问题的答案也是肯定的。

A short justification is that operations on mantissa bits (see http://en.wikipedia.org/wiki/Significand ) are precise if it is only possible and in case of 32-bit integer values it is possible. 一个简短的理由是,对尾数位的操作(参见http://en.wikipedia.org/wiki/Significand )是精确的,只要它是可能的,并且在32位整数值的情况下是可能的。

Longer story comes here. 更长的故事来到这里。 As long as your integer value fits in 52 bits of a fraction part called mantissa (see http://en.wikipedia.org/wiki/Double_precision ) all calculations on integer values using double will behave completely OK. 只要您的整数值适合称为尾数的小数部分的52位(请参阅http://en.wikipedia.org/wiki/Double_precision ),所有使用double的整数值计算都将完全正常。

This is because your number (say 173 which is 0000010101101b binary) will be represented as 1.010110100000b*2^7 , which is accurate. 这是因为你的号码(比如173,即0000010101101b二进制)将表示为1.010110100000b*2^7 ,这是准确的。

All operations on mantissa are straight forward as long as they fit in mantissa. 尾数上的所有操作都是直接的,只要它们适合尾数。 Rounding on integers occurs when result of a particular operation do not fit in mantissa - eg. 当特定操作的结果不适合尾数时,发生对整数的舍入 - 例如。 you would multiply 40 bits of mantissa by 40 bits of mantissa. 你会将40位的尾数乘以40位的尾数。 Rounding on floating point operations additionally occur when exponents are much different. 当指数差异很大时,还会出现浮点运算的舍入。 In this case even a simple addition operation can loose precision because matissas are shifted. 在这种情况下,即使是简单的加法操作也会因为matissas移位而失去精度。

Back to integers encoded in double - even division operation is precise, as long as the result is integer value. 返回以double - even除法运算编码的整数是精确的,只要结果是整数值。 So 4.0/2.0 == 8.0/4.0 is also guaranteed to be true. 所以4.0/2.0 == 8.0/4.0也保证是真的。

The problem begins when your number is not integer. 当您的数字不是整数时,问题就开始了。 But even in this case numbers are guaranteed to be represented precisely if they are a form of x/2^y and x fits in 52 bits (eg. 3/4 5/8 345/1024 ). 但即使在这种情况下,如果它们是x/2^y的形式并且x适合52位(例如, 3/4 5/8 3/4 5/8 345/1024 ),则可以保证数字被精确地表示。 Operations on such numbers are also precise given y can be equal for both operands, so even: 这些数字的操作也是精确的,因为对于两个操作数, y可以相等,所以即使:

123456789/1024/1024/1024/1024 == 
(23456789/1024/1024/1024/1024 +
100000000/1024/1024/1024/1024)

is guaranteed to be true. 保证是真的。

Interesting fact is that you can perform operation on 54 bit signed integers safely. 有趣的是,您可以安全地对54位有符号整数执行操作。 This is because you have additional bit at the beginning whose meaning is encoded by the exponent and one additional bit for a sign. 这是因为您在开头有额外的位,其含义由指数编码,另外一位用于符号。 Now -2^53 which would be MIN_INT in case of 54 bit signed integer does not fit the mantissa, but exponent will do the job here with mantissa full of zeros. 现在-2 ^ 53在54位有符号整数的情况下将是MIN_INT不适合尾数,但是指数将在这里完成尾数充满零的工作。

Yes, you can store a (32-bit) integer number in a double (64-bit floating-point number) without precision loss. 是的,您可以在double精度(64位浮点数)中存储(32位)整数,而不会丢失精度。

However, as soon as you perform calculations with your double , you will very likely introduce rounding errors, ie precision loss. 但是,只要用double精度执行计算,就很可能会引入舍入误差,即精度损失。 These errors will likely be small enough so that they get rounded away when you cast your double value back to int — but the error is there, so be aware of it. 这些错误可能足够小,以便在将double值转换回int时它们会被舍入 - 但错误就在那里,所以要注意它。

How it's done: See this document ( IEEE Standard 754 Floating Point Numbers by Steve Hollasch) for details on how an integer can be stored as a floating-point value. 工作 原理:有关如何将整数存储为浮点值的详细信息,请参阅此文档(Steve Hollasch的IEEE标准754浮点数

To summarise (somewhat inaccurately), a floating-point value consists of three parts: A sign bit, a "fraction" part (called the mantissa), and an "exponent" part. 总结(有些不准确),浮点值由三部分组成:符号位,“分数”部分(称为尾数)和“指数”部分。 They're put together roughly as follows: 它们大致如下:

value = -1 sign bit × fraction × 2 exponent value = -1 符号位 × 分数 ×2 指数

You can store the integer value in the "fraction" part of the double (which is 52 bits wide, which is more than wide enough for a 32-bit integer. The "exponent" part can just be set to 0, since it's not needed. 你可以将整数值存储在double的“fraction”部分(52位宽,这对于32位整数来说足够宽。“exponent”部分可以设置为0,因为它不是需要。

I opened Visual Studio, and I tested it. 我打开了Visual Studio,然后测试了它。

Here is my code: 这是我的代码:

int i = 5;
double t = i;
double k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i += 5;
t += 5;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i += (int)Math.Round(5.6);
t += 5.6;
t = (int)Math.Round(t);
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i = int.MaxValue - 5438;
t = int.MaxValue - 5438;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i = (int)Math.Round(double.MaxValue);
t = Math.Round(double.MaxValue);
k = i;
MessageBox.Show((i == t).ToString()); //false
MessageBox.Show((k == t).ToString()); //false
i = (int)Math.Round(double.MaxValue);
t = i;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true

The result was two messageboxes saying true. 结果是两个消息框说真的。

I guess that concludes that: Yes, you are guaranteed that it will be true. 我猜这得出结论:是的,你可以保证这是真的。

EDIT: Extended my test a little. 编辑:扩展我的测试一点。 The only test returning false were the test at double.MaxValue, but i doubt that you will use that big numbers. 返回false的唯一测试是double.MaxValue的测试,但我怀疑你会使用那些大数字。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM