C ошибка 0,00 и -0,00 - PullRequest
       22

C ошибка 0,00 и -0,00

2 голосов
/ 27 октября 2019
Программа

C для определения ранга матрицы.

Я использовал метод исключения Гаусса для определения ранга матрицы. Это метод, который я использовал.

Мой код в 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

Iполучите это:

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); 
}

Любая помощь будет оценена. Спасибо в ожидании.

Ответы [ 2 ]

3 голосов
/ 27 октября 2019

Когда вы печатаете число в 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-number x может варьироваться от 0 до бесконечности или даже может быть «не числом». В частном случае выполнения исключения Гаусса с небольшими матрицами из небольших целочисленных входных данных может оказаться возможным доказать, чтоРезультаты с плавающей точкой, достаточно близкие к 0, соответствуют результатам, которые были бы равны 0, если их вычислить с помощью арифметики действительных чисел. В этом случае обработка результатов, близких к 0, как если бы они были равны 0, может дать правильные результаты.

В целом это неверное решение, а округление результатов с плавающей запятой до 0 или сравнение с допускомне следует рекомендовать в целом. Это полезно только в особых случаях.

1 голос
/ 27 октября 2019

Вместо сравнения с 0, сравните с эпсилон

    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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...