使用ToString("F1")时浮点数舍入不正确。

11

我有一个浮点值: 12345.6489

当我使用以下方式进行格式化:

(12345.6489f).ToString("F1")

然后我得到的结果是:

12345.7

但是这是不正确的,因为它应该是12345.6。

有人明白为什么会发生这种情况吗?另一个提示是,在格式化之前将其转换为double会返回正确的结果,而如果我的浮点值稍小,例如1234.6489,那么我也会得到正确的结果。


1
@DavidStratton:Double 也不是精确的,会出现类似的怪异情况。它们只是具有不同的精度和范围。 - Jon Skeet
@JonSkeet - 感谢您的澄清!我已删除该评论。 - David
1
Single.ToString做了一些非常奇怪的事情。我认为你需要查看IL。文档(http://msdn.microsoft.com/en-us/library/f71z6k0c.aspx)说:“默认情况下,返回值只包含7位精度,尽管最多保留9位内部”,但我确定我不知道这意味着什么。 - David Heffernan
哦,[MethodImpl(MethodImplOptions.InternalCall)],所以没有 IL。 - David Heffernan
@DavidHeffernan 还有一个有趣的事实需要注意,虽然在大多数地区,float 比具有 7 个有效数字的十进制数更精确,但也有一些地方不是这样。在这些地区,由于 ToString() 只提供了 7 位小数,因此会得到有趣的结果。例如:在接近 90 亿(9E+09f)时,具有 7 位数字的十进制数的精度为 1000,而 float 的精度仅为 1024。因此,例如 float x = 9E+09f; Console.WrireLine(x); 将会给出一个惊喜(9000000000 是 512 对 1024 取模)。 - Jeppe Stig Nielsen
假设float是32位IEEE单精度浮点数,最接近12345.6489的可表示值正好是12345.6484375。比它大的下一个可表示值正好是12345.6494140625。因此,有足够的精度来正确获取小数点后一两位数字。对于double(IEEE 64位),周围的值分别为12345.64889999999832070898264646530151367187512345.64890000000013969838619232177734375 - Keith Thompson
2个回答

3
这似乎与我以前提出的一个问题有关:.NET中的Double.ToString方法的两次舍入误差。请注意,如果您在数字上调用.ToString("G"),它将被正确地四舍五入为12345.65。如果您对四舍五入后的数字进行一位小数的舍入,则会出现问题。当我早些时候调查自己的问题时,我还发现了一些无法解释为两次舍入错误的示例,因此也要检查该线程。 补充:请注意,任何可以由float(精确)表示的数字也可以(带有许多零位)由double表示。可以使用以下技巧(该问题也提到了):
float x = 12345.6489f;
string trick = ((double)x).ToString("F1");

1
感谢您的提问!这是一个非常有趣的调查课题。但我想提到另一面的问题。您问了以下问题:
(12345.6489f).ToString("F1")
然后我得到了结果
12345.7
但这是不正确的,因为它应该是12345.6。
那么,我想知道您是如何确定什么是“正确”的和什么是“不正确”的输出结果的?这些格式化字符串不应该用于舍入目的。文档明确地说明了这一点:

http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx#FFormatString


说实话,当我第一次看到你问题中的数字时 - 第一个想法是 Hans Passant 在他的回答中提到的四舍五入算法。所以,我甚至不奇怪这种算法被选择了,它实际上是相当直观的 :) 我甚至不会感到惊讶,如果他们考虑将简单截断作为浮点数格式化的算法。它仍然非常准确和有效。
因此,尽管所有这些都非常有趣,看起来像是一个错误/悖论/奇迹,但实际上这只是我们对于这个函数期望的错误,它是为了做(并且实际上做得很好)另一件事而设计的。

啊,我曾经在某个地方读到过字符串格式化应该为你四舍五入数字,这就是为什么我认为12345.6应该是正确的答案。是的,Math.Round肯定更可靠! - L. Desjardins
Math.Round完全没有帮助。它返回一个浮点值。你仍然需要得到一个字符串,以便回到ToString。你需要在转换为字符串的过程中进行四舍五入。这很关键,因为浮点数使用二进制表示,但字符串表示是十进制的。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接