我想使用Java中的Math.ulp(double)方法计算一系列加法,乘法和除法的浮点舍入误差。根据最后一个单元(ULP)上的维基页面,似乎一个浮点计算的误差,比如2 + 3或2 * 3,将是0.5 * ulp(2 + 3)或0.5 * ulp( 2 * 3),其中2 * 3和2 + 3是浮点计算。但是,添加这些错误并不能解释我在最终产品中遇到的实际错误。说最大误差,例如,2 + 3 * 4 = 0.5 * ulp(2+ [3 * 4])+ 0.5 * ulp(3 * 4)似乎并不能解释我得到的实际误差。因此,我很困惑,也许我误解了Math.ulp(双)或者我可能需要使用某种相对错误。我不知道。任何人都可以向我解释这个问题,并且可能会给出一些浮点数和精确数的加法,乘法和除法的例子吗?非常感谢。
我正在尝试计算Matrix类的矩阵的减少行梯形形式,我需要知道,经过几次计算后,我用于计算的二维数组中的某些项是否相等如果一行全为零,我退出代码。如果它中有一个非零数字,我将该数字除以它然后执行高斯消元。问题在于,在执行一系列操作之后,浮点错误可能会进入并且计算会导致零结束为非零数字,然后会混淆我的矩阵计算。因此,我试图改变高斯消除从零到小于计算误差界限的条件,并且我正在计算矩阵中每个项目的误差界限,这是基于对该项目所做的计算,在一个中加在一起新的错误数组。 这是我的代码:
/**
* Finds the reduced row echelon form of the matrix using partial pivoting
* @return rref: The reduced row echelon form of the matrix
*/
public Matrix rref()
{
//ref()
Matrix ref = copy();
int iPivot = 0, jPivot = 0, greatestPivotRow;
double[][] errorArray = new double[height][width];
while(iPivot < height && jPivot < width)
{
do
{
//Finds row with greatest absolute-value-of-a-number at the horizontal value of the pivot position
greatestPivotRow = iPivot;
for(int n = iPivot; n < height; n++)
{
if(Math.abs(ref.getVal(n, jPivot)) > Math.abs(ref.getVal(greatestPivotRow, jPivot)))
greatestPivotRow = n;
}
//Swaps row at pivot with that row if that number is not 0 (Or less than the floating-point error)
//If the largest number is 0, all numbers below in the column are 0, so jPivot increments and row swapper is repeated
if(Math.abs(ref.getVal(greatestPivotRow, jPivot)) > errorArray[greatestPivotRow][jPivot])
ref = ref.swapRows(iPivot, greatestPivotRow);
else
jPivot++;
}
while(jPivot < width && Math.abs(ref.getVal(greatestPivotRow, jPivot)) <= errorArray[greatestPivotRow][jPivot]);
if(jPivot < width)
{
//Pivot value becomes 1
double rowMultiplier1 = 1/ref.getVal(iPivot,jPivot);
for(int j = jPivot; j < width; j++)
{
ref.matrixArray[iPivot][j] = ref.getVal(iPivot,j) * rowMultiplier1;
errorArray[iPivot][j] += 0.5 * (Math.ulp(ref.matrixArray[iPivot][j]) + Math.ulp(rowMultiplier1));
}
//1st value in nth row becomes 0
for(int iTarget = iPivot + 1; iTarget < height; iTarget++)
{
double rowMultiplier0 = -ref.getVal(iTarget, jPivot)/ref.getVal(iPivot, jPivot);
for(int j = jPivot; j < width; j++)
{
errorArray[iTarget][j] += 0.5 * (Math.ulp(ref.getVal(iPivot, j) * rowMultiplier0) + Math.ulp(ref.getVal(iTarget, j)
+ ref.getVal(iPivot, j)*rowMultiplier0) + Math.ulp(rowMultiplier0));
ref.matrixArray[iTarget][j] = ref.getVal(iTarget, j)
+ ref.getVal(iPivot, j)*rowMultiplier0;
}
}
}
//Shifts pivot down 1 and to the right 1
iPivot++;
jPivot++;
}
//rref
Matrix rref = ref.copy();
iPivot = 1;
jPivot = 1;
//Moves pivot along the diagonal
while(iPivot < height && jPivot < width)
{
//Moves horizontal position of pivot to first nonzero number in the row (the 1)
int m = jPivot;
while(m < width && Math.abs(rref.getVal(iPivot, m)) < errorArray[iPivot][m])
m++;
if(m != width)
{
jPivot = m;
//1st value in rows above pivot become 0
for(int iTarget = 0; iTarget < iPivot; iTarget++)
{
double rowMultiplier = -rref.getVal(iTarget, jPivot)/rref.getVal(iPivot, jPivot);
for(int j = jPivot; j < width; j++)
{
errorArray[iTarget][j] += 0.5 * (Math.ulp(rref.getVal(iTarget, j) * rowMultiplier) + Math.ulp(rref.getVal(iTarget, j)
+ rref.getVal(iPivot, j)*rowMultiplier) + Math.ulp(rowMultiplier));
rref.matrixArray[iTarget][j] = rref.getVal(iTarget, j)
+ rref.getVal(iPivot, j)*rowMultiplier;
}
}
}
iPivot++;
jPivot++;
}
//Get rid of floating-point errors in integers
for(int i = 0; i < height; i++)
{
for(int j =0; j < width; j++)
{
if(Math.abs(rref.getVal(i, j) - (int)(rref.getVal(i, j) + 0.5)) <= errorArray[i][j])
rref.matrixArray[i][j] = (int)(rref.getVal(i, j) + 0.5);
}
}
return rref;
}
代码的最后一部分,将小于计算误差的浮点数从整数值转换为该整数值,主要是告诉我我的错误公式是否有效,因为我计算的一些矩阵结束了up,而不是整数,5.000000000000004s等。因此,我知道如果我的数字非常接近整数而不是整数,我也知道我的错误界限不够大,显然它们不是,所以我认为我做错了。
我的输入矩阵是一个带有实例变量
的矩阵double[][] matrixArray = {{1,-2,0,0,3}, {2,-5,-3,-2,6}, {0,5,15,10,0}, {2,6,18,8,6}};
我的结果是数组
[[1.0, 0.0, 0.0, -2.0000000000000013, 3.0], [0.0, 1.0, 0.0, -1.0000000000000004, 0.0], [0.0, 0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]
虽然我的错误计算修复了零被转换为1然后用于高斯消除的问题,但我仍然有不是整数的数字,所以我知道我的错误界限是不准确的。它可能在这种情况下有效,但如果没有正确的错误界限,可能不会在下一个。
答案 0 :(得分:0)
2 + 3 * 4 = 0.5 * ulp(2+ [3 * 4])+ 0.5 * ulp(3 * 4)
错误复合。就像兴趣一样,最终的错误会呈指数级增长。你的例子中的操作是准确的,所以很难看出你在抱怨什么(当然你确实得到了14个?)。您是否考虑了表示错误,导致计算中涉及的常数不是数学值而是0.5ULP近似值?
除了在以必要的精度静态计算时出现错误的指数增长,还有一个问题是您使用不准确的浮点数学来计算错误:
errorArray[iTarget][j] += 0.5 * (Math.ulp(rref.getVal(iTarget, j) * rowMultiplier) + Math.ulp(rref.getVal(iTarget, j)
实际误差可以超过这个语句计算,因为没有什么能阻止浮点加法成为数学结果的较低近似值(乘法恰好可能是精确的,因为其中一个被乘数是2的幂)每个案例)。
在另一种编程语言中,您可以将舍入模式更改为“向上”以进行此计算,但Java不提供对此功能的访问。
以下是一系列切线相关的评论:
当数学上预期的结果是整数时,获得整数的双精度的通常方法是确保整个计算的1ULP错误。除非你采取特殊措施来确保这种绑定(例如Dekker multiplication),否则你几乎永远不会得到一个涉及多个操作的计算的1ULP绑定。
Java可以在hexadecimal format中使用常量和打印结果,如果你想看到确切的结果,你应该使用它。
如果您有兴趣获得特定计算的最终误差的上限,而不是静态地计算所有计算,那么interval arithmetic比将误差表征为单个绝对值稍微准确一些,并且需要更少的思考。在通过其他方式知道结果必须是整数的上下文中,如果结果区间只包含一个整数,那么您肯定知道这是唯一可能的答案。
答案 1 :(得分:0)
如果您有兴趣计算高斯消除过程的误差范围,那么这是一个非常复杂的问题。例如,本文给出了误差上限的公式: Higham NJ,Higham DJ。高斯消除旋转中的大生长因子。 SIAM期刊矩阵分析与应用。 。1989; 10(2):155
这绝不简单!
另一方面,如果您的目标是防止爬行浮点错误破坏您的零,我认为您甚至不需要创建 errorArray [] [] 。您可以通过计算浮点数然后在Math.ulp()或机器epsilon的帮助下设置精度条件来做得很好。通过这种方式,您最终不需要最终循环来“摆脱”那些讨厌的零!
你也可以使用java的BigDecimal
,看看你是否得到了更好的结果。也许this question及其给出的答案可以提供帮助。