ToString(“f2”) 中的 .NET 舍入错误

发布于 2024-08-18 09:20:31 字数 724 浏览 5 评论 0原文

您好,我有以下 C# 代码:

float n = 2.99499989f;
MessageBox.Show("n = " + n.ToString("f2", CultureInfo.InvariantCulture));

以及 C++ 代码:

float n = 2.99499989f;
printf("n = %.2f", n);

第一个输出 3.00
第二个输出2.99

我不知道为什么会发生这种情况。

更新:

我还尝试了 Objective-C NSLog,输出是2.99

我需要快速修复它,所以我使用了以下方法:

float n = 2.99499989f;
float round = (float)Math.Round(n, 2);
MessageBox.Show("round = " + round.ToString(CultureInfo.InvariantCulture));

此代码显示2.99,但以双精度计算舍入。我找不到 Math.RoundF。

Hello I have this code in C#:

float n = 2.99499989f;
MessageBox.Show("n = " + n.ToString("f2", CultureInfo.InvariantCulture));

And this code in C++:

float n = 2.99499989f;
printf("n = %.2f", n);

First one outputs 3.00.
Second one outputs 2.99.

I have no clue why this is happening.

Update:

I also tried Objective-C NSLog and the output is 2.99.

I needed to fix it fast so I used following method:

float n = 2.99499989f;
float round = (float)Math.Round(n, 2);
MessageBox.Show("round = " + round.ToString(CultureInfo.InvariantCulture));

This code shows 2.99, but computes round in double precision. I can't find Math.RoundF.

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

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

发布评论

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

评论(7

月寒剑心 2024-08-25 09:20:31

使用 BitConverter.GetBytes 并打印出生成的实际字节表明,这不是编译器差异 - 在这两种情况下,存储的实际浮点值是 0x403FAE14这个方便的计算器告诉我的是确切的值

2.99499988555908203125

因此,差异必定在于 printfToString 的不同行为。更重要的是我不能立即说。

Using BitConverter.GetBytes and printing out the actual bytes produced shows that this is not a compiler difference - in both cases, the actual float value stored is 0x403FAE14, which this handy calculator tells me is the exact value

2.99499988555908203125

The difference therefore must lie in differing behaviours of printf and ToString. More than that I cannot immediately say.

嗳卜坏 2024-08-25 09:20:31

(我意识到这是一个老问题,但我回答这个问题是因为我自己理解了一些事情......)

我的建议是“printf”在应用格式之前将浮点转换为双精度。
我尝试将浮点数转换为双精度数,然后查看双精度数的 ToString("f2") 结果,它在与浮点数的值四舍五入相反的方向上进行舍入。

其他人强化了关于 printf 的这个想法:

C 自动将浮点值转换为双精度值(这是当您调用采用变量参数的函数时进行的标准转换,例如 int printf...

https://stackoverflow.com/a/7480244/119418

https://stackoverflow.com/a/6395747 /119418

我相信浮点向上舍入的原因是因为 2.995F 不能完美转换为二进制并且 = 2.99499989F,并且我们期望 2.995 向上舍入

我相信浮点复制到 a 的原因 。 double 向下舍入是因为 2.995 作为 double <> 2.99499989 作为 double,并且 2.995 实际 double 值大于该值(更接近其真实的十进制值),因此我们希望该值向下舍入2.99499989F

四舍五入到 3.00 似乎是错误的,尽管它看起来小于 2.995,但请记住,2.99499989 是小数,而不是浮点数,并且您将其“转换”为浮点数,基本上是转换基数-10 到以 2 为基数,它实际上是 1 和 0,然后要求它以 10 为基数进行四舍五入,这意味着必须进行转换。嗯,我提到的至少有 2 个以 10 为基数的值可以转换为浮点数,其中最简单的是 2.995。

(I realize this is an old question, but I'm answering this as I am understanding some things myself...)

My suggestion is that "printf" is converting the float to a double before applying the formatting.
I tried converting the float to a double, and then looking at the double's ToString("f2") result, which it did round in the opposite direction as the float's value was rounded.

Others re-enforce this idea about printf:

C automatically converts the float values to double (it is a standard conversion made when you call a function that takes variable arguments, such as int printf...

https://stackoverflow.com/a/7480244/119418

https://stackoverflow.com/a/6395747/119418

I believe the reason the float rounds up is because 2.995F doesn't convert perfectly into binary and is = 2.99499989F, and we'd expect 2.995 to round up.

I believe the reason the float copied into a double rounds down is because 2.995 as a double <> 2.99499989 as a double, and 2.995 actual double value is greater than that value (more precision closer to its true decimal value), and so that value we'd expect to round down.

It might seem wrong that 2.99499989F rounds up to 3.00 even though it is seemingly less than 2.995, but keep in mind that the 2.99499989 is a decimal, not a float, and you are "converting" it into a float, basically converting base-10 to base-2, and it is really 1's and 0's, then asking it to be rounded in base-10 terms, which means a conversion has to happen. Well, there are at least 2 base-10 values that I mentioned that can convert to that number as a float, and the simplest of those is 2.995.

岁吢 2024-08-25 09:20:31

第一个错误地舍入了数字。

我猜第二个只选择逗号后的两位数字。

也许有一些浮点精度问题? 2.9949...实际上四舍五入到超过 2.995?

First one rounds the number incorrectly.

Second one only selects two digits after comma, I guess.

Maybe some floating-point-precission issues? 2.9949... in fact rounds to more than 2.995?

疧_╮線 2024-08-25 09:20:31

浮点精度为 2^{-23} 或大约 0,0000001192(相对误差)。
看,2.995-2.99499989 = 0,00000011。相对误差为3,6*10^{-8}

如果某些编译器读取 2.99499989f 常量的所有数字,则结果是大于 2.995 的数字。但是,如果另一个编译器仅读取 2.994999 (因为精度小于 2^{-23} 并且最后一位数字不重要),则结果是小于 2.995 的数字

The float precision is 2^{-23} or about 0,0000001192 (relative error).
Look, 2.995-2.99499989 = 0,00000011. Relative error is 3,6*10^{-8}.

If some compiler reads all digits of the 2.99499989f constant then the result is number greater than 2.995. But if another compiler reads only 2.994999 (because the precision is less than 2^{-23} and last digits are unimportant) then the result is number less than 2.995

笑红尘 2024-08-25 09:20:31

这不是因为“浮动”不“那么”精确吗?

当您使用小数时,请检查一下:

        // float
        Console.WriteLine (2.99499989f.ToString ("f2"));
        // The above line outputs 3.00

        // decimal
        Console.WriteLine (2.99499989M.ToString ("f2"));
        // The above line outputs 2.99

也许 float 无法表示 2.99499989 或 2.99 等任何数字。
另请看看执行此操作时会发生什么:

// float
Console.WriteLine (2.98499f.ToString ("f2"));
// The above line outputs 2.99

Isn't that due to the fact that a 'float' isn't 'that' precise ?

Check this out, when you use a decimal:

        // float
        Console.WriteLine (2.99499989f.ToString ("f2"));
        // The above line outputs 3.00

        // decimal
        Console.WriteLine (2.99499989M.ToString ("f2"));
        // The above line outputs 2.99

Perhaps float can't represent 2.99499989 or whatever as 2.99.
Look also what happens when you do this:

// float
Console.WriteLine (2.98499f.ToString ("f2"));
// The above line outputs 2.99
悲凉≈ 2024-08-25 09:20:31

我相信解释是 float.ToString() 按解释进行了两次舍入 此处

C# 中的以下代码:

float f = #NUMBER BETWEEN 1 AND 10#;
Console.WriteLine(f.ToString("f2"));

和 C 中的以下代码:

float f = #NUMBER BETWEEN 1 AND 10#;
printf("%.2f", f + 5e-7);

应产生相同的输出。特别是 C# 代码:

float f = 2.99499989f;
Console.WriteLine(f.ToString("f2"));

产生 3.00 并且 C 代码:

float f = 2.99499989f;
printf("%.2f", f + 5e-7);

也产生 3.00

I believe the explanation is that float.ToString() rounds twice as explained here

The following code in C#:

float f = #NUMBER BETWEEN 1 AND 10#;
Console.WriteLine(f.ToString("f2"));

and the following code in C:

float f = #NUMBER BETWEEN 1 AND 10#;
printf("%.2f", f + 5e-7);

Should yield the same output. In particular the C# code:

float f = 2.99499989f;
Console.WriteLine(f.ToString("f2"));

yields 3.00 and the C code:

float f = 2.99499989f;
printf("%.2f", f + 5e-7);

also yields 3.00

萧瑟寒风 2024-08-25 09:20:31

数字 2.99499989f 在 IEEE754 中无法准确表示。显然 printf 和 ToString 处理这种情况的方式不同。

The number 2.99499989f can't be presented exactly in IEEE754. Apparently printf and ToString handle this situation differently.

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