Подтвердить что ты не робот

Как найти, если две переменные приблизительно равны?

Я пишу модульные тесты, которые проверяют вычисления в базе данных, и есть много округления и усечения, и это означает, что иногда цифры немного отключаются.

Когда вы проверяете, я нахожу много раз, когда все пройдет, но скажу, что они терпят неудачу - например, цифра будет 1, и я получаю 0.999999

Я имею в виду, я мог бы обойти все в целое число, но поскольку я использую много рандомизированных образцов, в конце концов я собираюсь получить что-то вроде этого

10,5 +10,4999999999

один округляется до 10, другой округляется до 11.

Как мне решить эту проблему, когда мне нужно что-то примерно правильное?

4b9b3361

Ответ 1

Определите значение допуска (например, "эпсилон" ), например, 0,00001, а затем используйте для сравнения разности так:

if (Math.Abs(a - b) < epsilon)
{
   // Values are within specified tolerance of each other....
}

[Вы можете использовать Double.Epsilon, но вам придется использовать множитель.]

Еще лучше, напишите метод расширения, чтобы сделать то же самое. У нас есть что-то вроде Assert.AreSimiliar(a,b) в наших модульных тестах.

Ответ 2

Вы можете предоставить функцию, которая включает параметр для приемлемой разницы между двумя значениями. Например

// 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; 
}

И затем назовите его

double value1 = 24.5;
double value2 = 24.4999;

bool equalValues = CloseEnoughForMe(value1, value2, 0.001);

Если вы хотите быть немного профессиональным, вы можете вызвать функцию ApproximatelyEquals или что-то в этом направлении.

static bool ApproximatelyEquals(this double value1, double value2, double acceptableDifference)

Ответ 3

Я не проверял, в какую версию MS Test были добавлены, но в v10.0.0.0. Методы Assert.AreEqual имеют перегрузки, которые принимают параметр дельта и делают приблизительное сравнение.

т.е.

//
// 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);

Ответ 4

Один из способов сравнения чисел с плавающей запятой - сравнить количество отображаемых чисел с плавающей запятой. Это решение равнодушно к размеру чисел, и поэтому вам не нужно беспокоиться о размере "эпсилон", упомянутом в других ответах.

Описание алгоритма можно найти здесь (в конце - функция AlmostEqual2sComplement), и вот моя версия С#.

UPDATE: Предоставленная ссылка устарела. Новая версия, которая содержит некоторые улучшения и исправления, здесь

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;
    }
}

Если вы хотите сравнить поплавки, измените все double на float, все long на int и 0x8000000000000000 на 0x80000000.

С помощью параметра representationTolerance вы можете указать, насколько велика ошибка. Более высокое значение означает, что принимается большая ошибка. Обычно я использую значение 10.

Ответ 5

Вопрос заключался в том, как утверждать, что в модульном тестировании что-то было почти равным. Вы утверждаете, что что-то почти равное, используя встроенную функцию Assert.AreEqual. Например:

Assert.AreEqual(expected: 3.5, actual : 3.4999999, delta:0.1);

Этот тест пройдет. Проблема решена и без необходимости писать свою собственную функцию!