Extract decimal part of a decimal

Asked

Viewed 4,483 times

4

I’m having trouble formulating a good means of extracting the decimal part of a variable decimal, so far the existing implementation in the system is that:

public static bool getCasasDecimais(decimal val, out int result)
{
    string[] split = val.ToString().Split('.');
    return int.TryParse(split[split.Length-1], out result);
}

But it has a problem, it is necessary to adapt the code depending on the culture in which the program is running because of the division of the number by the decimal point.

I’m trying to find some other implementation to pull that part of the number, preferably something that doesn’t manipulate a string to carry out such an operation. The only way that came to mind was using Truncate:

decimal number = 12.5523;
var x = number - Math.Truncate(number);

But for some reason, this implementation doesn’t seem very robust, I don’t really know why, but I would like to see other possible implementations before deciding which one to use.

After a user comment star in the question, I decided to re-test the implementation specified above and it does not return the expected value, since I wish to have the decimal digits in an integer, not just the decimal part.

Exemplo: 45.545    
Resultado esperado: 545
Resultado proveniente da implementação com Math.Truncate: 0.545

The doubt continues, there is a way to receive this value without manipulating the number as string?

  • If the input is 123.456, you want the result to be 456 or 0.456?

  • the expected result is 456, and now testing the implementation using the Truncate it won’t even work because it will return 0.456

  • 'Cause :/ I think the best thing is to use the invariant culture.

4 answers

3


It is possible to get the decimal part as an integer in the form you expect, without using Strings, using the Math.Floor as follows: counting how many decimal places there are in the resulting number and multiplying the decimal part by the power of the number of places:

public static void Main()
{
    decimal numero = 45.545M;
    int parteDecimal = ObterCasasDecimais(numero);
    Console.WriteLine(parteDecimal); // resulta 545
}

static int ObterCasasDecimais(decimal numero) 
{    
    decimal resultado = numero - Math.Floor(numero);
    int qtdCasas = QuantidadeCasasDecimais(resultado);
    int parteDecimal = Convert.ToInt32((double)resultado * Math.Pow(10, qtdCasas));
    return parteDecimal;
}

static int QuantidadeCasasDecimais(decimal num)
{
    return BitConverter.GetBytes(decimal.GetBits(num)[3])[2];
}

See the code working In that . Net Fiddle

Performance:

I did some performance tests using your current method (with Strings) and the method I proposed (using Floor) and concluded that when called only once, or in a loop with few iterations, the method with string is faster. When there are many iterations, the Floor method performs better. In practice, if they are called only once and not within loops, the difference is derisory so use the method you find most convenient.

The code I used to test was more or less this:

int TotalNumeros = 1000;
decimal[] decimais =
{
    2.50M, 3.2345M, 54.4004M, 32.1212M, 123123.3244325M, 3112.453M, 1.2M, 43.12M, 9994.2342M, 24324.2M, 5345.0M, 3123.00M, 123134.456456M
};

Random random = new Random();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < TotalNumeros; i++)
{
    decimal number = decimais[random.Next(decimais.Length - 1)]; // pega um número aleatório do array de decimais
    int resultado = ObterCasasDecimais(number); // Método proposto para resposta (Floor)
}
stopwatch.Stop();
TimeSpan resultadoFloor = stopwatch.Elapsed;

stopwatch.Reset();
stopwatch.Start();
for (int i = 0; i < TotalNumeros; i++)
{ 
    decimal number = decimais[random.Next(decimais.Length - 1)];// pega um número aleatório do array de decimais 
    int resultado;
    getCasasDecimais(number, out resultado); // método atual (String)
}
stopwatch.Stop();
TimeSpan resultadoMetodoAtual = stopwatch.Elapsed;

Console.WriteLine(resultadoFloor < resultadoMetodoAtual ? "Método Floor melhor" : "Método atual melhor");

Console.WriteLine("\r\nResultado Floor: {0:g}", resultadoFloor);
Console.WriteLine("Resultado Método Atual: {0:g}", resultadoMetodoAtual);

2

I found a simple and efficient solution:

  1. Removes the entire part and converts the result into a string: Math.Abs((decimal)numero) % 1
  2. Removes "0." from the result: Substring(2)
  3. Converts the resulting string to Int32: Convert.ToInt32

The final code stays like this:

decimal numero = 123.5489m;
int fracao = Convert.ToInt32((Math.Abs(numero) % 1).ToString().Substring(2));
Console.WriteLine("Fracao: " + fracao);

Original code taken out from here.

0

Well, the simplest way to solve the culture problem is to use the CultureInfo.InvariantCulture. So the proper functioning of the code no longer depends on the machine where it runs.

val.ToString(CultureInfo.InvariantCulture)
  • When using this solution, which decimal separator digit will be used? '.' or ',' ?

  • Unfortunately the Math.Truncate does not return what I desire, so she is not the solution ;/

  • IS ., then the Split does not need to be changed.

0

Try the following one: float x = 1,466; string nu = (Mathf.Abs (x) %1). Tostring("N1"). Substring(2); int milliseconds = System.Convert.Toint32 (naked);

EXIT: 4 where: N1 - formats in a decimal place, N2 -two... Substring removes "1.";

It is not the best practice more it is the one that solves in a time counter to get the milliseconds.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.