2015-04-27 10 views
8

Załóżmy, że mam dwa obiekty i i f odpowiednich typów I i F. Wiem, że std::is_integral<I>::value jest prawdziwe, a std::is_floating_point<F>::value jest prawdziwe.Zgodny ze standardami sposób porównywania wartości zmiennoprzecinkowej z całkową?

Czy istnieje w pełni zgodne ze standardami sposobem, aby dowiedzieć się, jeżeli wartość i jest mniejsza niż wartość f? Zwróć uwagę na nacisk "w pełni zgodny ze standardami", w tym pytaniu interesują mnie jedynie odpowiedzi wspierane przez gwarancje ze standardu C++.


trywialne realizacja i < I(f) nie działa, ponieważ wartość f nie może zmieścić i. Brawurowe wdrożenie F(i) < f również nie działa, ponieważ precyzja f może nie wystarczyć do reprezentowania i, powodując, że i zostanie zaokrąglone do wartości równej f (jeśli masz pływające IEEE754, to zawiedzie 16777219 < 16777220.f).

Ale tu pojawia się prawdziwy dylemat: jeśli chcesz użyć std::numeric_limits::max, aby złagodzić te problemy, powróć do pierwotnego problemu porównywania wartości liczbowych i liczb całkowitych! Dzieje się tak, ponieważ typ std::numeric_limits::max jest równy typowi oryginalnemu.

+0

Wymóg „na to pytanie Jestem zainteresowany tylko w odpowiedzi, które są poparte gwarancjami C++ standard” jest nieuzasadnione i głupi na to pytanie. Jest to podobne do pytania o sposób implementacji sortowania bąbelkowego i wymaga, aby odpowiedzi były wspierane przez standard C++. Standard C++ nie ma nic do powiedzenia na temat rodzaju bąbelków. –

+0

'i Barry

+3

@ Cheersandhth.-Alf Nie zgadzam się. Cała kwestia polega na znalezieniu metody dobrze zdefiniowanej w dowolnej implementacji C++. Możesz to zrobić dla sortowania bąbelkowego, ale nie jestem pewien, czy możesz to porównać, stąd moje pytanie. – orlp

Odpowiedz

0

ten sposób byłoby to zrobić:

Przypuszczam, że f jest skończony, przypadki nieskończony i NaN mają być obsługiwane w innym miejscu.

  1. porównać f i F (I), jeśli nie równe, skończysz, F i I są albo < lub>

  2. jeśli równe, a następnie porównać I (f) oraz i

Jedyne założenia:

  • jeśli jest pływak mający dokładnie wartość I, a następnie F (I) podaje, że v artość

  • jeśli jest liczbą całkowitą o dokładnie taką samą wartość jak F, następnie (f) umożliwia, że ​​wartość

  • monotoniczności funkcji f i

EDIT

Mówiąc dokładniej, powyższe triki służą do pisania funkcji porównania, a nie tylko do testowania równości ...

floatType F(intType i); 
intType I(floatType f); 
int cmpfi(floatType f,intType i) 
{ 
    assert(isfinite(f)); 
    if(f < F(i)) return -1; 
    if(f == F(i)) 
    { 
    if(I(f) < i) return -1; 
    return I(f) > i; 
    } 
    return 1; 
} 

do ciebie, aby przekształcić ten projekt w C++ kod, który może obsługiwać kilka różnych floatType/intType

+1

Szukam testu zamówienia, a nie równości. – orlp

+0

@orlp, nie podałem szczegółowej odpowiedzi, ale te wskazówki dotyczą pisania czegoś w rodzaju funkcji cmp. –

+1

Cóż, w kroku 1 powiedziałeś, że "skończyłeś", gdy dowiedziałeś się, że 'f! = I'. To oczywiście nie rozwiązuje problemu :) – orlp

2
  1. Jeśli f jest poza zasięgiem I, można powiedzieć wynik tylko przez jego znak.

  2. Jeśli f znajduje się w zakresie I, ale jest za duży, aby mieć część ułamkową, porównaj ją jako liczbę całkowitą.

  3. W przeciwnym razie, jest to bezpieczne, aby rzucić i do F ponieważ zaokrąglanie nie zmieni wynik porównania: f jest już mniejsza niż wartość I które byłyby zaokrąglone.

.

template< typename I, typename F > 
std::enable_if_t< std::is_integral_v<I> && std::is_floating_point_v<F>, 
bool > less(I i, F f) { 
    // Return early for operands of different signs. 
    if (i < 0 != f < 0) return i < 0; 

    bool rev = i >= 0; 
    if (rev) { 
     f = - f; // Make both operands non-positive. 
     i = - i; // (Negativity avoids integer overflow here.) 
    } 

    if (f < /* (F) */ std::numeric_limits<I>::min()) { 
     // |i| < |f| because f is outside the range of I. 
     return rev; 
    } 
    if (f * std::numeric_limits<F>::epsilon() <= -1) { 
     // f must be an integer (in I) because of limited precision in F. 
     I fi = f; 
     return rev? fi < i : i < fi; 
    } 
    // |f| has better than integer precision. 
    // If (F) |i| loses precision, it will still be greater than |f|. 
    return rev? f < i : i < f; 
} 

Demo: http://coliru.stacked-crooked.com/a/b5c4bea14bc09ee7

+0

... ehh, ten kod działa tylko dla * podpisanych * liczb całkowitych. Możesz dodać przeciążenie niepodpisanych liczb całkowitych poprzez usunięcie całej działalności związanej z podpisem. – Potatoswatter

Powiązane problemy