Сравнение вещественных чисел
В связи с текущей возней с EFCore пришлось «освежить» понимание множества базовых вещей.
Одна из этих вещей — вещественные числа.
Задача
1. Добавляем запись с FLOAT колонкой.
2. Выбираем эту запись и сравниваем значение FLOAT колонки с ожидаемым значением. Ожидаемое float-значение передается в запросе в виде текста.
Выглядит это как-то так:
1 2 3 4 5 6 | const string c_valueSource= "-3.40282347E+38" ; const float c_valueTarget= float .MinValue; //-3.40282347E+38 System.Int64? testID=Helper__InsertRow(db,c_valueSource,c_valueTarget); var recs=db.testTable.Where(r => ( float )( object )c_valueSource==r.COL_TARGET && r.TEST_ID==testID); |
В конечном итоге, на сервер уезжает запрос вида:
1 2 3 | SELECT "t" . "TEST_ID" , "t" . "COL_VARCHAR_128" , "t" . "COL2_FLOAT" FROM "TEST_MODIFY_ROW2" AS "t" WHERE (-3.40282347E+38 = "t" . "COL2_FLOAT" ) AND ( "t" . "TEST_ID" = CAST (:__testID_0 AS BIGINT )) |
То есть, вроде все путем. Но сервер возвращает 0 записей.
Почему, Карл?
Потому что сервер (FB3) приведет левую и правую часть к double и будет сравнивать значение -3.4028234700000002e+38 с -3.4028234663852886e+38.
То есть, литера «-3.40282347E+38» преобразуется в более менее похожее значение «-3.4028234700000002e+38», а вот значение колонки (float.MinValue) преобразуется в -3.4028234663852886e+38.
А вот если ожидаемое значение передать в виде float-параметра, то все нормально — запрос возвращает нашу запись.
1 2 3 4 5 6 7 8 | const string c_valueSource= "-3.40282347E+38" ; const float c_valueTarget= float .MinValue; System.Int64? testID=Helper__InsertRow(db,c_valueSource,c_valueTarget); float vv=c_valueSource; var recs=db.testTable.Where(r => ( float )( object )vv==r.COL_TARGET && r.TEST_ID==testID); |
Потому что, Карл, сервер сравнивает float-значения как есть, без каких-либо преобразований.
Вот такая вот трава ботва.
PS. Пока писал эту заметку, ласково вспоминал разработчиков WordPress, которые в своем обновлении 5.7 сломали «preview» и вставку картинок.