【问题标题】:C bug of 0.00 and -0.000.00 和 -0.00 的 C 错误
【发布时间】:2020-02-22 22:14:48
【问题描述】:

求矩阵秩的C程序。

我使用高斯消元法来确定矩阵的秩。 This is the method which I have used.

我在 c 中的代码:

#include<stdio.h>
#include<stdlib.h>

#define min(x,y) x>y?y:x

float** create_matrix(int,int);
void input(float**,int,int);
void display(float**,int,int);
void matrix_op(float**,int,int,int,float);
void row_trans(float**,int,int);
void row_swap(float**,int,int,int);
int rank(float**,int,int);

int main()
{
    int row,col;
    printf("Enter number of rows and columns: ");
    scanf("%d %d",&row,&col);
    float** matrix=create_matrix(row,col);
    input(matrix,row,col);
    printf("The rank of the matrix is: %d\n",rank(matrix,row,col));
    return 0;
}

float** create_matrix(int r,int c)
{
    int i;
    float** matrix=(float**)calloc(r,sizeof(float*));
    for(i=0;i<r;i++)
        *(matrix+i)=(float*)calloc(c,sizeof(float));
    return matrix;
}

void input(float** matrix,int  r,int c)
{
    int i,j;
    for(i=0;i<r;i++)
    {
        for(j=0;j<c;j++)
            scanf("%f",&matrix[i][j]);
    }
}

void display(float** matrix,int  r,int c)
{
    int i,j;
    for(i=0;i<r;i++)
    {
        for(j=0;j<c;j++)
            printf("%.2f ",matrix[i][j]);
        printf("\n");
    }
}

void matrix_op(float** matrix,int c,int j,int i,float scalar)
{
    int k;
    for(k=0;k<c;k++)
    {
        matrix[j][k]+=matrix[i][k]*scalar;
    }
}

void row_trans(float** matrix,int r,int i)
{
    if(matrix[i][i]!=0)
    {
        for(int j=i+1;j<r;j++)
        {
            if(j==i)
                continue;
            if(matrix[j][i]!=0)
                matrix_op(matrix,r,j,i,(-1.00)*(matrix[j][i]/matrix[i][i]));
        }
    }
    else 
    {
        int j=i+1;
        while(j<r)
        {
            if(matrix[j][i]==0)
                j+=1;
            else 
                break;
        }
        if(j!=r)
        {
            row_swap(matrix,r,i,j);
            row_trans(matrix,r,i);
        }
    }
}

void row_swap(float** matrix,int c,int i,int j)
{
    for(int k=0;k<c;k++)
    {
        float temp=matrix[i][k];
        matrix[i][k]=matrix[j][k];
        matrix[j][k]=temp;
    }
}

int rank(float** matrix,int r,int c)
{
    int i;
    int l=min(r,c);
    for(i=0;i<l;i++)
    {
        row_trans(matrix,r,i);
        display(matrix,r,c);
        printf("\n");
    }
    i=r-1;
    while(i>=0)
    {
        if(matrix[i][l-1]==0)
            i-=1;
        else
            break;
    }
    return i+1;
}

当我输入这个时:

Enter number of rows and columns: 5 5
3 -1 -2 3 -1
4 1 2 5 4
7 10 10 2 -3
2 -3 -6 1 -6
3 9 8 -3 -7

我明白了:

3.00 -1.00 -2.00 3.00 -1.00 
0.00 2.33 4.67 1.00 5.33 
0.00 12.33 14.67 -5.00 -0.67 
0.00 -2.33 -4.67 -1.00 -5.33 
0.00 10.00 10.00 -6.00 -6.00 

3.00 -1.00 -2.00 3.00 -1.00 
0.00 2.33 4.67 1.00 5.33 
0.00 0.00 -10.00 -10.29 -28.86 
0.00 0.00 0.00 -0.00 -0.00 
0.00 0.00 -10.00 -10.29 -28.86 

3.00 -1.00 -2.00 3.00 -1.00 
0.00 2.33 4.67 1.00 5.33 
0.00 0.00 -10.00 -10.29 -28.86 
0.00 0.00 0.00 -0.00 -0.00 
0.00 0.00 0.00 -0.00 -0.00 

3.00 -1.00 -2.00 3.00 -1.00 
0.00 2.33 4.67 1.00 5.33 
0.00 0.00 -10.00 -10.29 -28.86 
0.00 0.00 0.00 -0.00 -0.00 
0.00 0.00 0.00 0.00 0.00 

3.00 -1.00 -2.00 3.00 -1.00 
0.00 2.33 4.67 1.00 5.33 
0.00 0.00 -10.00 -10.29 -28.86 
0.00 0.00 0.00 -0.00 -0.00 
0.00 0.00 0.00 0.00 0.00 

The rank of the matrix is: 4

请注意,-0.00 和 0.00 被认为是不同的。 这就是为什么我的排名是 4,而排名应该是 3。

编辑1:在最终显示中,我们可以看到,在最后一列中,倒数第二行是-0.00,最后一行是0.00。在函数rank() 中,i 遍历最后一列的零点,然后它将返回rows- #(zeroes)。但它没有考虑-0.00=0.00。

编辑 2:使用 double 后,我遇到了同样的问题。调试后我得到了这个,

115     while(i>=0)
(gdb) p i
$4 = 3
(gdb) n
117         if(matrix[i][l-1]==0)
(gdb) p matrix[i][l-1]
$5 = 8.8817841970012523e-16
(gdb) p matrix[i][l-1]==0
$6 = 0

编辑 3: 引入精度后问题就解决了。

double set_prec(double n) 
{ 
    return floor(pow(10,3)*n)/pow(10,3); 
}

任何帮助将不胜感激。 感谢期待。

【问题讨论】:

  • 注意 -0.00 和 0.00 被认为是不同的。你能澄清一下吗?
  • @DavidRanieri 五行中有两行是 0.00 或 -0.00,因此排名应该是 3,但给出的排名是 4,这意味着其中一行(可能是 -0.00 的行)被计为非零行。
  • 这可能吗?据我所知(0.00f == -0.00f) 是true
  • @DavidRanieri 是的,我刚刚检查了我的机器,它们也进行了相同的比较。
  • 浮点数可能是-0,因为常见的浮点编码是符号幅度。但也有可能这些值实际上并不为零,只是接近于零。例如,如果x = -0.0001 并且您使用%.2f 打印x,则输出将为-0.00

标签: c floating-accuracy


【解决方案1】:

与其比较 0,不如比较 epsilon

    double epsilon = 1E-12; // tweak as you deem fit
    //if (a != 0)
    if (fabs(a) > epsilon)
    //if (a == 0)
    if (fabs(a) < epsilon)
    //if (a == b)
    if (fabs(a - b) < epsilon)

【讨论】:

    【解决方案2】:

    当您在 C 中使用 %.2f 打印数字时,C 实现会将其四舍五入到小数点后两位。 0.003… 或 -0.0001… 等数字将打印为 0.00 或 -0.00。因此,数字打印为0.00 的事实并不意味着它是 0。从您报告的行为来看,很明显您在程序中拥有的数字不是 0 或 -0。 (IEEE 754 算术的 -0 在数学上为 0,但保留符号作为程序员可能使用的信息。它比较等于 +0。)

    浮点运算仅近似于实数运算。当您以普通方式使用浮点运算时,任何计算结果 x 只是您使用实数运算得到的结果 x 的近似值。由于您只计算了x 而不是x,因此您没有关于x 是什么的完整信息,因此不可能,无需专门的分析和设计,就可以准确地知道x是什么。因此,如果某个结果x 接近于 0,则无法知道 x 是否为 0。

    一般来说,浮点结果 x 和 raal 数结果之间的差异 x 的范围可以从 0 到无穷大,甚至可以是“非数字”。在使用小整数输入的小矩阵进行高斯消元的特定情况下,可以证明足够接近 0 的浮点结果对应于使用实数算术计算时为 0 的结果。在这种情况下,将接近 0 的结果视为 0 可以产生正确的结果。

    这通常不是正确的解决方案,一般不建议将浮点结果舍入为 0 或与容差进行比较。它仅在特定有限的情况下有用。 p>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多