试图模仿 Excel 舍入让我很伤心
Trying to mimic Excel rounding is causing me grief
该值为2.01和2.52之和除以2的结果(2.01 + 2.52) / 2
。 Excel 将此值显示为 2.265,当格式化为 2 位数字时,它是 2.27。但是,文件中存储的值为 2.2649999999999997。当我重新创建这是 C# 时,我也在我的变量中得到了那个值,而不是 2.265。我知道这是由于 4.53 除以 2 的浮点精度问题。
double result = (2.01 + 2.52) / 2;
Console.WriteLine(result);
控制台显示 2.265,但 QuickWatch 调试器中显示的值显示 2.264999999999997。我怀疑在 WriteLine 方法中将值转换为字符串正在纠正浮点精度错误。
当我应用 Math.Round(result, 2, MidpointRounding.AwayFromZero)
时,返回的结果是 2.26
,而不是我预期的 2.27
。看起来它看起来像是我想要四舍五入的数字右边的第一个数字,看到它是 4 并忽略它右边的所有其他内容。问题是那些 9 只是因为精度问题而存在,需要包括在内,或者更好的是,该值应该是 2.265
.
我在我的代码中所做的是从 Excel 电子表格 "2.2649999999999997"
中读取文本值,将其转换为双精度 2.264999999999999
,然后转换为字符串,这给我 "2.265"
。然后我将其转换回双精度 2.265
,以便我可以将 Math.Round
应用于它并获得 2.27
的预期结果。这是完整的代码:
double result = Convert.ToDouble(((2.01 + 2.52) / 2).ToString());
Console.WriteLine(Math.Round(result, 2, MidpointRounding.AwayFromZero));
我依靠 ToString 来清理浮点精度和舍入问题的方法是否正确?如果没有,我应该怎么做?
第一:题目很难。因为 4.53/2 = 2.265。这四舍五入为 2.27。然而,计算中导致较小结果 (2.264999999....) 的最小舍入误差将导致舍入为 2.26。这就是这里发生的事情。
要解决这个问题,您需要一个浮点运算,它具有与 Excel 相同的内部舍入误差。
从该文档https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel 看来,Excel 使用的是 修改后的 版本的 IEEE 754,而 C# 使用的是 IEEE 754。我不知道差异在哪里,但看起来好像在内部 Excel 产生不同的舍入误差。
本文档描述了差异:https://support.microsoft.com/en-us/kb/78113/en-us
(例如,Excel 不使用非规范化数字。这意味着数字 < 2 的舍入误差有不同的行为)。
所以我假设你无法使用 "double"
解决这个问题
更新
但是,现在我明白问题不在于算法,而是 Excel 显示数字的方式,也许这是一个解决方案
Math.Round(Math.Round(result, 3, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero)
先轮到3,再轮到2位数。在我看来 Excel 正在这样做。
该值为2.01和2.52之和除以2的结果(2.01 + 2.52) / 2
。 Excel 将此值显示为 2.265,当格式化为 2 位数字时,它是 2.27。但是,文件中存储的值为 2.2649999999999997。当我重新创建这是 C# 时,我也在我的变量中得到了那个值,而不是 2.265。我知道这是由于 4.53 除以 2 的浮点精度问题。
double result = (2.01 + 2.52) / 2;
Console.WriteLine(result);
控制台显示 2.265,但 QuickWatch 调试器中显示的值显示 2.264999999999997。我怀疑在 WriteLine 方法中将值转换为字符串正在纠正浮点精度错误。
当我应用 Math.Round(result, 2, MidpointRounding.AwayFromZero)
时,返回的结果是 2.26
,而不是我预期的 2.27
。看起来它看起来像是我想要四舍五入的数字右边的第一个数字,看到它是 4 并忽略它右边的所有其他内容。问题是那些 9 只是因为精度问题而存在,需要包括在内,或者更好的是,该值应该是 2.265
.
我在我的代码中所做的是从 Excel 电子表格 "2.2649999999999997"
中读取文本值,将其转换为双精度 2.264999999999999
,然后转换为字符串,这给我 "2.265"
。然后我将其转换回双精度 2.265
,以便我可以将 Math.Round
应用于它并获得 2.27
的预期结果。这是完整的代码:
double result = Convert.ToDouble(((2.01 + 2.52) / 2).ToString());
Console.WriteLine(Math.Round(result, 2, MidpointRounding.AwayFromZero));
我依靠 ToString 来清理浮点精度和舍入问题的方法是否正确?如果没有,我应该怎么做?
第一:题目很难。因为 4.53/2 = 2.265。这四舍五入为 2.27。然而,计算中导致较小结果 (2.264999999....) 的最小舍入误差将导致舍入为 2.26。这就是这里发生的事情。 要解决这个问题,您需要一个浮点运算,它具有与 Excel 相同的内部舍入误差。
从该文档https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel 看来,Excel 使用的是 修改后的 版本的 IEEE 754,而 C# 使用的是 IEEE 754。我不知道差异在哪里,但看起来好像在内部 Excel 产生不同的舍入误差。
本文档描述了差异:https://support.microsoft.com/en-us/kb/78113/en-us (例如,Excel 不使用非规范化数字。这意味着数字 < 2 的舍入误差有不同的行为)。
所以我假设你无法使用 "double"
解决这个问题更新
但是,现在我明白问题不在于算法,而是 Excel 显示数字的方式,也许这是一个解决方案
Math.Round(Math.Round(result, 3, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero)
先轮到3,再轮到2位数。在我看来 Excel 正在这样做。