minimalna podwójna wartość w C / C ++

92

Czy istnieje standardowy i / lub przenośny sposób reprezentowania najmniejszej wartości ujemnej (np. Użycie ujemnej nieskończoności) w programie C (++)?

DBL_MIN w float.h to najmniejsza liczba dodatnia .

Będzie
źródło
4
Pójdę na -DBL_MAX, ale jestem pewien, że jest jakiś techniczny powód, dla którego tak nie jest :-)
4
@Neil, nie, nie ma, to nie jest tak, jak 2 liczby całkowite dopełniacza
fortran
W standardzie nie widziałem jeszcze niczego, co by powiedziało, że zakres typów zmiennoprzecinkowych musi być symetryczny wokół zera. Ale stałe w limits.h i <limits> sugerują, że zarówno standard C, jak i C ++ przewidują, że tak będzie.
Steve Jessop,
4
W rzeczywistości DBL_MIN w float.h jest najmniejszą dodatnią znormalizowaną liczbą. Są liczby, które są jeszcze mniejsze.
fdermishin
1
@fortran: IEEE 754 FP używa bitu znaku, a obecnie większość sprzętu FP to IEEE 754. Ale C i C ++ obsługują sprzęt inny niż IEEE 754 FP, więc pytanie jest otwarte, czy język gwarantuje, że -DBL_MAX musi być równa minimalnej możliwej do przedstawienia wartości.
j_random_hacker,

Odpowiedzi:

135

-DBL_MAX w ANSI C , który jest zdefiniowany w float.h.

dfa
źródło
wydaje się, że jest to najbardziej standardowe i przenośne
Will
Oto wyjaśnienie mojego -1: kto lub co mówi, że język C lub C ++ gwarantuje, że -DBL_MAX będzie reprezentowalny, nie mówiąc już o minimalnej reprezentowalnej wartości? Fakt, że większość sprzętu FP jest zgodna z IEEE 754 i używa tej reprezentacji, nie oznacza, że ​​-DBL_MAX ma gwarancję działania na dowolnej platformie C. zgodnej ze standardami.
j_random_hacker,
@j_random_hacker: zobacz odpowiedź Fortran „poniżej”.
JohnTortugo
3
@j_random_hacker To bardzo dobra uwaga, ale standard C wymaga, -DBL_MAXaby był dokładnie reprezentowalny, więc jeśli sprzęt FP nie jest w stanie tego zrobić, implementacja musi to obejść. Zobacz model zmiennoprzecinkowy w 5.2.4.2.2 Charakterystyka typów zmiennoprzecinkowych <float.h> p2 z C99 (od tamtej pory mogły zostać przeniesione w inne miejsce).
2
@j_random_hacker Tak, ale p2 określa e_min i e_max są niezależne od bitu znaku, więc DBL_MAXjest dokładnie (1 - b ^ −p) b ^ e_max, co jest dokładnie reprezentowalne, najbardziej ujemna wartość skończona to dokładnie - (1 - b ^ −p) b ^ e_max, a ponieważ tak się składa, że ​​jest dokładnie -DBL_MAX, negowanie DBL_MAXrównież nie może wprowadzać żadnych błędów zaokrągleń.
70

Liczby zmiennoprzecinkowe (IEEE 754) są symetryczne, więc jeśli możesz przedstawić największą wartość ( DBL_MAXlub numeric_limits<double>::max()), po prostu dodaj znak minus na początku.

A potem jest fajny sposób:

double f;
(*((long long*)&f))= ~(1LL<<52);
fortran
źródło
6
+1 Za wskazanie symetrii liczb zmiennoprzecinkowych :)
Andrew Hare
4
A co z implementacjami C / C ++, które nie używają pływaków IEEE 754?
Steve Jessop,
1
Podręcznik gcc dla -ffast-math mówi: „Ustawia -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans i -fcx-limited- zakres Ta opcja nie jest włączana żadną opcją -O, ponieważ może skutkować nieprawidłowym wyjściem dla programów, które są zależne od dokładnej implementacji reguł / specyfikacji IEEE lub ISO dla funkcji matematycznych. nie wymagają gwarancji wynikających z tych specyfikacji. " Szybka matematyka jest powszechnym ustawieniem, a Intel ICC na przykład domyślnie je przyjmuje. W sumie nie wiem, co to dla mnie oznacza :-)
Will
4
Oznacza to, że implementacje nie używają arytmetyki IEEE 754, ale aby być uczciwym, te opcje nadal używają reprezentacji IEEE. Możesz znaleźć niektóre biblioteki emulacji używające reprezentacji innej niż IEEE, ponieważ nie wszystkie procesory mają natywny format zmiennoprzecinkowy (chociaż mogą publikować C ABI, który zawiera format odpowiadający bibliotekom emulacji dostarczonym przez producenta). Dlatego nie wszystkie kompilatory mogą go używać. Zależy to tylko od tego, co masz na myśli, gdy pytasz o „standardowe i / lub przenośne”, w zasadzie są przenośne i przenośne w praktyce.
Steve Jessop
3
To, co mówisz, jest prawdą dla IEEE 754, ale standard nie wymaga użycia tego kodowania (jak wskazuje @SteveJessop, przenośność w praktyce nie jest tym samym, co przenośność w zasadzie).
Christophe,
44

W C użyj

#include <float.h>

const double lowest_double = -DBL_MAX;

W C ++ pre-11 użyj

#include <limits>

const double lowest_double = -std::numeric_limits<double>::max();

W C ++ 11 i nowszych wersjach użyj

#include <limits>

constexpr double lowest_double = std::numeric_limits<double>::lowest();
rubenvb
źródło
Czy min()funkcja nie była dostępna przed C ++ 11? A może to inna wartość niż -max()? en.cppreference.com/w/cpp/types/numeric_limits
Alexis Wilke
5
@Alexis: jeśli spojrzysz na najniższe trzy wiersze w tabeli na stronie, do której jesteś podłączony, zobaczysz, że minotrzymujesz najmniejszą dodatnią wartość wielkości i lowestnajwiększą ujemną wartość. Tak, to straszne. Witamy w cudownym świecie biblioteki standardowej C ++ :-P.
rubenvb
dla C jest zdefiniowane w float.h. limits.hdotyczy liczb całkowitych
Ciprian Tomoiagă
33

Spróbuj tego:

-1 * numeric_limits<double>::max()

Odniesienie: numeric_limits

Ta klasa jest wyspecjalizowana dla każdego z typów podstawowych, a jej elementy członkowskie zwracają lub są ustawione na różne wartości, które definiują właściwości tego typu na określonej platformie, na której jest kompilowany.

Andrew Hare
źródło
1
Dlaczego nie po prostu -numeric_limits<double>::max()?
k06a
4
@ k06a mając negację reprezentowaną przez pojedynczy znak w tak długim wyrażeniu, w którym łańcuch mówi nawet „max”, z pewnością prędzej czy później kogoś znajdzie. Albo jest przechowywany w zmiennej opisowej, albo użyj-1 * ... aby uczynić go nieco jaśniejszym.
Filip Haglund
20

Szukasz rzeczywistej nieskończoności lub minimalnej wartości skończonej? Jeśli pierwszy, użyj

-numeric_limits<double>::infinity()

który działa tylko wtedy, gdy

numeric_limits<double>::has_infinity

W przeciwnym razie powinieneś użyć

numeric_limits<double>::lowest()

który został wprowadzony w C ++ 11.

Jeśli lowest()nie jest dostępny, możesz wrócić do

-numeric_limits<double>::max()

które mogą się różnić od lowest() zasady, ale zwykle nie jest w praktyce.

Christoph
źródło
+1 za różnicę między wartością skończoną i nieskończoną! Ale standard nie gwarantuje symetrycznego kodowania zmiennoprzecinkowego. Więc -numeric_limits<double>::max()nawet jeśli to działa w praktyce, nie jest w pełni przenośne w teorii.
Christophe,
@Christophe: [x] naprawiono
Christoph
10

Naprawdę przenośne rozwiązanie C ++

Począwszy od C ++ 11 możesz użyć numeric_limits<double>::lowest(). Zgodnie ze standardem zwraca dokładnie to, czego szukasz:

Skończona wartość x taka, że ​​nie ma innej skończonej wartości y, gdzie y < x.
Ma znaczenie dla wszystkich specjalizacji, w których is_bounded != false.

Demo online


Wiele nieprzenośnych odpowiedzi C ++ tutaj!

Istnieje wiele odpowiedzi -std::numeric_limits<double>::max().

Na szczęście sprawdzą się w większości przypadków. Schematy kodowania zmiennoprzecinkowego rozkładają liczbę w mantysie i wykładniku, a większość z nich (np. Popularny IEEE-754 ) używa odrębnego bitu znaku, który nie należy do mantysy. Pozwala to przekształcić największy dodatni w najmniejszy ujemny, po prostu odwracając znak:

wprowadź opis obrazu tutaj

Dlaczego te nie są przenośne?

Standard nie narzuca żadnego standardu zmiennoprzecinkowego.

Zgadzam się, że mój argument jest trochę teoretyczny, ale przypuśćmy, że jakiś ekscentryczny twórca kompilatorów użyłby rewolucyjnego schematu kodowania z mantysą zakodowaną w pewnych odmianach dopełnienia do dwóch . Kodowanie dopełniające do dwóch nie jest symetryczne. na przykład dla znaku 8-bitowego ze znakiem maksymalna wartość dodatnia wynosi 127, ale minimalna wartość ujemna to -128. Więc możemy sobie wyobrazić, że niektóre kodowania zmiennoprzecinkowe wykazują podobne asymetryczne zachowanie.

Nie znam żadnego takiego schematu kodowania, ale chodzi o to, że standard nie gwarantuje, że odwracanie znaku daje zamierzony wynik . Więc tej popularnej odpowiedzi (przepraszam!) Nie można uznać za w pełni przenośne standardowe rozwiązanie! / * przynajmniej nie, jeśli nie zapewniłeś, że numeric_limits<double>::is_iec559to prawda * /

Christophe
źródło
1

Pierwotne pytanie dotyczy nieskończoności. Dlaczego więc nie użyć

#define Infinity  ((double)(42 / 0.0))

zgodnie z definicją IEEE? Możesz to oczywiście zaprzeczyć.

Norbert
źródło
Dobry pomysł ! I to działa . Ale tylko wtedy, gdynumeric_limits<double>::has_infinity && ! numeric_limits<double>::traps
Christophe,
1

Czy istnieje standardowy i / lub przenośny sposób reprezentowania najmniejszej wartości ujemnej (np. Użycie ujemnej nieskończoności) w programie C (++)?

Podejście C.

Wiele implementacji obsługuje nieskończoność +/-, więc najbardziej ujemna doublewartość to -INFINITY.

#include <math.h>
double most_negative = -INFINITY;

Czy istnieje sposób standardowy i / lub przenośny ....?

Teraz musimy wziąć pod uwagę również inne przypadki:

  • Żadnych nieskończoności

Po prostu -DBL_MAX.

  • Tylko nieskończoność bez znaku .

Spodziewałbym się w tym przypadku, wolałby OP -DBL_MAX.

  • Odnormalne wartości większe niż DBL_MAX.

Jest to niezwykły przypadek, prawdopodobnie poza obawami OP. Gdy doublejest zakodowany jako para liczb zmiennoprzecinkowych, aby osiągnąć pożądany zakres / precesję (patrz double-double ), istnieje maksymalna normalna double i być może większa niż normalna . Widziałem debatę, czy DBL_MAXnależy odnosić się do największej normy , największej z obu.

Na szczęście to sparowane podejście zwykle obejmuje -nieskończoność, więc pozostaje najbardziej ujemna wartość -INFINITY.


Aby zapewnić większą przenośność, kod może iść w dół trasy

// HUGE_VAL is designed to be infinity or DBL_MAX (when infinites are not implemented)
// .. yet is problematic with unsigned infinity.
double most_negative1 = -HUGE_VAL;  

// Fairly portable, unless system does not understand "INF"
double most_negative2 = strtod("-INF", (char **) NULL);

// Pragmatic
double most_negative3 = strtod("-1.0e999999999", (char **) NULL);

// Somewhat time-consuming
double most_negative4 = pow(-DBL_MAX, 0xFFFF /* odd value */);

// My suggestion
double most_negative5 = (-DBL_MAX)*DBL_MAX;
chux - Przywróć Monikę
źródło
-1

Jeśli nie masz włączonych wyjątków typu float (których nie powinieneś imho), możesz po prostu powiedzieć:

double neg_inf = -1/0.0;

Daje to ujemną nieskończoność. Jeśli potrzebujesz pływaka, możesz rzucić wynik

float neg_inf = (float)-1/0.0;

lub użyj arytmetyki pojedynczej precyzji

float neg_inf = -1.0f/0.0f;

Wynik jest zawsze taki sam, istnieje dokładnie jedna reprezentacja ujemnej nieskończoności zarówno w pojedynczej, jak i podwójnej precyzji, i są one konwertowane na siebie zgodnie z oczekiwaniami.

cmaster - przywróć monica
źródło
Dlaczego miałbyś to zrobić zamiast po prostu pisać-INFINITY
MM
Ponadto nieskończoność może istnieć lub nie, a jeśli istnieje, to dodatnia i ujemna może nie być rozróżnialna (w standardzie C).
MM
W wielu kompilatorach i / lub architekturach kod C / C ++ spowolni propagowanie wartości nieskończoności i NaN przez wielu z was.
markgalassi
@markgalassi Przyjrzyj się bliżej: Zauważysz, że neg_infinicjalizacja ma stałą wartość . Kompilator zajmie się obliczeniem infwartości. A kiedy użyjesz go jako wartości null do obliczenia maksimum, pierwsza iteracja na ogół nadpisze ją większą wartością. To znaczy wydajność nie jest problemem. Program operacyjny zawiera konkretnie pytanie o „np. Użycie ujemnej nieskończoności” i -infjest rzeczywiście jedyną poprawną odpowiedzią na to pytanie. Głosowałeś w dół za poprawną i przydatną odpowiedź.
cmaster