programar - posible comparacion de referencias no intencionada c#
¿Cómo encuentro si dos variables son aproximadamente iguales? (5)
Defina un valor de tolerancia (también conocido como ''epsilon'' o ''delta''), por ejemplo, 0.00001, y luego use para comparar la diferencia de la siguiente manera:
if (Math.Abs(a - b) < delta)
{
// Values are within specified tolerance of each other....
}
Podría usar Double.Epsilon
pero tendría que usar un factor de multiplicación.
Mejor aún, escriba un método de extensión para hacer lo mismo. Tenemos algo como Assert.AreSimiliar(a,b)
en nuestras pruebas de unidad.
El método Assert.AreEqual()
Microsoft tiene una sobrecarga que toma un delta: public static void AreEqual(double expected, double actual, double delta)
NUnit también proporciona una sobrecarga a su método Assert.AreEqual()
que permite proporcionar un delta.
Estoy escribiendo pruebas unitarias que verifican los cálculos en una base de datos y hay muchos redondeos y truncamientos y cosas que significan que, a veces, las cifras están ligeramente fuera de lugar.
Al verificar, encuentro muchas veces cuando las cosas pasan, pero dicen que fallan. Por ejemplo, la cifra será 1 y obtengo 0.999999
Quiero decir, podría simplemente redondear todo en un entero, pero como estoy usando muchas muestras aleatorias, eventualmente obtendré algo como esto
10.5 10.4999999999
uno va a redondear a 10, el otro redondeará a 11.
¿Cómo debo resolver este problema cuando necesito algo para estar aproximadamente correcto?
La pregunta era preguntar cómo afirmar que algo era casi igual en las pruebas unitarias. Usted afirma que algo es casi igual usando la función Assert.AreEqual
incorporada. Por ejemplo:
Assert.AreEqual(expected: 3.5, actual : 3.4999999, delta:0.1);
Esta prueba pasará. Problema resuelto y sin tener que escribir tu propia función!
No he comprobado en qué versión de MS Test se agregó, pero en v10.0.0.0 los métodos Assert.AreEqual tienen sobrecargas que aceptan un parámetro delta y hacen una comparación aproximada.
Es decir, https://msdn.microsoft.com/en-us/library/ms243458(v=vs.140).aspx
//
// Summary:
// Verifies that two specified doubles are equal, or within the specified accuracy
// of each other. The assertion fails if they are not within the specified accuracy
// of each other.
//
// Parameters:
// expected:
// The first double to compare. This is the double the unit test expects.
//
// actual:
// The second double to compare. This is the double the unit test produced.
//
// delta:
// The required accuracy. The assertion will fail only if expected is different
// from actual by more than delta.
//
// Exceptions:
// Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException:
// expected is different from actual by more than delta.
public static void AreEqual(double expected, double actual, double delta);
Podría proporcionar una función que incluya un parámetro para una diferencia aceptable entre dos valores. Por ejemplo
// close is good for horseshoes, hand grenades, nuclear weapons, and doubles
static bool CloseEnoughForMe(double value1, double value2, double acceptableDifference)
{
return Math.Abs(value1 - value2) <= acceptableDifference;
}
Y luego llamalo
double value1 = 24.5;
double value2 = 24.4999;
bool equalValues = CloseEnoughForMe(value1, value2, 0.001);
Si quisiera ser un poco profesional al respecto, podría llamar a la función ApproximatelyEquals
o algo parecido.
static bool ApproximatelyEquals(this double value1, double value2, double acceptableDifference)
Una forma de comparar números de punto flotante es comparar cuántas representaciones de punto flotante los separan. Esta solución es indiferente al tamaño de los números y, por lo tanto, no tiene que preocuparse por el tamaño de "épsilon" mencionado en otras respuestas.
here se puede encontrar una descripción del algoritmo (la función AlmostEqual2sComplement al final) y aquí está mi versión C #.
ACTUALIZACIÓN: El enlace proporcionado está desactualizado. La nueva versión que incluye algunas mejoras y correcciones de errores está randomascii.wordpress.com/2012/02/25/…
public static class DoubleComparerExtensions
{
public static bool AlmostEquals(this double left, double right, long representationTolerance)
{
long leftAsBits = left.ToBits2Complement();
long rightAsBits = right.ToBits2Complement();
long floatingPointRepresentationsDiff = Math.Abs(leftAsBits - rightAsBits);
return (floatingPointRepresentationsDiff <= representationTolerance);
}
private static unsafe long ToBits2Complement(this double value)
{
double* valueAsDoublePtr = &value;
long* valueAsLongPtr = (long*)valueAsDoublePtr;
long valueAsLong = *valueAsLongPtr;
return valueAsLong < 0
? (long)(0x8000000000000000 - (ulong)valueAsLong)
: valueAsLong;
}
}
Si desea comparar flotadores, cambie todo double
a float
, long
a int
y 0x8000000000000000
a 0x80000000
.
Con el parámetro renderingTolerance puede especificar qué tan grande es el error que se tolera. Un valor más alto significa que se acepta un error más grande. Normalmente uso el valor 10 por defecto.