ToString(“f2”) 中的 .NET 舍入错误
您好,我有以下 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
使用 BitConverter.GetBytes 并打印出生成的实际字节表明,这不是编译器差异 - 在这两种情况下,存储的实际浮点值是
0x403FAE14
,这个方便的计算器告诉我的是确切的值因此,差异必定在于
printf
和ToString
的不同行为。更重要的是我不能立即说。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 is0x403FAE14
, which this handy calculator tells me is the exact valueThe difference therefore must lie in differing behaviours of
printf
andToString
. More than that I cannot immediately say.(我意识到这是一个老问题,但我回答这个问题是因为我自己理解了一些事情......)
我的建议是“printf”在应用格式之前将浮点转换为双精度。
我尝试将浮点数转换为双精度数,然后查看双精度数的 ToString("f2") 结果,它在与浮点数的值四舍五入相反的方向上进行舍入。
其他人强化了关于 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:
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.
第一个错误地舍入了数字。
我猜第二个只选择逗号后的两位数字。
也许有一些浮点精度问题? 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?
浮点精度为
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 about0,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 only2.994999
(because the precision is less than 2^{-23} and last digits are unimportant) then the result is number less than 2.995这不是因为“浮动”不“那么”精确吗?
当您使用小数时,请检查一下:
也许 float 无法表示 2.99499989 或 2.99 等任何数字。
另请看看执行此操作时会发生什么:
Isn't that due to the fact that a 'float' isn't 'that' precise ?
Check this out, when you use a decimal:
Perhaps float can't represent 2.99499989 or whatever as 2.99.
Look also what happens when you do this:
我相信解释是
float.ToString()
按解释进行了两次舍入 此处C# 中的以下代码:
和 C 中的以下代码:
应产生相同的输出。特别是 C# 代码:
产生
3.00
并且 C 代码:也产生
3.00
I believe the explanation is that
float.ToString()
rounds twice as explained hereThe following code in C#:
and the following code in C:
Should yield the same output. In particular the C# code:
yields
3.00
and the C code:also yields
3.00
数字 2.99499989f 在 IEEE754 中无法准确表示。显然 printf 和 ToString 处理这种情况的方式不同。
The number 2.99499989f can't be presented exactly in IEEE754. Apparently printf and ToString handle this situation differently.