Extract decimal part of a Decimal

4

I'm having trouble formulating a good way to extract the decimal part of a decimal variable, so far the existing implementation on the system is this:

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, you have to adapt the code depending on the culture in which the program is running because of the number division by the decimal point.

I'm trying to find some other implementation to remove that part of the number, preferably something that does not handle a string to perform such an operation. The only way I came to mind was by using Truncate :

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

But for some reason, this implementation does not seem very robust, I do not really know why, but I'd like to see other possible implementations before deciding which one I'll use.

After a comment from the user dcastro in the question, I decided to retry the implementation specified above and it does not return the expected value, since I want 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 question remains, is there a way to get this value without manipulating the number as a string?

    
asked by anonymous 14.05.2015 / 14:08

4 answers

3

You can get the decimal part as an integer in the way you expect, without using Strings, using 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 houses:

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 this .Net Fiddle

Performance:

I did some performance tests using your current method (with Strings) and the method I proposed (using Floor) and I concluded that when calling only once, or in a loop with few iterations, the string method 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 paltry 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);
    
14.05.2015 / 16:39
2

I found a simple and efficient solution:

  • Removes the integer part and converts the result to string: Math.Abs((decimal)numero) % 1
  • Remove the "0." of the result: Substring(2)
  • Converts the resulting string to an Int32: Convert.ToInt32
  • The final code looks like this:

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

    Original code removed from here .

        
    14.05.2015 / 15:57
    0

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

    val.ToString(CultureInfo.InvariantCulture)
    
        
    14.05.2015 / 14:20
    -2

    Try the following:     float x = 1.466;     string nu = (Mathf.Abs (x)% 1) .ToString ("N1") Substring (2);     int milliseconds = System.Convert.ToInt32 (nu);

    OUTPUT: 4 where: N1 - formats in a decimal, N2 - two ... Substring remove "1.";

    It is not the best practice that is solved in a timer to get the milliseconds.

        
    13.11.2017 / 23:56