“Округлите в большую сторону половину” на значениях с плавающей точкой

Мы застреваем с базой данных, которая (к сожалению), использует плавания вместо десятичных значений. Это делает округление немного трудного. Рассмотрите следующий пример (SQL Server T-SQL):

SELECT ROUND(6.925e0, 2)   --> returns 6.92

ROUND приводит в порядок круглую половину, но так как числа с плавающей точкой не могут точно представить десятичные числа, "неправильный" результат (с точки зрения конечного пользователя) отображен. Я понимаю, почему это происходит.

Я уже предложил два возможных решения (оба возврата плавания, которое является, к сожалению, также требованием):

  1. Преобразуйте в тип данных decimal перед округлением: SELECT CONVERT(float, ROUND(CONVERT(decimal(29,14), 6.925e0), 2))
  2. Умножьтесь, пока третья цифра не имеет на левой стороне десятичной точки (т.е. точно представлена), и затем сделайте округление: SELECT ROUND(6.925e0 * 1000, -1) / 1000

Какой я должен выбрать? Есть ли некоторое лучшее решение? (К сожалению, мы не можем изменить типы поля в базе данных из-за некоторых унаследованных приложений, получающих доступ к тому же DB.)

Существует ли устойчивое решение для лучшей практики для этого (распространенный?) проблема?

(Очевидно, общая техника "округление дважды" не поможет здесь, с тех пор 6.925 уже округлен к трем десятичным разрядам - насколько это возможно в плавании.)

7
задан Heinzi 10 March 2010 в 18:37
поделиться

2 ответа

Ваше первое решение кажется более безопасным, а также похоже на концептуально более близкое к проблеме: как можно скорее преобразовать из числа с плавающей запятой в десятичное, произвести все соответствующие вычисления внутри десятичного типа, а затем выполнить в последнюю минуту преобразование обратно в float перед записью в БД.

Изменить: вам, скорее всего, все равно потребуется выполнить дополнительный раунд (например, до 3 знаков после запятой или что-то еще, что подходит для вашего приложения) сразу после получения значения с плавающей запятой и преобразования в десятичное, чтобы убедиться, что вы в конечном итоге получите десятичное значение, которое действительно было задумано. 6.925e0 , преобразованный в десятичный формат, снова вероятно (при условии, что десятичный формат имеет точность> 16 цифр) даст что-то, что очень близко, но не точно равно, 6.925 ; дополнительный раунд позаботится об этом.

Второе решение не кажется мне надежным: что, если сохраненное значение для 6.925e0 окажется слишком маленьким из-за обычных проблем с двоичными числами с плавающей запятой? Затем после умножения на 1000 результат может все еще быть касанием под 6925 , так что шаг округления округляется в меньшую сторону, а не в большую. Если вы знаете, что ваше значение всегда имеет не более 3 цифр после точки, вы можете исправить это, выполнив дополнительный раунд после умножения на 1000, что-то вроде ROUND (ROUND (x * 1000, 0) , -1) .

(Отказ от ответственности: хотя у меня есть большой опыт работы с проблемами с плавающей запятой и десятичной дробью в других контекстах, я почти ничего не знаю о SQL.)

6
ответ дан 7 December 2019 в 05:21
поделиться

Использование формат произвольной точности, такой как DECIMAL. Таким образом, вы можете оставить это на язык, чтобы сделать его правильным (или неправильным, в зависимости от обстоятельств).

0
ответ дан 7 December 2019 в 05:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: