Analisar um número de notação exponencial

88

Preciso analisar a string "1.2345E-02" (um número expresso em notação exponencial) para um tipo de dados decimal, mas Decimal.Parse("1.2345E-02")simplesmente gera um erro

Jimbo
fonte

Respostas:

175

É um número de ponto flutuante, você deve dizer que:

decimal d = Decimal.Parse("1.2345E-02", System.Globalization.NumberStyles.Float);
Hans Passant
fonte
50

Funciona se você especificar NumberStyles.Float:

decimal x = decimal.Parse("1.2345E-02", NumberStyles.Float);
Console.WriteLine(x); // Prints 0.012345

Não tenho certeza de porque isso não é compatível por padrão - o padrão é usar NumberStyles.Number, que usa os estilos AllowLeadingWhite, AllowTrailingWhite, AllowLeadingSign, AllowTrailingSign, AllowDecimalPoint e AllowThousands. Possivelmente está relacionado ao desempenho; especificar um expoente é relativamente raro, suponho.

Jon Skeet
fonte
Estou tentando fazer isso funcionar com o dobro, mas parece que não vai. Não tenho certeza por que não poderia ..?
JanT
@JanT: Sem mais informações do que "não vai" e "não poderia", não posso ajudar mais. Sugiro que você faça uma nova pergunta com muito mais detalhes, mostrando o que você tentou e exatamente o que aconteceu.
Jon Skeet
Tentei executar o código como na sua resposta, mas em vez de decimal usei o dobro. Mas já encontrei uma solução alternativa. Saúde
JanT
1
@JanT Seria bom se você pudesse compartilhar sua solução alternativa. Tenho exatamente o mesmo problema e poderia usar as informações. Obrigado!
Rick Glimmer
@RickGlimmer: Não tenho certeza de como você sabe que seu problema é exatamente igual ao de JanT, visto que eles nunca forneceram detalhes do que estavam tentando fazer. Substituir decimalpor doubleem meu código funciona bem para mim, exatamente como eu esperava. Se você pudesse fornecer detalhes do que está tentando, o código que está usando e o resultado, seria muito mais fácil ajudar.
Jon Skeet
35

Além de especificar NumberStyles, recomendo que você use a função decimal.ExperimenteParse , como:

decimal result;
if( !decimal.TryParse("1.2345E-02", NumberStyles.Any, CultureInfo.InvariantCulture, out result) )
{
     // do something in case it fails?
}

Como alternativa a NumberStyles.Any, você pode usar um conjunto específico se tiver certeza de seus formatos. por exemplo:

NumberStyles.AllowExponent | NumberStyles.Float
Sverrir Sigmundarson
fonte
1
Mas não é necessário usar Float com AllowExponent porque Float = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowDecimalPoint | AllowExponent
Lukáš Kmoch
@ LukášKmoch Certamente você está certo. Força do hábito como as outras (exceto Qualquer) não a inclui. No entanto, não deve doer para realizar a operação extra.
Sverrir Sigmundarson
13
decimal d = Decimal.Parse("1.2345E-02", System.Globalization.NumberStyles.Float);
Trigo mitch
fonte
9

Tenha cuidado com a resposta selecionada: há uma sutileza especificando System.Globalization.NumberStyles.Float in Decimal.Parse que pode levar a uma System.FormatException porque seu sistema pode estar aguardando um número formatado com ',' em vez de '.'

Por exemplo, na notação francesa, "1.2345E-02" é inválido, você deve convertê-lo para "1,2345E-02" primeiro.

Em conclusão, use algo como:

Decimal.Parse(valueString.Replace('.',','), System.Globalization.NumberStyles.Float);
KwentRell
fonte
1
Você está absolutamente correto. Não entendo por que ninguém mais tocou no assunto.
Carles Alcolea
10
Melhor usar CultureInfo.InvariantCulture como um terceiro parâmetro de Parse
Andriy Kozachuk
4

Descobri que a passagem NumberStyles.Float, em alguns casos, muda as regras pelas quais a string é processada e resulta em uma saída diferente de NumberStyles.Number(as regras padrão usadas pordecimal.Parse ).

Por exemplo, o código a seguir irá gerar um FormatExceptionem minha máquina:

CultureInfo culture = new CultureInfo("");
culture.NumberFormat.NumberDecimalDigits = 2;
culture.NumberFormat.NumberDecimalSeparator = ".";
culture.NumberFormat.NumberGroupSeparator = ",";
Decimal.Parse("1,234.5", NumberStyles.Float, culture); // FormatException thrown here

Eu recomendo usar a entrada NumberStyles.Number | NumberStyles.AllowExponent, pois isso permitirá números exponenciais e ainda processará a string de acordo com as decimalregras.

CultureInfo culture = new CultureInfo("");
culture.NumberFormat.NumberDecimalDigits = 2;
culture.NumberFormat.NumberDecimalSeparator = ".";
culture.NumberFormat.NumberGroupSeparator = ",";
Decimal.Parse("1,234.5",NumberStyles.Number | NumberStyles.AllowExponent, culture); // Does not generate a FormatException

Para responder à pergunta do autor da postagem, a resposta certa deve ser:

decimal x = decimal.Parse("1.2345E-02", NumberStyles.Number | NumberStyles.AllowExponent);
Console.WriteLine(x);
bastos.sergio
fonte
1

Aviso sobre o uso de NumberStyles.Any:

"6,33E + 03" converte para 6330 conforme o esperado. Em alemão, os pontos decimais são representados por vírgulas, mas 6,33E + 03 é convertido em 633000! Isso é um problema para meus clientes, pois a cultura que gera os dados não é conhecida e pode ser diferente da cultura que opera os dados. No meu caso, sempre tenho notação científica, então sempre posso substituir a vírgula pela vírgula decimal antes de analisar, mas se você estiver trabalhando com números arbitrários, como números bem formatados como 1.234.567, essa abordagem não funciona.

David Kitzinger
fonte
0

Você não precisa substituir os pontos (respectivamente as vírgulas), basta especificar a entrada IFormatProvider:

float d = Single.Parse("1.27315", System.Globalization.NumberStyles.Float, new CultureInfo("en-US"));
float d = Single.Parse("1,27315", System.Globalization.NumberStyles.Float, new CultureInfo("de-DE"));
mortal
fonte
0

Se você quiser verificar e converter o valor do expoente, use este

string val = "1.2345E-02";
double dummy;
bool hasExponential = (val.Contains("E") || val.Contains("e")) && double.TryParse(val, out dummy);
if (hasExponential)
{
    decimal d = decimal.Parse(val, NumberStyles.Float);
}

Espero que isso ajude alguém.

RAM
fonte
0

O padrão NumberStylepara decimal.Parse(String)é NumberStyles.Number, portanto, se você deseja apenas adicionar a funcionalidade para permitir expoentes, pode fazer um OR bit a bit para incluir NumberStyles.AllowExponent.

decimal d = decimal
    .Parse("1.2345E-02", NumberStyles.Number | NumberStyles.AllowExponent);
Joe Phillips
fonte