【发布时间】:2017-05-25 12:58:19
【问题描述】:
注意:我想声明,在提交我的问题之前,我已经做出了合理的努力,以自己和其他问题找到解决方案 - 但是我找到的解决方案不适合我的需求。
我正在使用的 API 返回一个带有 7 位小数的 double,例如: 1495120024.7705889
但是我需要将其打印(或返回一个字符串)为:1495120024.770589 - 基本上四舍五入到小数点后六位。
原本看似微不足道的事情,结果却令人费解。
这些是我尝试过的方法(假设上面的值存储在一个名为 d 的 double 中):
d.ToString() = 1495120024.77059
d.ToString("F6") = 1495120024.770590
d.ToString("F7") = 1495120024.7705900
Math.Round(d, 6) = 1495120024.7705891 - [舍入误差?]
Math.Truncate(d * 1000000) / 1000000 = 1495120024.7705879
Convert.ToDecimal(Math.Truncate(d * 1000000)) = 1495120024770590
Convert.ToDecimal(d) = 1495120024.77059
我知道这听起来很傻,但我需要将其四舍五入到小数点后六位并正确设置为字符串。我也知道双精度数的舍入错误,但遗憾的是我无法控制 API 返回的数据类型。
编辑 v1:
根据BWA 的评论,这是我想出的解决方案:
var s = Math.Round(d - Math.Truncate(d), 6).ToString();
s = s.Substring(s.IndexOf(".") + 1);
s = $"{Math.Truncate(d)}.{s}";
但是hack这个词甚至还没有开始描述这个......
【问题讨论】:
-
标记为 [rounding error] 的那个是合法的,确切的值是 1495120024.7705891132354736328125(这是你能找到的最接近的 1495120024.770589,因此也是输入
double x = 1495120024.770589;的实际结果)。我想它以这种方式打印有点烦人(它不是解析回值的最短字符串)。我不确定 F6 格式有什么问题 -
只要我找到一种合适的方法将它转换为字符串然后可能会修剪掉它,我就会对那一点舍入错误很好。但即使是转换为字符串的行为也会把它搞砸:(
-
是浮点点,小数点位置不相关。
float可存储 6 位有效数字,double可存储 15 位有效数字。如果计算复杂,您将丢失数字。您只能看到 17 位数字,因为这是调试器用来将 binary 值转换为十进制的。四舍五入到 16 位只会误导任何人假设结果比它可能的更准确。它不会欺骗机器,最后一位数字只是随机噪声。提前满足您的需求的唯一方法是在decimal中完成所有数学运算,它可以存储 28 位数字。 -
任何代码 sn-ps @HansPassant ?正如我所说,我收到的数据类型是双重的,我无法改变它。尝试转换为十进制也会破坏任何其他计算之前的数字。