Pergunta

Eu estou escrevendo um programa onde eu preciso excluir pontos duplicados armazenados em uma matriz. O problema é que quando se trata de verificar se esses pontos estão na matriz, MATLAB não pode reconhecê-los na matriz embora eles existem.

No código a seguir, a função intersections recebe os pontos de interseção:

[points(:,1), points(:,2)] = intersections(...
    obj.modifiedVGVertices(1,:), obj.modifiedVGVertices(2,:), ...
    [vertex1(1) vertex2(1)], [vertex1(2) vertex2(2)]);

O resultado:

>> points
points =
   12.0000   15.0000
   33.0000   24.0000
   33.0000   24.0000

>> vertex1
vertex1 =
    12
    15

>> vertex2    
vertex2 =
    33
    24

pontos Dois (vertex1 e vertex2) devem ser eliminados do resultado. Deve ser feito pela abaixo comandos:

points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :);
points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :);

Depois de fazer isso, temos este resultado inesperado:

>> points
points =
   33.0000   24.0000

O resultado deve ser uma matriz vazia. Como você pode ver, a primeira (ou segunda?) Par de [33.0000 24.0000] foi eliminada, mas não o segundo.

Então eu verifiquei essas duas expressões:

>> points(1) ~= vertex2(1)
ans =
     0
>> points(2) ~= vertex2(2)
ans =
     1   % <-- It means 24.0000 is not equal to 24.0000?

Qual é o problema?


O mais surpreendente, eu fiz um novo script que tem apenas estes comandos:

points = [12.0000   15.0000
          33.0000   24.0000
          33.0000   24.0000];

vertex1 = [12 ;  15];
vertex2 = [33 ;  24];

points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :);
points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :);

O resultado esperado:

>> points
points =  
   Empty matrix: 0-by-2
Foi útil?

Solução

O problema que você está tendo diz respeito à forma como de ponto flutuante números são representados em um computador. Uma discussão mais detalhada de representações de ponto flutuante aparece no final da minha resposta (A seção "representação de ponto flutuante"). O TL; DR Versão: porque os computadores têm quantidades finitas de memória, números só podem ser representados com precisão finita. Deste modo, a precisão dos números de ponto flutuante é limitado a um certo número de casas decimais (cerca de 16 dígitos significativos para valores de precisão dupla , o padrão usado em MATLAB).

vs. real exibido precisão

Agora, para resolver o exemplo específico na questão ... enquanto 24.0000 e 24.0000 são apresentada da mesma forma, verifica-se que eles realmente diferem por quantidades muito pequenas decimais neste caso. Você não vê-lo porque MATLAB só exibe 4 dígitos significativos por padrão , mantendo a exibição geral limpo e arrumado. Se você quiser ver a precisão total, você deverá emitir o comando format long ou ver uma hexadecimal representação do número:

>> pi
ans =
    3.1416
>> format long
>> pi
ans =
   3.141592653589793
>> num2hex(pi)
ans =
400921fb54442d18

valores inicializado vs. valores computados

Uma vez que há apenas um número finito de valores que podem ser representados por um número de ponto flutuante, é possível para um cálculo resultar em um valor que cai entre duas dessas representações. Em tal caso, um, o resultado tem de ser arredondado para um deles. Isto introduz uma pequena erro da máquina de precisão . Isto também significa que inicializar um valor directamente ou por alguma computação pode dar resultados ligeiramente diferentes. Por exemplo, o valor 0.1 não tem um exata representação de ponto flutuante (ou seja, ele fica ligeiramente arredondados), e assim você acaba com resultados contra-intuitivo como este devido à forma redonda off erros se acumulam:

>> a=sum([0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]);  % Sum 10 0.1s
>> b=1;                                               % Initialize to 1
>> a == b
ans =
  logical
   0                % They are unequal!
>> num2hex(a)       % Let's check their hex representation to confirm
ans =
3fefffffffffffff
>> num2hex(b)
ans =
3ff0000000000000

Como lidar corretamente com comparações de ponto flutuante

Uma vez que valores de ponto flutuante podem diferir por quantidades muito pequenas, qualquer comparação deve ser feito verificando se os valores estão dentro de algum intervalo (tolerância ou seja) um do outro, ao invés de exatamente igual ao outro. Por exemplo:

a = 24;
b = 24.000001;
tolerance = 0.001;
if abs(a-b) < tolerance, disp('Equal!'); end

exibirá "Igual!".

Você pode então alterar seu código para algo como:

points = points((abs(points(:,1)-vertex1(1)) > tolerance) | ...
                (abs(points(:,2)-vertex1(2)) > tolerance),:)

representação de ponto flutuante

Uma visão bem de números de ponto flutuante (e especificamente a IEEE 754 padrão para ponto flutuante aritmética ) é O que cada cientista computador deve saber sobre Floating -ponto Aritmética por David Goldberg.

Um binário número de ponto flutuante é realmente representado por três números inteiros: um bit de sinal s, significando um (ou coeficiente / fracção) b, e um e expoente. Para precisão dupla formato de ponto flutuante , cada número é representado por 64 bits definidos na memória da seguinte forma:

 enter descrição da imagem aqui

O valor real pode ser encontrado com a seguinte fórmula:

enter descrição da imagem aqui

Este formato permite representações de números da gama 10 ^ -308 a 10 ^ 308. Para MATLAB você pode obter esses limites de realmin e realmax :

>> realmin
ans =
    2.225073858507201e-308
>> realmax
ans =
    1.797693134862316e+308

Uma vez que existem um número finito de bits utilizados para representar um número de ponto flutuante, só há tantos números finitos que podem ser representados dentro da gama acima dada. Cálculos, muitas vezes, resultar em um valor que não corresponde exatamente uma dessas representações finitas, então os valores devem ser arredondados. Estes erros máquina de precisão tornar-se evidente em formas diferentes, como discutido nos exemplos acima.

A fim de entender melhor estes round-off erros, é útil olhar para a precisão de ponto flutuante relativa proporcionada pela função eps , que quantifica a distância a partir de um dado número para o próximo maior representação de ponto flutuante:

>> eps(1)
ans =
     2.220446049250313e-16
>> eps(1000)
ans =
     1.136868377216160e-13

Observe que a precisão é relação para o tamanho de um dado número ser representado; números maiores terão distâncias maiores entre representações de ponto flutuante, e terá assim menos dígitos de precisão após o ponto decimal. Isso pode ser uma consideração importante com alguns cálculos. Considere o seguinte exemplo:

>> format long              % Display full precision
>> x = rand(1, 10);         % Get 10 random values between 0 and 1
>> a = mean(x)              % Take the mean
a =
   0.587307428244141
>> b = mean(x+10000)-10000  % Take the mean at a different scale, then shift back
b =
   0.587307428244458

Note que quando mudamos os valores de x do [0 1] intervalo para o [10000 10001] gama, calcular uma média, em seguida, subtrair a média de deslocamento para comparação, obtemos um valor que difere para os últimos 3 dígitos significativos. Isso ilustra como um deslocamento ou escala de dados pode alterar a precisão dos cálculos realizados nele, que é algo que tem de ser contabilizado com certos problemas.

Outras dicas

Olhe para este artigo: Os perigos de ponto flutuante . Embora seus exemplos estão em Fortran tem sentido para praticamente qualquer linguagem de programação moderna, incluindo MATLAB. Seu problema (e solução para ele) é descrito em "Comparações de segurança".

tipo

format long g

Este comando irá mostrar o valor total do número. É provável que seja algo como 24,00000021321! = 24,00000123124

Tente escrever

0,1 + 0,1 + 0,1 == 0,3.

Atenção: Você pode se surpreender com o resultado

Talvez os dois números são realmente 24,0 e 24,000000001 mas você não está vendo todas as casas decimais.

Confira o Matlab EPS função .

Matlab usa matemática ponto flutuante até 16 dígitos de precisão (somente 5 são exibidos).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top