MySQL с использованием FLOOR приводит к неожиданному результату

0

В MySQL у меня есть следующая формула:

SELECT FLOOR(38774/184*126*23+0.5);

Что, к моему удивлению, дает 610690 хотя результат формулы внутри FLOOR является 610691, В Excel это работает просто отлично и дает мне желаемый результат, также в любом калькуляторе, который я использую на macOS или Windows.

Я что-то пропускаю здесь? Есть ли объяснение этому поведению и, что более важно, как я могу предотвратить это?

TylerDurden
источник

Ответы:

0

Вычисления с плавающей точкой довольно забавная вещь: Попробуйте это:

SELECT FLOOR(38774/184*126*23+0.5);
SELECT FLOOR(38774*126*23/184+0.5);
SELECT FLOOR(ROUND(38774/184*126*23+0.5, 3));

Что тут происходит?

Наиболее важной частью является то, что 38774/184 не имеет точного представления в виде числа с плавающей запятой - вы всегда и немного меньше. С остальными вычислениями, составляющими ровно 610691, вы в итоге чуть-чуть не дотянете до 610691, что приведет FLOOR 610690.

Вторая строка избегает этого, перегруппировывая части вычисления в последовательность, которая имеет точное представление в виде числа с плавающей запятой.

Третья строка использует ROUND отрезать очень маленькую разницу, тем самым делая FLOOR работать как положено.

Практическое правило. Числа с плавающей точкой не дают точных результатов. Хотя они чаще всего являются «достаточно точными», существуют крайние случаи, когда это не так. Вы просто нажали один, и довольно обычный: преобразование обратно в целое число через FLOOR или же CEIL,

Теперь, чтобы ответить на ваш вопрос: из опыта я бы приказал вычислить так, как это делает 2-я строка - большие числа (в диапазоне INT32) с большей вероятностью приведут к правильному FLOOR, Если вычислительная стоимость приемлема для вас, я бы пошел на

SELECT FLOOR(ROUND(38774*126*23/184+0.5, 3));

и будьте вполне уверены, что все крайние случаи покрыты.

Eugen Rieck
источник
Я на самом деле использую это, чтобы имитировать поведение округления Excel во встроенной базе данных MySQL, где я не могу изменить режим. Как работает ROUND повлиять на это? Мой подход был похож FLOOR(x*100+0.5)/100, Так что в вашем примере с ROUND следующее округлено «неправильно»: FLOOR(ROUND(4.994999999*100+0.5,3))/100=5.00 вместо 4.99
TylerDurden