valore doppio minimo in C / C ++


92

Esiste un modo standard e / o portabile per rappresentare il valore negativo più piccolo (ad esempio per usare l'infinito negativo) in un programma C (++)?

DBL_MIN in float.h è il numero positivo più piccolo .


4
Vado per -DBL_MAX, ma sono sicuro che ci sia qualche ragione tecnica per cui non è così :-)

4
@Neil, no non c'è, non è come 2 numeri interi del complemento
fortran

Non ho ancora visto nulla nello standard per dire che l'intervallo dei tipi in virgola mobile deve essere simmetrico intorno allo zero. Ma le costanti in limits.he <limits> suggeriscono che sia lo standard C che C ++ si aspettano che lo siano.
Steve Jessop,

4
In realtà DBL_MIN in float.h è il numero normalizzato positivo più piccolo . Ci sono numeri ancora più piccoli.
fdermishin

1
@fortran: IEEE 754 FP utilizza un bit di segno, e sicuramente la maggior parte dell'hardware FP in questi giorni è IEEE 754. Ma C e C ++ supportano hardware non IEEE 754 FP, quindi la domanda è aperta se il linguaggio garantisce che -DBL_MAX deve essere uguale al valore minimo rappresentabile.
j_random_hacker

Risposte:


135

-DBL_MAX in ANSI C , che è definito in float.h.


questo sembra il più standard e portatile
Will il

Ecco la spiegazione del mio -1: chi o cosa dice che -DBL_MAX è garantito dal linguaggio C o C ++ come rappresentabile, per non parlare del valore rappresentabile minimo? Il fatto che la maggior parte dell'hardware FP sia conforme a IEEE 754 e utilizzi questa rappresentazione, non significa che -DBL_MAX sia garantito per funzionare su qualsiasi piattaforma C conforme allo standard.
j_random_hacker

@j_random_hacker: vedi la risposta di fortran "sotto".
JohnTortugo

3
@j_random_hacker Questo è un ottimo punto, ma lo standard C richiede -DBL_MAXdi essere esattamente rappresentabile, quindi se l'hardware FP non è in grado di farlo, l'implementazione deve solo aggirarlo . Vedere il modello a virgola mobile in 5.2.4.2.2 Caratteristiche dei tipi mobili <float.h> p2 di C99 (potrebbe essere stato spostato altrove da allora).

2
@j_random_hacker Sì, ma p2 specifica e_min ed e_max sono indipendenti dal bit del segno, quindi DBL_MAXè esattamente (1 - b ^ −p) b ^ e_max, che è esattamente rappresentabile, il valore finito più negativo è esattamente - (1 - b ^ −p) b ^ e_max, e poiché ciò è esattamente -DBL_MAX, la negazione DBL_MAXnon può introdurre alcun errore di arrotondamento.

70

I numeri in virgola mobile (IEEE 754) sono simmetrici, quindi se puoi rappresentare il valore più grande ( DBL_MAXo numeric_limits<double>::max()), anteponi semplicemente un segno meno.

E poi è il modo interessante:

double f;
(*((long long*)&f))= ~(1LL<<52);

6
+1 Per aver sottolineato la simmetria dei numeri in virgola mobile :)
Andrew Hare

4
Che dire delle implementazioni C / C ++ che non utilizzano i float IEEE 754?
Steve Jessop,

1
il manuale di gcc per -ffast-math dice "Sets -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans e -fcx-limited- range Questa opzione non è attivata da nessuna opzione -O poiché può dare come risultato un output errato per programmi che dipendono da un'esatta implementazione delle regole / specifiche IEEE o ISO per le funzioni matematiche. Tuttavia, potrebbe produrre un codice più veloce per i programmi che lo fanno non richiedere le garanzie di queste specifiche. " La matematica veloce è un'impostazione comune e Intel ICC, ad esempio, la utilizza per impostazione predefinita. Tutto sommato, non sono sicuro di cosa significhi per me :-)
Will

4
Significa che le implementazioni non usano l'aritmetica IEEE 754, ma per essere onesti quelle opzioni usano ancora la rappresentazione IEEE. Potresti trovare alcune librerie di emulazione che utilizzano una rappresentazione non IEEE, poiché non tutti i processori hanno un formato float nativo (sebbene possano pubblicare un C ABI che include un formato, corrispondente alle librerie di emulazione fornite dal produttore). Quindi non tutti i compilatori possono usarne uno. Dipende solo da cosa intendi quando chiedi "standard e / o portatile", c'è portabile in linea di principio e portabile in pratica.
Steve Jessop

3
Quello che dici è vero per IEEE 754, ma lo standard non richiede l'uso di questa codifica (come sottolinea @SteveJessop, portabile in pratica non è la stessa cosa di portabile in linea di principio).
Christophe,

44

In C, usa

#include <float.h>

const double lowest_double = -DBL_MAX;

In C ++ pre-11, usa

#include <limits>

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

In C ++ 11 e versioni successive, usa

#include <limits>

constexpr double lowest_double = std::numeric_limits<double>::lowest();

La min()funzione non era disponibile prima di C ++ 11? O è un valore diverso da -max()? en.cppreference.com/w/cpp/types/numeric_limits
Alexis Wilke

5
@Alexis: se guardi le tre righe più basse della tabella sulla pagina che hai collegato, vedrai che minottieni il valore positivo più piccolo in magnitudine e lowestil valore negativo più grande in magnitudine. Sì, è terribile. Benvenuto nel fantastico mondo della libreria standard C ++ :-P.
rubenvb

per C è definito in float.h. limits.hè per numeri interi
Ciprian Tomoiagă

33

Prova questo:

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

Riferimento: numeric_limits

Questa classe è specializzata per ciascuno dei tipi fondamentali, con i suoi membri che restituiscono o impostano i diversi valori che definiscono le proprietà di quel tipo nella piattaforma specifica in cui viene compilato.


1
Perché non solo -numeric_limits<double>::max()?
k06a

4
@ k06a avendo la negazione rappresentata da un singolo carattere in un'espressione così lunga, dove la stringa dice anche "max", sicuramente qualcuno prima o poi arriverà. O è memorizzato in una variabile descrittiva o viene utilizzato -1 * ...per renderlo un po 'più chiaro.
Filip Haglund

20

Cerchi l'infinito effettivo o il valore finito minimo? Se il primo, usa

-numeric_limits<double>::infinity()

che funziona solo se

numeric_limits<double>::has_infinity

Altrimenti, dovresti usare

numeric_limits<double>::lowest()

che è stato introdotto in C ++ 11.

Se lowest()non è disponibile, puoi ricorrere a

-numeric_limits<double>::max()

che può differire lowest()in linea di principio, ma normalmente non in pratica.


+1 per la differenza tra valore finito e infinito! Ma lo standard non garantisce una codifica in virgola mobile simmetrica. Quindi, -numeric_limits<double>::max()anche se funziona in pratica, non è completamente portabile in teoria.
Christophe

@Christophe: [x] fixed
Christoph

10

Una soluzione C ++ veramente portatile

A partire da C ++ 11 puoi usare numeric_limits<double>::lowest(). Secondo lo standard, restituisce esattamente quello che stai cercando:

Un valore finito x tale che non ci siano altri valori finiti y dove y < x.
Significativo per tutte le specializzazioni in cui is_bounded != false.

Demo online


Molte risposte C ++ non portatili qui!

Ci sono molte risposte -std::numeric_limits<double>::max().

Fortunatamente, funzioneranno bene nella maggior parte dei casi. Gli schemi di codifica in virgola mobile scompongono un numero in una mantissa e un esponente e la maggior parte di essi (ad esempio il popolare IEEE-754 ) utilizza un bit di segno distinto, che non appartiene alla mantissa. Questo permette di trasformare il più grande positivo nel più piccolo negativo semplicemente capovolgendo il segno:

inserisci qui la descrizione dell'immagine

Perché non sono portatili?

Lo standard non impone alcuno standard in virgola mobile.

Sono d'accordo che la mia argomentazione sia un po 'teorica, ma supponiamo che qualche compilatore eccentrico utilizzi uno schema di codifica rivoluzionario con una mantissa codificata in alcune varianti di un complemento a due . La codifica in complemento di due non è simmetrica. ad esempio per un carattere a 8 bit con segno il massimo positivo è 127, ma il minimo negativo è -128. Quindi possiamo immaginare che una codifica in virgola mobile mostri un comportamento asimmetrico simile.

Non sono a conoscenza di alcuno schema di codifica del genere, ma il punto è che lo standard non garantisce che il capovolgimento del segno produca il risultato desiderato . Quindi questa risposta popolare (scusate ragazzi!) Non può essere considerata una soluzione standard completamente portatile! / * almeno non se non hai affermato che numeric_limits<double>::is_iec559è vero * /



1

La domanda originale riguarda l'infinito. Quindi, perché non usare

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

secondo la definizione IEEE? Puoi negarlo ovviamente.


Bella idea! E funziona . Ma solo senumeric_limits<double>::has_infinity && ! numeric_limits<double>::traps
Christophe,

1

Esiste un modo standard e / o portabile per rappresentare il valore negativo più piccolo (ad esempio per usare l'infinito negativo) in un programma C (++)?

Approccio C.

Molte implementazioni supportano +/- infiniti, quindi il doublevalore più negativo è -INFINITY.

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

Esiste un modo standard e / o portatile ...?

Ora dobbiamo considerare anche altri casi:

  • Nessun infinito

Semplicemente -DBL_MAX.

  • Solo un infinito senza segno .

Mi aspetto che in questo caso OP preferirei -DBL_MAX.

  • Valori anomali di grandezza maggiore di DBL_MAX.

Questo è un caso insolito, probabilmente al di fuori delle preoccupazioni di OP. Quando doubleè codificato come una coppia di punti mobili per ottenere l'intervallo / precessione desiderato, (vedere doppio-doppio ) esiste un normale massimo doublee forse uno più normale . Ho visto dibattiti se DBL_MAXdovrebbe riferirsi alla più grande normalità , alla più grande di entrambe.

Fortunatamente questo approccio accoppiato di solito include un -infinity, quindi rimane il valore più negativo -INFINITY.


Per una maggiore portabilità, il codice può seguire il percorso

// 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;

-1

Se non hai le eccezioni float abilitate (cosa che non dovresti imho), puoi semplicemente dire:

double neg_inf = -1/0.0;

Questo produce un infinito negativo. Se hai bisogno di un galleggiante, puoi lanciare il risultato

float neg_inf = (float)-1/0.0;

o usa l'aritmetica a precisione singola

float neg_inf = -1.0f/0.0f;

Il risultato è sempre lo stesso, c'è esattamente una rappresentazione dell'infinito negativo in precisione singola e doppia e si convertono tra loro come ci si aspetterebbe.


Perché dovresti farlo invece di scrivere semplicemente-INFINITY
MM

Inoltre, l'infinito può o non può esistere, e se esiste, allora positivo e negativo potrebbero non essere distinguibili (nello Standard C).
MM

In molti compilatori e / o architetture il codice C / C ++ rallenterà molti di voi che propagheranno valori infinito e NaN.
markgalassi

@markgalassi Dai un'occhiata più da vicino: noterai che neg_infè inizializzato a un valore costante . Il compilatore si occuperà di calcolare il infvalore. E quando lo usi come valore nullo per calcolare un massimo, la prima iterazione lo sovrascriverà generalmente con un valore maggiore. Cioè le prestazioni non sono certo un problema. E l'OP chiede specificamente "ad esempio di usare l'infinito negativo", ed -infè in effetti l'unica risposta corretta a questo. Hai downvotato una risposta corretta e utile.
cmaster - ripristina monica il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.