Float to Double conversion - лучшее утверждение в unit test?
Учитывая утверждения
float f = 7.1f;
double d = f;
Что мы можем утверждать в unit test о d?
Например, это не работает:
Console.WriteLine(d == 7.1d); // false
Console.WriteLine(d < 7.1d + float.Epsilon); // true by luck
Console.WriteLine(d > 7.1d - float.Epsilon); // false (less luck)
Лучший способ, который я нашел до сих пор, - это преобразовать значение обратно:
float f2 = (float)d;
Console.WriteLine(f2 == f); // true
Который был бы таким же, как грубый способ сказать
Console.WriteLine(d == 7.1f); // 7.1f implicitly converted to double as above
Этот вопрос НЕ о двойной и плавающей точности в целом, но действительно JUST о прагматичном вопросе о том, как unit test лучше всего описать границы d. В моем случае d является результатом преобразования, которое происходит в коде, генерируемом генерацией легкого кода. При тестировании этого генерации кода я должен делать утверждения об исходе этой функции, и это, наконец, сводится к простому вопросу выше.
Ответы
Ответ 1
В вашем "наилучшем способе" утверждается, что ваш сгенерированный код возвращает что-то, что находится в пределах погрешности float
, 7.1
. Это может быть то, что вы хотите проверить, и в этом случае продолжайте.
С другой стороны, вы можете утверждать, что ваш сгенерированный код возвращает конкретный результат литья 7.1f
в double
, и в этом случае вы могли бы сделать:
Console.WriteLine(d == (double)f);
Это более жестко - ваш тест утверждает, что d
находится в пределах небольшого диапазона, в то время как вышеупомянутый тест утверждает, что d
является конкретным значением.
Это действительно зависит от того, для чего вы будете использовать d
for. Если это случай, когда что-то пойдет не так, если это не точное значение, проверьте точное значение, но если он ОК, чтобы быть в пределах float
от значения, проверьте на float
.
Ответ 2
Чтобы сравнить два значения точки плавающей запятой ibm sugests для тестирования abs(a/b - 1) < epsilon
msnd заявляет, что свойство Epsilon
отражает наименьшее положительное значение, которое имеет значение в числовых операциях или сравнениях, когда значение экземпляра равно нулю.
так что вы должны проверить
Math.Abs(d/(double)f) - 1) < float.Epsilon)
Ответ 3
(float) d == f
.
Другой ответ предложил d == (double) f
, но это бесполезный тест, потому что (double) f
выполняет то же самое преобразование, которое d = f
выполняет неявно. Таким образом, единственное, что может быть проверено этим утверждением, заключается в том, нарушен ли какой-либо аспект реализации (например, компилятор реализовал одну из преобразований неправильно и иным образом отличается от другой), какой-то внешний механизм изменил d
или f
между присваиванием и утверждением, или исходный код был сломан, так что d
не был ни double
, ни float
, ни любым типом, который мог бы удерживать значение f
в точности или назначение d = f
не выполнялось.
В общем случае мы не ожидаем ошибки с плавающей запятой, поскольку в каждой нормальной реализации с плавающей запятой преобразование с более узкой точностью в более высокую точность одного и того же радиуса не имеет ошибки, поскольку более широкая точность может представлять каждое значение более узкий точность может. В необычных ситуациях более широкий формат с плавающей запятой может иметь меньший диапазон экспонентов. Только в этом случае или в извращенных форматах с плавающей точкой преобразование в более широкий формат может привести к изменению значения. В этих случаях выполнение такого же преобразования не обнаружит изменения.
Вместо этого мы конвертируем из более широкого формата обратно в более узкий формат. Если d
отличается от f
, это преобразование имеет шанс обнаружить ошибку. Например, предположим, что f
содержит 0x1p-1000, но по какой-то причине он не представлен в формате d
, поэтому он был округлен до нуля. Затем (float) d == f
оценивается до (float) 0 == 0x1p-1000
, затем до 0 == 0x1p-1000
, затем до false
. Кроме того, этот тест может обнаруживать те же ошибки, что и другое предложение: сломанная реализация, изменение d
или f
, неправильный тип d
и отсутствие назначения d = f
.
Кроме того, какие ошибки вы пытаетесь обнаружить с помощью утверждения здесь?