variable tipo solo separador punto numeros mostrar miles formato float declarar decimales dato dar con c# numbers decimal

c# - tipo - Obtener el número de dígitos antes del punto decimal



tipo dato double c# (26)

Tengo una variable de tipo decimal y quiero verificar el número de dígitos antes del punto decimal. ¿Que debería hacer? Por ejemplo, 467.45 debería devolver 3 .


Aquí está mi versión optimizada del código inspirado en la respuesta de Gray:

static int GetNumOfDigits(decimal dTest) { int nAnswer = 0; dTest = Math.Abs(dTest); //For loop version for (nAnswer = 0; nAnswer < 29 && dTest > 1; ++nAnswer) { dTest *= 0.1M; } //While loop version //while (dTest > 1) //{ // nAnswer++; // dTest *= 0.1M; //} return (nAnswer); }

Si no desea que se invoque Math.Abs ​​dentro de esta función, asegúrese de usarlo fuera de la función del parámetro antes de llamar a GetNumOfDigits.

Decidí eliminar los otros códigos para reducir el desorden en mi respuesta, a pesar de que me ayudaron a llegar a este punto ...

Si hay alguna mejora necesaria, házmelo saber y lo actualizaré :).


Aquí hay un ejemplo recursivo (principalmente por diversión).

void Main() { digitCount(0.123M); //0 digitCount(493854289.543354345M); //10 digitCount(4937854345454545435549.543354345M); //22 digitCount(-4937854345454545435549.543354345M); //22 digitCount(1.0M); //1 //approximately the biggest number you can pass to the function that works. digitCount(Decimal.MaxValue + 0.4999999M); //29 } int digitCount(decimal num, int count = 0) { //divided down to last digit, return how many times that happened if(Math.Abs(num) < 1) return count; return digitCount(num/10, ++count); //increment the count and divide by 10 to ''remove'' a digit }


En lugar de convertir a cadena, también puede dividir el número entre 10 hasta que sea igual a 0. Resulta interesante que las operaciones matemáticas en decimales son mucho más lentas que convertir el decimal en una cadena y devolver la longitud (vea los puntos de referencia a continuación).
Esta solución no usa los métodos matemáticos que toman un doble como entrada; por lo que todas las operaciones se realizan en decimales y no se trata de lanzar.

using System; public class Test { public static void Main() { decimal dec = -12345678912345678912345678912.456m; int digits = GetDigits(dec); Console.WriteLine(digits.ToString()); } static int GetDigits(decimal dec) { decimal d = decimal.Floor(dec < 0 ? decimal.Negate(dec) : dec); // As stated in the comments of the question, // 0.xyz should return 0, therefore a special case if (d == 0m) return 0; int cnt = 1; while ((d = decimal.Floor(d / 10m)) != 0m) cnt++; return cnt; } }

La salida es 29 . Para ejecutar esta muestra, visite este link .

Nota al margen: algunos puntos de referencia muestran resultados sorprendentes (10k ejecuciones):

  • while ((d = decimal.Floor(d / 10m)) != 0m) : 25ms
  • while ((d = d / 10m) > 1m) : 32ms
  • ToString con Math-double-operations: 3ms
  • ToString con operaciones decimales: 3ms
  • BigInt (ver answer de @Heinzi): 2ms

También el uso de números aleatorios en lugar de siempre el mismo valor (para evitar el posible almacenamiento en caché de la conversión de decimales a cadenas) mostró que los métodos basados ​​en cadenas son mucho más rápidos.


Entonces, me encontré con esto antes, y lo resolví con este código:

SqlDecimal d = new SqlDecimal(467.45M); int digits = d.Precision - d.Scale;

SqlDecimal es parte del espacio de nombres System.Data.SqlTypes . "Precisión" es el número total de dígitos significativos, mientras que "Escala" es el número de dígitos después del punto decimal.

Ahora, sé que una objeción a seguir esta ruta es que SqlDecimal es parte del código específico de SQL Server. Es un punto válido, pero también señalaría que es una parte del propio framework .NET, y lo ha sido desde al menos la versión 1.1, por lo que parece que todavía sería aplicable independientemente del código que esté haciendo. .

Miré bajo el capó con un descompilador ( JetBrains ''dotPeek en este caso), para ver si tal vez el código para calcular la precisión y la escala podría extraerse fácilmente y usarse, sin tirar de SqlDecimal . El código para calcular la escala es muy simple, pero el método para calcular la precisión no es trivial, así que si fuera yo, simplemente pasaría por SqlDecimal .


Esta respuesta casi se elimina de Calculate System.Decimal Precision and Scale pero con un pequeño cambio para adaptarse a la pregunta.

class Program { static void Main() { decimal dec = 467.45m; Console.WriteLine(dec.GetNumberOfDigitsBeforeDecimalPlace()); } } public static class DecimalEx { public static int GetNumberOfDigitsBeforeDecimalPlace(this decimal dec) { var x = new System.Data.SqlTypes.SqlDecimal(dec); return x.Precision - x.Scale; } }

Además, si desea hacerlo sin utilizar la clase SqlDecimal, consulte la respuesta de Jon Skeet para la misma pregunta.


Esta sería la solución de Java

public class test { public static void main(String args[]) { float f = 1.123f; int a = (int) f; int digits = 0; while (a > 0) { digits++; a=a/10; } System.out.println("No Of digits before decimal="+digits); } }


Esto funcionará si realmente no desea usar el método de registro (cuál es la mejor forma de IMO). Es la manera más clara que puedo pensar de hacer esto usando ToString ():

Math.Abs(val).ToString("f0", CultureInfo.InvariantCulture).Length

O, como alternativa, si no desea contar 0.123M como teniendo un dígito:

Math.Abs(val).ToString("#", CultureInfo.InvariantCulture).Length


Intentaré esto:

Math.Truncate(467.45).ToString().Length

Si quieres estar seguro de no tener resultados raros para diferentes culturas y con decimales negativos, es mejor que uses esto:

var myDecimal = 467.45m; Math.Truncate(Math.Abs(myDecimal)).ToString(CultureInfo.InvariantCulture).Length


La forma matemática de hacer esto (y probablemente la más rápida) es obtener un logarytm de la base 10 de un valor absoluto de este número y redondearlo.

Math.Floor(Math.Log10(Math.Abs(val)) + 1);


Las otras soluciones perderán dígitos si el número es demasiado grande.

public int Digits(Decimal i) { NumberFormatInfo format = CultureInfo.CurrentCulture.NumberFormat; var str = Math.Abs(i).ToString().Replace(format.NumberGroupSeparator, ""); var index = str.IndexOf(format.NumberDecimalSeparator); var digits = index == -1 ? str.Length : index; }


No he probado esto, pero lo mantendría sencillo y lo haré:

decimal value = 467.45; string str = Convert.toString(value); // convert your decimal type to a string string[] splitStr = str.split(''.''); // split it into an array (use comma separator assuming you know your cultural context) Console.WriteLine(splitStr[0].Count); // get the first element. You can also get the number of figures after the point by indexing the next value in the array.

Esto no maneja números negativos. Si te preocupas por aquellos entonces considerando tomar el valor absoluto. Además, si quiere que 0 antes de la posición decimal no se cuente, puede usar una instrucción if simple para verificarlo.


Para obtener una respuesta precisa y culturalmente agnóstica, hago lo siguiente:

  1. Use System.Numerics.BigInteger , cuyo constructor acepta un decimal y no parece producir ningún error de redondeo.
  2. Use BigInteger.Abs() para eliminar cualquier signo.
  3. Use BigInteger.ToString() con el formato "#" para suprimir cualquier separador que pueda ocurrir.

Código

decimal num = 123213123.123123M; int length = BigInteger.Abs((BigInteger)num).ToString("#").Length;


Preferiría lo siguiente en lugar de convertir a int para garantizar que también se puedan manejar números grandes (por ejemplo, decimal.MaxValue ):

Math.Truncate ( Math.Abs ( decValue ) ).ToString( "####" ).Length


Puede hacer esto redondeando el número, luego obteniendo la longitud del nuevo número. Podrías hacerlo así:

var number = 476.43; var newNumber = Math.round(number); //get the length of the rounded number, and subtract 1 if the //number is negative (remove the negative sign from the count) int digits = newNumber.ToString().Length - (number < 0 ? 1 : 0);


Puede usar la función ToString con un formato personalizado.

Decimal value = 467.45m; int count = Math.Abs(value).ToString("#", System.Globalization.CultureInfo.InvariantCulture).Length;

El # especifica que solo quieres los caracteres antes del .

System.Globalization.CultureInfo.InvariantCulture garantiza que no obtendrá ningún formato de la opción de región.


Si tiene un sesgo hacia números más pequeños, puede usar algo más simple como este.

Se divide en dos métodos, por lo que el primer método es más pequeño y puede incluirse.

El rendimiento es aproximadamente el mismo que la solución con Log10, pero sin los errores de redondeo. El método que usa Log10 sigue siendo el más rápido (un poco) especialmente para números> 1 millón.

public static int CountNrOfDigitsIfs(decimal d) { var absD = Math.Abs(d); // 1 if (absD < 10M) return 1; // 2 if (absD < 100M) return 2; // 3 if (absD < 1000M) return 3; // 4 if (absD < 10000M) return 4; return CountNrOfDigitsIfsLarge(d); } private static int CountNrOfDigitsIfsLarge(decimal d) { // 5 if (d < 100000M) return 5; // 6 if (d < 1000000M) return 6; // 7 if (d < 10000000M) return 7; // 8 if (d < 100000000M) return 8; // 9 if (d < 1000000000M) return 9; // 10 if (d < 10000000000M) return 10; // 11 if (d < 100000000000M) return 11; // 12 if (d < 1000000000000M) return 12; // 13 if (d < 10000000000000M) return 13; // 14 if (d < 100000000000000M) return 14; // 15 if (d < 1000000000000000M) return 15; // 16 if (d < 10000000000000000M) return 16; // 17 if (d < 100000000000000000M) return 17; // 18 if (d < 1000000000000000000M) return 18; // 19 if (d < 10000000000000000000M) return 19; // 20 if (d < 100000000000000000000M) return 20; // 21 if (d < 1000000000000000000000M) return 21; // 22 if (d < 10000000000000000000000M) return 22; // 23 if (d < 100000000000000000000000M) return 23; // 24 if (d < 1000000000000000000000000M) return 24; // 25 if (d < 10000000000000000000000000M) return 25; // 26 if (d < 100000000000000000000000000M) return 26; // 27 if (d < 1000000000000000000000000000M) return 27; // 28 if (d < 10000000000000000000000000000M) return 28; return 29; // Max nr of digits in decimal }

Este código se genera utilizando la siguiente plantilla T4:

<# const int SIGNIFICANT_DECIMALS = 29; const int SPLIT = 5; #> namespace Study.NrOfDigits { static partial class DigitCounter { public static int CountNrOfDigitsIfs(decimal d) { var absD = Math.Abs(d); <# for (int i = 1; i < SPLIT; i++) { // Only 29 significant digits var zeroes = new String(''0'', i); #> // <#= i #> if (absD < 1<#= zeroes #>M) return <#= i #>; <# } #> return CountNrOfDigitsIfsLarge(d); } private static int CountNrOfDigitsIfsLarge(decimal d) { <# for (int i = SPLIT; i < SIGNIFICANT_DECIMALS; i++) { // Only 29 significant digits var zeroes = new String(''0'', i); #> // <#= i #> if (d < 1<#= zeroes #>M) return <#= i #>; <# } #> return <#= SIGNIFICANT_DECIMALS #>; // Max nr of digits in decimal } } }


Si trata los ceros o la falta de ceros como 1 número, esto está bien. Si desea que el cero devuelva cero o la falta de cero para devolver cero, entonces hay unos pocos casos extremos para resolver que no deberían ser demasiado difíciles de agregar. Además, debe valor absoluto para manejar números negativos. Agregó ese caso de prueba también.

const decimal d = 123.45m; const decimal d1 = 0.123m; const decimal d2 = .567m; const decimal d3 = .333m; const decimal d4 = -123.45m; NumberFormatInfo currentProvider = NumberFormatInfo.InvariantInfo; var newProvider = (NumberFormatInfo) currentProvider.Clone(); newProvider.NumberDecimalDigits = 0; string number = d.ToString("N", newProvider); //returns 123 = .Length = 3 string number1 = d1.ToString("N", newProvider); //returns 0 = .Length = 1 string number2 = d2.ToString("N", newProvider); //returns 1 = .Length = 1 string number3 = d3.ToString("N", newProvider); //returns 0 = .Length = 1 string number4 = Math.Abs(d4).ToString("N", newProvider); //returns 123 = .Length = 3

Aquí hay una solución final, si encuentra un caso de prueba que no funciona, hágamelo saber. Debería devolver 3,0,0,0,3 para los casos de prueba proporcionados.

public static int NumbersInFrontOfDecimal(decimal input) { NumberFormatInfo currentProvider = NumberFormatInfo.InvariantInfo; var newProvider = (NumberFormatInfo)currentProvider.Clone(); newProvider.NumberDecimalDigits = 0; var absInput = Math.Abs(input); var numbers = absInput.ToString("N", newProvider); //Handle Zero and < 1 if (numbers.Length == 1 && input < 1.0m) { return 0; } return numbers.Length; }


Solución sin convertir a string (que puede ser peligrosa en el caso de cultivos exóticos):

static int GetNumberOfDigits(decimal d) { decimal abs = Math.Abs(d); return abs < 1 ? 0 : (int)(Math.Log10(decimal.ToDouble(abs)) + 1); }

Tenga en cuenta que esta solución es válida para todos los valores decimales

ACTUALIZAR

De hecho, esta solución no funciona con algunos valores grandes, por ejemplo: 999999999999998 , 999999999999999 , 9999999999999939 ...

Obviamente, las operaciones matemáticas con el double no son lo suficientemente precisas para esta tarea.

Al buscar valores incorrectos, tiendo a usar alternativas basadas en string propuestas en este tema. En cuanto a mí, esa es la evidencia de que son más confiables y fáciles de usar (pero tenga en cuenta las culturas). Las soluciones basadas en bucle pueden ser más rápidas.

Gracias a los comentaristas, lástima de mí, lección para ti.


TLDR todas las demás respuestas. Escribí esto en PHP, y las matemáticas serían las mismas. (Si supiera C #, habría escrito en ese idioma).

$input=21689584.999; $input=abs($input); $exp=0; do{ $test=pow(10,$exp); if($test > $input){ $digits=$exp; } if($test == $input){ $digits=$exp+1; } $exp++; }while(!$digits); if($input < 1){$digits=0;} echo $digits;

No dudo que haya una mejor manera, pero quería lanzar mi $ .02

EDITAR:

Hice php -ized el código que mencioné en mis comentarios, pero eliminé la conversión int.

function digitCount($input){ $digits=0; $input=abs($input); while ($input >= 1) { $digits++; $input=$input/10; //echo $input."<br>"; } return $digits; } $big=(float)(PHP_INT_MAX * 1.1); echo digitCount($big);


Use modulo, no soy un programador de C #, pero estoy bastante seguro de que esta solución funciona:

double i = 1; int numberOfDecimals = 0; while (varDouble % i != varDouble) { numberOfDecimals++; i*=10; } return numberOfDecimals;


Utilizar:

var value=467.45; var length=((int)value).ToString().Length


sencillo :

string value = "467.45"; int count = value.split(''.'')[0] == "0" ? 0 : value.split(''.'')[0].ToString().Replace("-","").Count();


Math.Floor(Math.Log10((double)n) + 1); es el camino a seguir.

Convertir a int es MALO porque el decimal puede ser más grande que int :

Decimal.MaxValue = 79,228,162,514,264,337,593,543,950,335; Int32.MaxValue = 2,147,483,647; //that is, hexadecimal 0x7FFFFFFF;

Math.Floor(n).ToString().Count(); es malo porque puede incluir miles de separadores.


Algoritmo:

  • Convertir |decimal| Encadenar.
  • Si "." existe en el decimal, corte antes de eso, de lo contrario considere el número entero.
  • Devuelve la longitud de la cuerda.

Ejemplo:

3.14 --> 3.14 --> "3.14" --> "3.14".Substring(0,1) --> "3".Length --> 1 -1024 --> 1024 --> "1024" --> IndexOf(".") = -1 --> "1024" --> 4

Código:

static int getNumOfDigits (decimal num) { string d = Math.Abs(num).ToString(); if (d.IndexOf(".") > -1) { d = d.Substring(0, d.IndexOf(".")); } return d.Length; }


decimal d = 467.45M; int i = (int)d; Console.WriteLine(i.ToString(CultureInfo.InvariantCulture).Length); //3

Como un método;

public static int GetDigitsLength(decimal d) { int i = int(d); return i.ToString(CultureInfo.InvariantCulture).Length; }

Nota : Por supuesto, debe verificar primero que la parte completa de sus decimales sea más grande que Int32.MaxValue o no. Porque si lo es, obtienes una OverflowException .

Es tal un caso, utilizando long lugar de int puede un mejor enfoque. Sin embargo, incluso un long ( System.Int64 ) no es lo suficientemente grande como para contener todos decimal valores decimal posibles.

Como Rawling mentioned , su parte completa puede contener el separador de miles y mi código se romperá en tal caso. Porque de esta manera, ignora por completo que mi número contiene NumberFormatInfo.NumberGroupSeparator o no.

Es por eso que obtener números solo es un mejor enfoque. Me gusta;

i.ToString().Where(c => Char.IsDigit(c)).ToArray()


var sep = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); var count = d.ToString().TakeWhile(c => c != sep).Count();