Considere o seguinte código
#include <iostream>
int main()
{
const std::string s("5.87747175e-39");
float f = std::stof(s);
std::cout << s << " - " << f << std::endl;
return 0;
}
Quando compilado com g++ e executado, isso gera std::out_of_range
. No entanto, como você pode ver aqui , 5.87747175e-39 é um float válido. Como isso pode ser?
Caso faça diferença, estou no amd64 Debian 12 que usa g++ versão 12.2
Pelo que vale, Python considera isso um float válido
python3 -c 'print(float("5.87747175e-39"))'
Além disso, parece que esse problema se estende a todos os floats desnormalizados . Por exemplo, "1.1754942e-38" é o maior float desnormalizado IEEE-754 válido , mas stof() o considera um erro. O menor float normalizado é "1.1754944e-38", que é o que é relatado como FLT_MIN
Observando a implementação do GCC,
std::stof
eles lançam esse erro se o float for menor queFLT_MIN
, nãoFLT_TRUE_MIN
.A razão para isso é provavelmente porque
FLT_TRUE_MIN
foi introduzido no C++17 , enquantostd::stof
no C++11 .Underflow pode ser relatado para resultados na faixa subnormal porque tais resultados podem ter erros maiores que o normal. O padrão C++ permite conversões de strings para
float
relatar underflow se o resultado estiver na faixa subnormal.5.87747175•10 −39 está dentro do intervalo de
float
, mas está no intervalo subnormal (discutido abaixo). O padrão C++ dizstof
chamadasstrtof
(no rascunho C++ 2020 N4849 21.3.4 [string.conversions] 3) e então adia para o padrão C para definirstrtof
. O padrão C indica questrtof
pode haver subfluxo. C 2018 7.12.1 6 diz "O resultado sofre subfluxo se a magnitude do resultado matemático for tão pequena que o resultado matemático não possa ser representado, sem erro de arredondamento extraordinário, em um objeto do tipo especificado." "Erro de arredondamento extraordinário" refere-se aos erros de arredondamento que ocorrem quando valores subnormais são encontrados. (Valores subnormais têm menos bits de significando significativos e, portanto, estão sujeitos a erros relativos maiores do que valores normais, portanto, seus erros de arredondamento podem ser considerados extraordinários.)Portanto, o padrão C++ permite que uma implementação C++ tenha subfluxo para valores subnormais, mesmo que eles sejam representáveis.
Esta pergunta é essencialmente uma duplicata desta pergunta, exceto que uma é para
double
e esta é parafloat
.Valores normais e subnormais
Para o ponto flutuante binário IEEE-754 de 32 bits, o intervalo normal é de 2 −126 a 2 128 −2 104 . Dentro desse intervalo, cada número é representado com um significante (a parte fracionária da representação de ponto flutuante) que tem um bit 1 inicial seguido por 23 bits adicionais e, portanto, o erro que ocorre ao arredondar qualquer número real nesse intervalo para o valor representável mais próximo é no máximo 2 −24 vezes o valor da posição do bit inicial.
Além dessa faixa normal, há uma faixa subnormal de 2 −149 a 2 −126 −2 −149 . Nesse intervalo, a parte expoente do formato de ponto flutuante atingiu seu menor valor e não pode mais ser diminuída. Para representar números cada vez menores nesse intervalo, o significando é reduzido abaixo do mínimo normal de 1. Ele começa com um 0 e é seguido por 23 bits adicionais. Nesse intervalo, o erro que ocorre ao arredondar um número real para o valor representável mais próximo pode ser maior que 2 −24 vezes o valor da posição do bit inicial. Como o expoente não pode ser diminuído mais, os números nesse intervalo têm números crescentes de bits 0 iniciais à medida que ficam cada vez menores. Assim, os erros relativos envolvidos no uso desses números aumentam.
De acordo com std::numeric_limits::min
a saída para
float
é1.17549e-38
. Isso é consistente com o valor documentadoFLT_MIN
de .