2010-10-07 15 views
9
#include <stdio.h> 
#include <stdlib.h> 

float values[] = { 4, 1, 10, 9, 2, 5, -1, -9, -2,10000,-0.05,-3,-1.1 }; 

int compare (const void * a, const void * b) 
{ 
    return ((int) (*(float*)a - *(float*)b)); 
} 

int main() 
{ 

    int i; 

    qsort (values, 13, sizeof(float), compare); 

    for (i = 0; i < 13; i++) 
    { 
     printf ("%f ",values[ i ]); 
    } 
    putchar('\n'); 

    return 0; 
} 

Wynikiem jest:Problem próbuje użyć funkcji C qsort

-9,000000 -3,000000 -2,000000 -1,000000 -1,100000 -0,050000 1,000000 2,000000 4,000000 5,000000 9,000000 10,000000 10000,000000

To źle ponieważ kolejność -1 i -1.1 zostało zmienione. Wierzę, że to się dzieje, ponieważ moja funkcja "porównaj".

Jak mogę rozwiązać ten problem?

Dzięki

+2

_qsort_ działa dobrze. Twój telefon do qsort_ jest zepsuty. – aaronasterling

Odpowiedz

2

przez zaokrąglenie różnicy do całkowitej utraty precyzji.

EDIT:

Zmienić funkcję porównania do

return (*(float*)a >= *(float*)b) ? 1 : -1;

Edit dla AndreyT: Nie sądzę, że powrót tylko 1 lub -1 spowoduje nieskończoną pętlę lub nieprawidłową kolejność (ja po prostu wymień równe wartości, które tego nie wymagają).

uwzględniając wyraźne argumenty za powrotem 0 będzie kosztować dodatkowe compatation pływaka, a są one rzadko równa. Zatem porównanie dla równości można pominąć, jeśli współczynnik kolizji w danych wejściowych jest mały.

+1

Nie działa. Ta funkcja zwróci '-1' dla równych wartości, co oznacza, że ​​dla równych' a' i 'b' porównywania' a' do 'b' powie, że' a AnT

+2

Edycja YOR niczego nie zmieniła, tyle że teraz równe wartości zawsze zwrócą '1'. Standardowy 'qsort' jest zaprojektowany dla komparatora, który jest funkcją trójwartościową. Zasadniczo nie można go zredukować do funkcji dwuwartościowej, niezależnie od tego, co robisz. Musisz zwrócić '-1, 0, + 1'. – AnT

+1

Nie jest niczym niezwykłym implementacja debugowania 'qsort' w celu sprawdzenia poprawności funkcji porównania. Jeśli twoja funkcja porównania zwróci '1' dla porównania' (a, b) 'i jednocześnie zwróci' 1' dla '(b, a)' porównania, taka implementacja '' '' 'qsort'' '' '' '' '' niepowodzenie. Implementacja bez debugowania spowoduje po prostu niezdefiniowane zachowanie. – AnT

31

Twoja funkcja porównania jest zepsuta. Mówi on na przykład, że -1.0 jest równe (równoważne) z -1.1, ponieważ (int) ((-1.0) - (-1.1)) jest równe zero. Innymi słowy, sam powiedziałeś qsort, że względna kolejność -1.0 i -1.1 nie ma znaczenia. Dlaczego jesteś zaskoczony, że w wynikowym porządku wartości te nie są sortowane?

Generalnie należy unikać porównywania wartości liczbowych poprzez odjęcie od siebie. To po prostu nie działa. W przypadku typów zmiennoprzecinkowych może powodować nieprecyzyjne wyniki z kilku różnych powodów, z których jeden właśnie zaobserwowałeś. Dla typów całkowitych może się przepełnić.

Ogólny idiom dla porównania dwóch wartości liczbowych a i b dla qsort wygląda jak (a > b) - (a < b). Zapamiętaj to i użyj go. W twoim przypadku to byłoby

int compare (const void * a, const void * b) 
{ 
    float fa = *(const float*) a; 
    float fb = *(const float*) b; 
    return (fa > fb) - (fa < fb); 
} 

w kodzie C może to uczynić sens zdefiniować makro

#define COMPARE(a, b) (((a) > (b)) - ((a) < (b))) 

i używać go zamiast literowania porównań wyraźnie.

+2

+1 Musi być więcej plus jeden i to musi być przyjęta jako odpowiedź. –

+0

'return (fa> fb) - (fa fb); 'może być szybsze. YMMV. – chux

+0

@chux: dlaczego byłaby szybsza? – AnT

Powiązane problemy