【发布时间】:2016-03-31 18:10:57
【问题描述】:
我想使用 Java 中的 Math.ulp(double) 方法计算一系列加法、乘法和除法的浮点舍入误差。根据最后位置单元 (ULP) 上的 wiki 页面,似乎来自一个浮点计算的错误,比如 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(double) 或者我需要使用某种相对错误。我不知道。谁能向我解释一下,也许可以举几个浮点数和精确数的加法、乘法和除法的例子?将不胜感激。
我正在尝试为 Matrix 类计算矩阵的简化行梯形形式,并且我需要知道,经过几次计算后,我用于计算的二维数组中的某些项目是否相等到 0。如果一行全为零,我退出代码。如果其中有一个非零数,我将这个数除以它自己,然后执行高斯消元。问题是,在执行一系列操作后,浮点错误可能会蔓延,并且应该导致零的计算最终会变成一个非零数,这会扰乱我的矩阵计算。因此,我试图将发生高斯消除的条件从零更改为小于计算的误差范围,并且我正在根据对矩阵中每个项目所做的计算计算矩阵中每个项目的误差范围,并将其加在一起新的错误数组。 这是我的代码:
/**
* 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;
}
代码的最后一部分,将小于计算误差的浮点数从整数值转换为该整数值,主要是为了告诉我我的错误公式是否有效,因为我正在计算的一些矩阵结束向上,而不是整数,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]]
虽然我的错误计算解决了将零变成一然后用于高斯消元的问题,但我仍然有不是整数的数字,所以我知道我的错误界限不准确。在这种情况下它可能有效,但如果没有正确的错误界限,它可能不会在下一个。
【问题讨论】:
-
您是否尝试计算 exact 错误,而不仅仅是错误的界限?就 ulp 或任何东西而言,不太可能有一个简单的公式。 (在任何情况下,结果
-
是的,我正在尝试计算误差范围。
-
那么您使用的公式有什么问题?这些示例中的实际误差将小于您计算的范围。
-
确定浮点运算序列的(严格)错误界限是一个非常重要的过程,并且已经编写了整本书来解决这个问题的各个方面,来自 JH Wilkinson 的“舍入错误”在代数过程中”(1965 年)到 Nicholas J. Higham 的“数值算法第二版的准确性和稳定性”。 (2002 年)。因此,我觉得这个问题太宽泛了,而且只与编程无关,但暂时不要进行近距离投票(也许有人可以将答案压缩成典型的 SO 答案格式)。
-
@abeta201 为了使您的问题更具体,您可能希望显示完整的代码,列出每个输入的可能范围,并说明您的预期和观察到的界限。
标签: java floating-point linear-algebra floating-accuracy