valor duplo mínimo em C / C ++


92

Existe uma maneira padrão e / ou portátil de representar o menor valor negativo (por exemplo, usar infinito negativo) em um programa C (++)?

DBL_MIN em float.h é o menor número positivo .


4
Vou para -DBL_MAX, mas tenho certeza de que há alguma razão técnica para que isso não seja assim :-)

4
@ Neil, não, não é, não é como 2 inteiros do complemento
fortran

Ainda não vi nada no padrão para dizer que o intervalo dos tipos de ponto flutuante tem que ser simétrico em torno de zero. Mas as constantes em limits.h e <limits> sugerem que tanto o padrão C quanto o C ++ esperam que o sejam.
Steve Jessop

4
Na verdade, DBL_MIN em float.h é o menor número normalizado positivo . Existem números que são ainda menores.
fdermishin

1
@fortran: IEEE 754 FP usa um bit de sinal, e certamente a maioria dos hardwares de FP hoje em dia é IEEE 754. Mas C e C ++ suportam hardware não IEEE 754 FP, então a questão é se a linguagem dá a garantia de que -DBL_MAX deve ser igual ao valor mínimo representável.
j_random_hacker

Respostas:


135

-DBL_MAX em ANSI C , que é definido em float.h.


este parece o mais padrão e portátil
Será

Aqui está a explicação para meu -1: quem ou o que diz que -DBL_MAX é garantido pela linguagem C ou C ++ como sendo representável, quanto mais o valor mínimo representável? O fato de que a maioria do hardware FP é compatível com IEEE 754, e usa essa representação, não significa que -DBL_MAX tem garantia de funcionamento em qualquer plataforma C em conformidade com o padrão.
j_random_hacker

@j_random_hacker: veja a resposta de fortran 'abaixo'.
JohnTortugo

3
@j_random_hacker Esse é um ponto muito bom, mas o padrão C precisa -DBL_MAXser exatamente representável, então se o hardware do FP não for capaz disso, a implementação precisa apenas contornar isso. Veja o modelo de ponto flutuante em 5.2.4.2.2 Características dos tipos flutuantes <float.h> p2 de C99 (pode ter sido movido para outro lugar desde então).

2
@j_random_hacker Sim, mas p2 especifica e_min e e_max são independentes do bit de sinal, então DBL_MAXé exatamente (1 - b ^ −p) b ^ e_max, que é exatamente representável, o valor finito mais negativo é exatamente - (1 - b ^ −p) b ^ e_max, e como isso é exatamente -DBL_MAX, negar DBL_MAXtambém não pode introduzir erros de arredondamento.

70

Os números de ponto flutuante (IEEE 754) são simétricos, portanto, se você pode representar o maior valor ( DBL_MAXou numeric_limits<double>::max()), apenas prefixe um sinal de menos.

E então é a maneira legal:

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

6
+1 Por apontar a simetria dos números de ponto flutuante :)
Andrew Hare

4
E quanto às implementações C / C ++ que não usam flutuadores IEEE 754?
Steve Jessop

1
O manual do gcc para -ffast-math diz "Sets -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans e -fcx-limited- intervalo Esta opção não é ativada por nenhuma opção -O, pois pode resultar em saída incorreta para programas que dependem de uma implementação exata de regras / especificações IEEE ou ISO para funções matemáticas. Pode, no entanto, produzir código mais rápido para programas que dependem não exigem as garantias dessas especificações. " Matemática rápida é uma configuração comum, e o Intel ICC, por exemplo, usa isso como padrão. Resumindo, não tenho certeza do que isso significa para mim :-)
Será que

4
Significa que as implementações não usam aritmética IEEE 754, mas para ser justo, essas opções ainda usam representação IEEE. Você pode encontrar algumas bibliotecas de emulação que usam representação não-IEEE, uma vez que nem todos os processadores têm um formato float nativo (embora possam publicar um C ABI que inclui um formato, correspondente às bibliotecas de emulação fornecidas pelo fabricante). Portanto, nem todos os compiladores podem usar um. Depende apenas do que você quer dizer quando pede "padrão e / ou portátil", há portátil em princípio e portátil na prática.
Steve Jessop de

3
O que você diz é verdade para o IEEE 754, mas o padrão não requer o uso dessa codificação (como @SteveJessop aponta, portátil na prática não é o mesmo que portátil em princípio).
Christophe,

44

Em C, use

#include <float.h>

const double lowest_double = -DBL_MAX;

Em C ++ pré-11, use

#include <limits>

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

No C ++ 11 e posterior, use

#include <limits>

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

A min()função não estava disponível antes do C ++ 11? Ou isso é um valor diferente de -max()? en.cppreference.com/w/cpp/types/numeric_limits
Alexis Wilke

5
@Alexis: se você olhar para as três linhas mais baixas da tabela na página vinculada, verá que minobtém o menor valor positivo em magnitude e lowesto maior valor negativo em magnitude. Sim, é terrível. Bem-vindo ao mundo brilhante da biblioteca padrão C ++ :-P.
rubenvb

para C é definido em float.h. limits.hé para números inteiros
Ciprian Tomoiagă

33

Experimente isto:

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

Referência: numeric_limits

Esta classe é especializada para cada um dos tipos fundamentais, com seus membros retornando ou configurados para os diferentes valores que definem as propriedades que o tipo possui na plataforma específica em que é compilado.


1
Por que não apenas -numeric_limits<double>::max()?
k06a

4
@ k06a tendo a negação representada por um único caractere em uma expressão tão longa, onde a string até diz "max", certamente alcançará alguém mais cedo ou mais tarde. Pode ser armazenado em uma variável descritiva ou usado -1 * ...para torná-lo um pouco mais claro.
Filip Haglund

20

Você está procurando o infinito real ou o valor finito mínimo? Se for o primeiro, use

-numeric_limits<double>::infinity()

que só funciona se

numeric_limits<double>::has_infinity

Caso contrário, você deve usar

numeric_limits<double>::lowest()

que foi introduzido no C ++ 11.

Se lowest()não estiver disponível, você pode voltar para

-numeric_limits<double>::max()

que pode ser diferente lowest()em princípio, mas normalmente não na prática.


+1 para a diferença entre valor finito e infinito! Mas o padrão não garante uma codificação de ponto flutuante simétrico. Portanto, -numeric_limits<double>::max()mesmo que funcione na prática, não é totalmente portátil em teoria.
Christophe,

@Christophe: [x] corrigido
Christoph

10

Uma solução C ++ verdadeiramente portátil

A partir do C ++ 11 você pode usar numeric_limits<double>::lowest(). De acordo com o padrão, ele retorna exatamente o que você está procurando:

Um valor finito x tal que não existe outro valor finito y onde y < x.
Significativo para todas as especializações nas quais is_bounded != false.

Demo online


Muitas respostas C ++ não portáveis ​​aqui!

Existem muitas respostas acontecendo -std::numeric_limits<double>::max().

Felizmente, eles funcionarão bem na maioria dos casos. Os esquemas de codificação de ponto flutuante decompõem um número em uma mantissa e um expoente e a maioria deles (por exemplo, o popular IEEE-754 ) usa um bit de sinal distinto, que não pertence à mantissa. Isso permite transformar o maior positivo no menor negativo apenas girando o sinal:

insira a descrição da imagem aqui

Por que não são portáteis?

O padrão não impõe nenhum padrão de ponto flutuante.

Eu concordo que meu argumento é um pouco teórico, mas suponha que algum criador de compiladores excêntricos usaria um esquema de codificação revolucionário com uma mantissa codificada em algumas variações de um do complemento de dois . A codificação do complemento de dois não é simétrica. por exemplo, para um caractere de 8 bits com sinal, o máximo positivo é 127, mas o mínimo negativo é -128. Portanto, podemos imaginar que alguma codificação de ponto flutuante mostre um comportamento assimétrico semelhante.

Não estou ciente de nenhum esquema de codificação como esse, mas o ponto é que o padrão não garante que a inversão do sinal produza o resultado pretendido . Portanto, esta resposta popular (desculpe pessoal!) Não pode ser considerada uma solução padrão totalmente portátil! / * pelo menos não se você não afirmou que numeric_limits<double>::is_iec559é verdade * /



1

A questão original diz respeito ao infinito. Então, por que não usar

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

de acordo com a definição do IEEE? Você pode negar isso, é claro.


Boa ideia ! E funciona . Mas apenas senumeric_limits<double>::has_infinity && ! numeric_limits<double>::traps
Christophe,

1

Existe uma maneira padrão e / ou portátil de representar o menor valor negativo (por exemplo, usar infinito negativo) em um programa C (++)?

Abordagem C.

Muitas implementações suportam +/- infinitos, então o doublevalor mais negativo é -INFINITY.

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

Existe uma forma padrão e / ou portátil ....?

Agora precisamos considerar também outros casos:

  • Sem infinitos

Simplesmente -DBL_MAX.

  • Apenas um infinito sem sinal .

Eu esperaria neste caso, OP preferiria -DBL_MAX.

  • Valores anormais maiores em magnitude do que DBL_MAX.

Este é um caso incomum, provavelmente fora das preocupações da OP. Quando doubleé codificado como um par de pontos flutuantes para atingir a faixa / precessão desejada, (ver duplo-duplo ) existe um máximo normal double e talvez um maior anormal . Eu vi um debate se DBL_MAXdeveria se referir ao maior normal , ao maior de ambos.

Felizmente, essa abordagem emparelhada geralmente inclui um -infinity, de modo que o valor mais negativo permanece -INFINITY.


Para maior portabilidade, o código pode seguir o caminho

// 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 você não tem exceções flutuantes habilitadas (o que você não deveria imho), você pode simplesmente dizer:

double neg_inf = -1/0.0;

Isso resulta em infinito negativo. Se você precisar de um float, você pode lançar o resultado

float neg_inf = (float)-1/0.0;

ou use aritmética de precisão única

float neg_inf = -1.0f/0.0f;

O resultado é sempre o mesmo, há exatamente uma representação de infinito negativo em precisão simples e dupla, e eles convertem um para o outro como você esperaria.


Por que você faria isso em vez de apenas escrever-INFINITY
MM de

Além disso, o infinito pode ou não existir e, se existir, o positivo e o negativo podem não ser distinguíveis (no Padrão C).
MM de

Em muitos compiladores e / ou arquiteturas, seu código C / C ++ retardará muito a propagação de valores infinitos e NaN.
markgalassi

@markgalassi Por favor, dê uma olhada mais de perto: Você notará que neg_infé inicializado com um valor constante . O compilador cuidará de calcular o infvalor. E quando você o usa como valor nulo para calcular um máximo, a primeira iteração geralmente o substitui por um valor maior. Ou seja, o desempenho dificilmente é um problema. E o OP pergunta especificamente sobre "por exemplo, usar infinito negativo", e -infé de fato a única resposta correta para isso. Você votou contra uma resposta correta e útil.
cmaster - restabelecer monica
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.