En ocasiones, y sobre todo si manejas geometría y puntos, puedes obtener resultados con un alto número de decimales. Al comparar dos números con tantos decimales en ocasiones obtenemos falsos negativos porque difieren en el octavo o noveno decimal. Este es un problema muy común y puede resultar invalidante a la hora de comprobar, por ejemplo, si dos puntos están en la misma recta. No suele ser una buena idea usar la función de redondeo de la librería Math, puesto que puede redondear a alza o a la baja el último de los decimales especificados por lo que cabe una posibilidad (remota) de que tengamos un falso negativo.
Generalmente, lo más aconsejable siempre es establecer un grado de precisión, de forma que cuando vayas a comparar los dos valores numéricos lo hagas manejando el mismo número de decimales alejando la posibilidad de que el último de los decimales cambie por una operación de redondeo. Para ello hemos implementado la función Trunk que asegura un corte limpio y sin sorpresas. cuyo código agregamos a continuación.
/// <summary>
/// Trunca un numero decimal con la cantidad de decimales especificados.
/// No redondea los decimales, solo corta en la posición establecida
/// </summary>
/// <param name=”n“>número a comprobar</param>
/// <param name=”Decs“>Número de decimales a cortar</param>
/// <returns>El número con los decimales recortados.</returns>
public static double Trunk(double n, Int16 Decs = 0)
{
string sep = CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;
string StrNum = n.ToString();
Int16 k = 0;
bool bVar = false;
for (int i = 0; i <= StrNum.Length – 1; i++)
{
if (StrNum.Substring(i, 1) == sep)
bVar = true;
if (bVar)
k += 1;
}
if (k <= Decs)
return n;
else
{
Int64 lngPot;
lngPot = Convert.ToInt64(Math.Pow(10, Decs));
if (n < 0)
{
n = Math.Ceiling(n * lngPot);
}
else
{
n = Math.Floor(n * lngPot);
}
return n / lngPot;
}
}
Para ponerlo a prueba puedes implementar un pequeño formulario parecido al siguiente:
En el se indica el número a controlar y la cantidad de decimales, que puede ir de 0 a 9. Hemos implementado las funcionalidades de solo números para que el usuario solo pueda entrar números o el separador decimal en el primer caso, si no dispones de la función puedes ir a este enlace para poder incorporar la función en tu proyecto.
Veamos a continuación el código en el formulario.
En el evento KeyPress del TextBox donde se indica el número con decimales:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
TextBox tb = sender as TextBox;
string antT = tb.Text;
string t = tb.Text;
e.Handled = MyClase.OnlyNumbers(e.KeyChar.ToString(), true, ref t);
if (antT != t)
{
tb.Text = t;
tb.SelectionStart = tb.Text.Length;
}
}
En el evento KeyPress del TextBox donde se indica la cantidad de decimales:
private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
{
TextBox tb = sender as TextBox;
string antT = tb.Text;
string t = tb.Text;
e.Handled = MyClase.OnlyNumbers(e.KeyChar.ToString(), false, ref t);
if (antT != t)
{
tb.Text = t;
tb.SelectionStart = tb.Text.Length;
}
}
Para este TextBox hemos establecido la propiedad MaxLength en 1 para que el usuario solo pueda establecer una cantidad de decimales entre 0 y 9.
Y, finalmente, en el evento Click del Botón colocamos el código que vamos a necesitar para operar el número recibido.
private void button1_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(this.textBox1.Text))
{
return;
}
if (string.IsNullOrWhiteSpace(this.textBox2.Text))
{
return;
}
double d = MyClase.Trunk(this.textBox1.ToDouble(),
Convert.ToInt16(this.textBox2.ToInteger()));
this.label3.Text = d.ToString();
}
Los resultados deberían ser parecidos a los siguientes: