我知道浮点运算有精度问题。我通常通过切换到数字的固定十进制表示法或简单地忽略错误来克服它们。
但是,我不知道这种不准确的原因是什么。为什么浮点数会有这么多的舍入问题?
decimal
类型的工作原理。另一方面,固定点是不同的。只要范围有限,定点就是一个很好的答案。但是,限制范围使定点不适合许多数学应用,因此,在硬件中定点数的实现常常没有得到很好的优化。
我知道浮点运算有精度问题。我通常通过切换到数字的固定十进制表示法或简单地忽略错误来克服它们。
但是,我不知道这种不准确的原因是什么。为什么浮点数会有这么多的舍入问题?
decimal
类型的工作原理。另一方面,固定点是不同的。只要范围有限,定点就是一个很好的答案。但是,限制范围使定点不适合许多数学应用,因此,在硬件中定点数的实现常常没有得到很好的优化。
Answers:
这是因为某些分数需要非常大(甚至是无限大)的位置来表示而不进行舍入。这对于十进制表示法和对二进制或任何其他表示法一样适用。如果要限制用于计算的小数位数(并避免以分数表示法进行计算),则必须将一个简单的表达式四舍五入为1/3 + 1/3。而不是写2/3,因此您必须写0.33333 + 0.33333 = 0.66666,这与2/3不同。
对于计算机,数字位数受其内存和CPU寄存器的技术特性限制。内部使用的二进制符号增加了更多困难。尽管某些编程语言添加了此功能,但计算机通常无法用分数表示法表示数字,从而可以在一定程度上避免这些问题。
舍入误差主要来自以下事实:所有实数的无穷可能无法由计算机的有限内存来表示,更不用说一小部分内存(例如单个浮点变量)了,因此存储的许多数字只是...的近似值。它们代表的数字。
由于仅存在有限数量的值而不是近似值,并且在近似值和另一个数之间的任何运算都会导致近似值,因此舍入误差几乎是不可避免的。
重要的是要意识到它们何时可能造成问题,并采取措施减轻风险。
除了大卫·戈德堡的必要什么每台计算机科学家应该知道关于浮点运算(由Sun / Oracle的重新出版的附录中的数值计算指南),这是由提到的Thorsten,在ACCU杂志超负荷运行了一个极好的理查德•哈里斯(Richard Harris)关于浮点布鲁斯的系列文章。
该系列开始于
数值计算有很多陷阱。理查德·哈里斯(Richard Harris)开始寻找银弹。
经常会从沉睡中唤醒数字错误的巨龙,但是如果不慎接近,他有时会对粗心的程序员的计算造成灾难性的破坏。
如此之多,以至于有些程序员在IEEE 754浮点运算法则的丛林中碰到了他,建议他们的同伴不要在那片美丽的土地上旅行。
在本系列文章中,我们将探索数值计算的世界,将浮点算术与已提出的一些更安全的替代技术进行对比。我们将了解到,龙的领土确实确实遥不可及,而且如果我们害怕龙的毁灭性关注,总的来说我们必须谨慎行事。
理查德首先解释了有理数,有理数,无理数,代数数和先验数的实数分类法。然后,他继续解释IEEE754表示形式,然后再讨论取消错误和执行顺序问题。
如果您阅读的内容不深,那么您将对与浮点数有关的问题有很好的了解。
如果您想了解更多,他继续
然后,他转向尝试帮助您治愈微积分蓝调
最后但并非最不重要的是
整个系列文章都很值得研究,总共66页,仍然比Goldberg论文的77页还小。
尽管本系列文章涵盖了许多相同的方面,但我发现它比Goldberg的论文更容易理解。在阅读了Richards的较早文章之后,我还发现更容易理解本文的更复杂部分,而在那些较早的文章之后,Richard进入了Goldberg论文未涉及的许多有趣领域。
作为这样说话AK在评论中提到:
由于这些文章的作者,我想提一提,我已经创造了他们的互动版本在我的博客www.thusspakeak.com开始thusspakeak.com/ak/2013/06。
任何形式的表示形式都会对一些数字产生舍入误差。尝试用IEEE浮点数或小数点表示1/3。两者都无法准确地做到这一点。这已经超出了回答您的问题的范围,但是我已经成功使用了以下经验法则:
到目前为止似乎尚未提及的是不稳定算法和病态问题的概念。我将首先讨论前者,因为对于新手数字学家来说,这似乎是一个更常见的陷阱。
考虑计算(互惠)黄金分割率的幂φ=0.61803…
;一种可行的解决方法是使用φ^n=φ^(n-2)-φ^(n-1)
以φ^0=1
和开头的递归公式φ^1=φ
。如果您在自己喜欢的计算环境中运行此递归并将结果与经过精确评估的能力进行比较,则会发现重要数字的缓慢消耗。这是在Mathematica中发生的情况:
ph = N[1/GoldenRatio];
Nest[Append[#1, #1[[-2]] - #1[[-1]]] & , {1, ph}, 50] - ph^Range[0, 51]
{0., 0., 1.1102230246251565*^-16, -5.551115123125783*^-17, 2.220446049250313*^-16,
-2.3592239273284576*^-16, 4.85722573273506*^-16, -7.147060721024445*^-16,
1.2073675392798577*^-15, -1.916869440954372*^-15, 3.1259717037102064*^-15,
-5.0411064211886014*^-15, 8.16837916750579*^-15, -1.3209051907825398*^-14,
2.1377864756200182*^-14, -3.458669982359108*^-14, 5.596472721011714*^-14,
-9.055131861349097*^-14, 1.465160458236081*^-13, -2.370673237795176*^-13,
3.835834102607072*^-13, -6.206507137114341*^-13, 1.004234127360273*^-12,
-1.6248848342954435*^-12, 2.6291189633497825*^-12, -4.254003796798193*^-12,
6.883122762265558*^-12, -1.1137126558640235*^-11, 1.8020249321541067*^-11,
-2.9157375879969544*^-11, 4.717762520172237*^-11, -7.633500108148015*^-11,
1.23512626283229*^-10, -1.9984762736468268*^-10, 3.233602536479646*^-10,
-5.232078810126407*^-10, 8.465681346606119*^-10, -1.3697760156732426*^-9,
2.216344150333856*^-9, -3.5861201660070964*^-9, 5.802464316340953*^-9,
-9.388584482348049*^-9, 1.5191048798689004*^-8, -2.457963328103705*^-8,
3.9770682079726053*^-8, -6.43503153607631*^-8, 1.0412099744048916*^-7,
-1.6847131280125227*^-7, 2.725923102417414*^-7, -4.4106362304299367*^-7,
7.136559332847351*^-7, -1.1547195563277288*^-6}
的所谓结果φ^41
有错误的符号,甚至更早的时候,的计算值和实际值φ^39
也没有共同的数字(3.484899258054952
* ^-9 for the computed version against the true value
7.071019424062048 *^-9
)。因此,该算法不稳定,因此不应在不精确的算法中使用此递归公式。这是由于递归公式的固有性质:此递归有一个“衰减”和“增长”的解决方案,并且当有另一个“增长”解决方案乞求时,尝试通过正向解决方案来计算“衰减”解决方案。对于数字的悲伤。因此,应该确保他/她的数值算法是稳定的。
现在,讨论病态问题的概念:即使可能存在一种稳定的数值方法,也很可能是您的问题无法用算法解决。这是问题本身的问题,而不是解决方法。数值的典型示例是涉及所谓的“希尔伯特矩阵”的线性方程的解:
矩阵是病态矩阵的典范示例:尝试求解具有大希尔伯特矩阵的系统可能会返回不准确的解决方案。
这是Mathematica的演示:比较精确算术的结果
Table[LinearSolve[HilbertMatrix[n], HilbertMatrix[n].ConstantArray[1, n]], {n, 2, 12}]
{{1, 1}, {1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1,
1}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1,
1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}
和不精确的算术
Table[LinearSolve[N[HilbertMatrix[n]], N[HilbertMatrix[n].ConstantArray[1, n]]], {n, 2, 12}]
{{1., 1.}, {1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1., 1.},
{1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1.},
{1., 1., 1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1., 1., 1.},
{1., 1., 1., 0.99997, 1.00014, 0.999618, 1.00062, 0.9994, 1.00031,
0.999931}, {1., 1., 0.999995, 1.00006, 0.999658, 1.00122, 0.997327,
1.00367, 0.996932, 1.00143, 0.999717}, {1., 1., 0.999986, 1.00022,
0.998241, 1.00831, 0.975462, 1.0466, 0.94311, 1.04312, 0.981529,
1.00342}}
(如果您确实在Mathematica中进行了尝试,则会注意到一些错误消息,警告您出现不良情况。)
在这两种情况下,仅仅提高精度是无法治愈的。这只会延迟不可避免的数字侵蚀。
这就是您可能会面临的问题。解决方案可能很困难:首先,要么回到绘图板上,要么遍历期刊/书籍/其他内容,以查找是否有人提出了比您更好的解决方案;第二,您要么放弃,要么将问题重新表述为更容易解决的问题。
我给你留下戴安娜·奥利里的名言:
生活可能会给我们带来一些疾病,但没有充分的理由来解决不稳定的算法。
因为不能以2为底数来表示以10为底的十进制数
或换句话说,不能将1/10的分母转换为分数为2的分数(这实际上是浮点数)
9*3.3333333
以十进制形式进行运算,并将其比较9*3 1/3
.1 + .1 != .2
因为使用浮点二进制编码,而不是十进制。
1.0/3.0*3.0 != 1.0
,因为使用浮点二进制编码,而不是三进制。
在数学中,有无限多个有理数。一个32位变量只能有2 32个不同的值,而一个64位变量只能有2 64个值。因此,有许多没有精确表示的有理数。
我们可以提出一些方案,使我们可以完美地表示1/3或1/100。事实证明,对于许多实际目的来说,这不是很有用。有一个大例外:在金融业中,十进制小数通常会弹出。这主要是因为金融本质上是一种人类活动,而不是物质活动。
因此,我们通常选择使用二进制浮点,并舍入无法用二进制表示的任何值。但是在金融领域,有时我们选择十进制浮点数,然后将值四舍五入到最接近的十进制值。
"√2"
。(我的旧HP-48计算器能够做到这一点,并且对该值进行平方运算就可以得出精确的结果2.0
。)对于任何有限表示形式,可表示的实数只有一个可数的无穷大,但是没有计算出的数字不会,原则上可以代表。在实践中,二进制浮点极大地限制了可表示数字的集合,具有相对于符号表示而言极快的速度和较小的存储量的优点。
我想到的唯一真正明显的带有浮点数的“舍入问题”是移动平均滤波器:
$$ \ begin {align} y [n]&= \ frac {1} {N} \ sum \ limits_ {i = 0} ^ {N-1} x [ni] \&= y [n-1] + \ frac {1} {N}(x [n]-x [nN])\ \ end {align} $$
为了使这项工作不产生噪音,您需要确保在当前样本中添加的$ x [n] $与将在样本中减去$ N $个样本的$ x [nN] $完全相同。未来。如果不是这样,那么不同之处在于会拖延您的延迟线,并且永远不会出来。这是因为此移动平均滤波器实际上是由IIR构成的,该IIR在$ z = 1 $处具有一个边际稳定的极点,而在其中的零则将其抵消。但是,它是一个积分器,所有被积分但未被完全删除的废话将永远存在于积分器总和中。这是定点与浮点数没有相同问题的地方。