Конечно, никогда не следует сравнивать значения с плавающей запятой, которые получаются в результате вычисления на равенство, но всегда используйте небольшой допуск, например ::
double value1 = ...
double value2 = ...
if (Math.Abs(value1 - value2) < tolerance * Math.Abs(value1))
{
... values are close enough
}
Но если я использую Math.Round, могу ли я всегда быть уверен, что результирующее значение будет согласованным, т. Е. Всегда ли следующий Assert будет успешным, даже если округленное значение является значением, которое не может быть точно представлено двойным?
public static void TestRound(double value1, double value2, int decimals)
{
double roundedValue1 = Math.Round(value1, decimals);
double roundedValue2 = Math.Round(value2, decimals);
string format = "N" + decimals.ToString();
if (roundedValue1.ToString(format) == roundedValue2.ToString(format))
{
// They rounded to the same value, was the rounding exact?
Debug.Assert(roundedValue1 == roundedValue2);
}
}
Если нет, предоставьте контрпример.
EDIT
Спасибо astander за контрпример, сгенерированный грубой силой, который доказывает, что результат не является "последовательным" в общем случае. Этот контрпример имеет 16 значащих цифр в округленном результате - он также дает сбой таким же образом при таком масштабировании:
double value1 = 10546080000034341D;
double value2 = 10546080000034257D;
int decimals = 0;
TestRound(value1, value2, decimals);
Однако меня также интересовало бы более математическое объяснение. Дополнительные бонусы за любой из более математических Stackoverflowers, которые могут сделать любое из следующего:
Найдите контрпример, в котором округленный результат содержит менее 16 значащих цифр.
Определите диапазон значений, для которых округленный результат будет всегда быть «последовательным», как определено здесь (например, все значения, где число значащих цифр в округленном результате
Предоставить алгоритмический метод для генерации контрпримеров.