为什么我的数字四舍五入不正确?

发布于 2024-08-26 13:47:53 字数 255 浏览 7 评论 0原文

这感觉像是那种只会在原地失败的代码,但我会尝试将其改编成代表我所看到内容的代码片段。

float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);

单步执行代码后,i==269,i2==268。这里发生了什么来解释这种差异?

This feels like the kind of code that only fails in-situ, but I will attempt to adapt it into a code snippet that represents what I'm seeing.

float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);

After stepping through the code, i==269, and i2==268. What's going on here to account for the difference?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

一张白纸 2024-09-02 13:47:53

浮点数学的执行精度比宣传的要高。但一旦将其存储在 float f 中,额外的精度就会丢失。当然,除非您将结果转换为 int,否则您不会失去第二种方法的精度。

编辑:参见这个问题 为什么 C# 中用括号分隔和用语句分隔时浮点精度不同? 以获得比我可能提供的更好的解释。

Float math can be performed at higher precision than advertised. But as soon as you store it in float f, that extra precision is lost. You're not losing that precision in the second method until, of course, you cast the result down to int.

Edit: See this question Why differs floating-point precision in C# when separated by parantheses and when separated by statements? for a better explanation than I probably provided.

烟火散人牵绊 2024-09-02 13:47:53

因为浮点变量不是无限准确。如果您需要这种精度,请使用小数。

不同的舍入模式也可能会导致此问题,但准确性问题是您要解决的问题正在跑到这里,据我所知。

Because floating point variables are not infinitely accurate. Use a decimal if you need that kind of accuracy.

Different rounding modes may also play into this issue, but the accuracy problem is the one you're running into here, AFAIK.

幻想少年梦 2024-09-02 13:47:53

浮点的精度有限,并且基于二进制而不是十进制。十进制数 13.45 无法用二进制浮点数精确表示,因此向下舍入。乘以 20 进一步加剧了精度损失。此时您有 268.999... - 不是 269 - 因此转换为整数会截断为 268。

要四舍五入到最接近的整数,您可以尝试在转换回整数之前添加 0.5。

对于“完美”算术,您可以尝试使用 Decimal 或 Rational 数字类型 - 我相信 C# 具有适用于这两种类型的库,但我不确定。然而,这些会比较慢。

编辑 - 到目前为止,我已经找到了“十进制”类型,但不是有理数 - 我对可用的可能是错误的。十进制浮点是不准确的,就像二进制一样,但这是我们习惯的那种不准确,因此它给出的结果不太令人惊讶。

Floating point has limited accuracy, and is based on binary rather than decimal. The decimal number 13.45 cannot be precisely represented in binary floating point, so rounds down. The multiplication by 20 further exaggerates the loss of precision. At this point you have 268.999... - not 269 - therefore the conversion to integer truncates to 268.

To get rounding to the nearest integer, you could try adding 0.5 before converting back to integer.

For "perfect" arithmetic, you could try using a Decimal or Rational numeric type - I believe C# has libraries for both, but am not certain. These will be slower, however.

EDIT - I have found a "decimal" type so far, but not a rational - I may be wrong about that being available. Decimal floating point is inaccurate, just like binary, but it's the kind of inaccuracy we're used to, so it gives less surprising results.

糖果控 2024-09-02 13:47:53

替换为

double f = myFloat * myConstInt;

,看看是否得到相同的答案。

Replace with

double f = myFloat * myConstInt;

and see if you get the same answer.

千仐 2024-09-02 13:47:53

我想提供一个不同的解释。

这是我已注释的代码(我查看了内存以剖析浮点数):

 float myFloat = 13.45; //In binary is 1101.01110011001100110011
 int myConstInt = 20;
 float f = myFloat * myConstInt; //In binary is exactly 100001101 (269 decimal)
 int i = (int)f; // Turns float 269 into int 269 -- no surprises
 int i2 = (int)(myFloat * myConstInt);//"Extra precision" causes round to 268

让我们仔细看看计算:

  • f = 1101.01110011001100110011 * 10100 = 100001100.111111111111111 111

    是第 25-27 位,这会导致第 24 位向上舍入,因此整个值向上舍入为 269

  • int i2 = (int)(myFloat * myConstInt)

    myfloat扩展为双精度计算(附加0):1101.0111001100110011001100000000000000000000000000000

    myfloat * 20 = 100001100.11111111111111111100000000000000000000000000

    第 54 位及以上均为 0,因此不进行舍入:转换结果为整数 268。

    (如果使用扩展精度,则类似的解释也有效。)

更新:我改进了我的答案并写了一篇完整的文章,名为当浮点数的行为与浮点数不同时

I'd like to offer a different explanation.

Here's the code, which I've annotated (I looked into memory to dissect the floats):

 float myFloat = 13.45; //In binary is 1101.01110011001100110011
 int myConstInt = 20;
 float f = myFloat * myConstInt; //In binary is exactly 100001101 (269 decimal)
 int i = (int)f; // Turns float 269 into int 269 -- no surprises
 int i2 = (int)(myFloat * myConstInt);//"Extra precision" causes round to 268

Let's look closer at the calculations:

  • f = 1101.01110011001100110011 * 10100 = 100001100.111111111111111 111

    The part after the space is bits 25-27, which cause bit 24 to be rounded up, and hence the whole value to be rounded up to 269

  • int i2 = (int)(myFloat * myConstInt)

    myfloat is extended to double precision for the calculation (0s are appended): 1101.0111001100110011001100000000000000000000000000000

    myfloat * 20 = 100001100.11111111111111111100000000000000000000000000

    Bits 54 and beyond are 0s, so no rounding is done: the cast results in the integer 268.

    (A similar explanation would work if extended precision is used.)

UPDATE: I refined my answer and wrote a full-blown article called When Floats Don’t Behave Like Floats

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文