Мы застреваем с базой данных, которая (к сожалению), использует плавания вместо десятичных значений. Это делает округление немного трудного. Рассмотрите следующий пример (SQL Server T-SQL):
SELECT ROUND(6.925e0, 2) --> returns 6.92
ROUND
приводит в порядок круглую половину, но так как числа с плавающей точкой не могут точно представить десятичные числа, "неправильный" результат (с точки зрения конечного пользователя) отображен. Я понимаю, почему это происходит.
Я уже предложил два возможных решения (оба возврата плавания, которое является, к сожалению, также требованием):
SELECT CONVERT(float, ROUND(CONVERT(decimal(29,14), 6.925e0), 2))
SELECT ROUND(6.925e0 * 1000, -1) / 1000
Какой я должен выбрать? Есть ли некоторое лучшее решение? (К сожалению, мы не можем изменить типы поля в базе данных из-за некоторых унаследованных приложений, получающих доступ к тому же DB.)
Существует ли устойчивое решение для лучшей практики для этого (распространенный?) проблема?
(Очевидно, общая техника "округление дважды" не поможет здесь, с тех пор 6.925 уже округлен к трем десятичным разрядам - насколько это возможно в плавании.)
Ваше первое решение кажется более безопасным, а также похоже на концептуально более близкое к проблеме: как можно скорее преобразовать из числа с плавающей запятой в десятичное, произвести все соответствующие вычисления внутри десятичного типа, а затем выполнить в последнюю минуту преобразование обратно в float перед записью в БД.
Изменить: вам, скорее всего, все равно потребуется выполнить дополнительный раунд (например, до 3 знаков после запятой или что-то еще, что подходит для вашего приложения) сразу после получения значения с плавающей запятой и преобразования в десятичное, чтобы убедиться, что вы в конечном итоге получите десятичное значение, которое действительно было задумано. 6.925e0
, преобразованный в десятичный формат, снова вероятно (при условии, что десятичный формат имеет точность> 16 цифр) даст что-то, что очень близко, но не точно равно, 6.925
; дополнительный раунд позаботится об этом.
Второе решение не кажется мне надежным: что, если сохраненное значение для 6.925e0
окажется слишком маленьким из-за обычных проблем с двоичными числами с плавающей запятой? Затем после умножения на 1000
результат может все еще быть касанием под 6925
, так что шаг округления округляется в меньшую сторону, а не в большую. Если вы знаете, что ваше значение всегда имеет не более 3 цифр после точки, вы можете исправить это, выполнив дополнительный раунд после умножения на 1000, что-то вроде ROUND (ROUND (x * 1000, 0) , -1)
.
(Отказ от ответственности: хотя у меня есть большой опыт работы с проблемами с плавающей запятой и десятичной дробью в других контекстах, я почти ничего не знаю о SQL.)
Использование формат произвольной точности, такой как DECIMAL. Таким образом, вы можете оставить это на язык, чтобы сделать его правильным (или неправильным, в зависимости от обстоятельств).