Добрый день!
возникла проблема с арифметическим округлением SimpleRoundTo(5.055,-2) - получил 5.05 вместо ожидаемого 5.06.
Почитал много вопросов связанных с этой проблемой, нашел статью:
"Загадки округления" http://www.delphikingdom.com/asp/viewitem.asp?catalogID=1217
в данной статье предлагается функция "DecimalRoundExt" написанная неким "John Herbster", но поиски данного алгоритма так и не увенчались успехом!
Может кто нибудь подскажет где достать этот алгоритм, или "верно работающий" его аналог арифметического округления (того которому обучали в школах)?
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
22-05-2007 02:41 | Сообщение от автора вопроса
Все, разобрались...
сошлись на том что:
5,23495 = 5,23
5,0645 = 5,06
5,06501 = 5,07
5,065 = 5,07
тоесть все что после третего знака от запятой - отбрасываем, и после этого последний знак округляем...
Тоесть так как говорили Уважаемые Geo и Николай Белюченко
Если не удастся объяснить заказчику про математику, то придется Вам самому писать функцию округления :-)
Говоришь: "Глянул я, как вы считаете - неправильно абсолютно. Все считают иначе". Если согласятся - то в счете нужно будет указать "Аудит и консалтинговые услуги". Если нет - "Разработка эксклюзивно-заказного ПО".
>>> 5,0645 до сотых берем только значащую циру после сотых да?
Ну, на практике получается и так. Хотя смысл несколько другой.
Мы хотим округлить число до второго знака после запятой. То есть наше число больше 5,06 и меньше 5,07. Разница в 0,01. Какую цифру ставить? По правилу мы должны смотреть на оставшуюся часть числа: она больше половины диапазона (0,005) или меньше. Если больше либо равна половине, то округляем вверх. Если меньше, то вниз.
В Вшем случае оставшаяся часиь равна 0,0045. А это меньше, чем 0,005. Значит округляем вниз.
>>> просто было число 5,23495 - я округлял как 5,23... но заказчик сказал что правильно будет 5,24...
С точки зрения математики, заказчик не прав. Но с точки зрения правил работы с клиентом, заказчик всегда прав ;-) Если не удастся объяснить заказчику про математику, то придется Вам самому писать функцию округления :-)
тоесть при округлении числа
5,0645 до сотых берем только значащую циру после сотых да?
тоесть 5,0645 смотрим третий знак, а остальное выкидываем...
получается 5,064 значащая цифра = 4, значит округляем как 5,06...
Странно ну ладна...
просто было число 5,23495 - я округлял как 5,23... но заказчик сказал что правильно будет 5,24...
Совсем запутался, надо открывать школьный учебник за третий клас и идти к заказчику разбирать примерчики! :)
последний знак =5
значит убираем, а знаку перед ним прибавляем +1...
получаем
5,065
далее последний знак опять = 5
значит убираем, а знаку перед ним прибавляем +1...
итог:5,07
Может кто нибудь подскажет где достать этот алгоритм, или "верно работающий" его аналог арифметического округления (того которому обучали в школах)?
зато из 5,0645 получает 5,06... а надо 5,07
В какой школе обучали, что 5,0645 при округлении дает 5,07?
По крайней мере 5.055 в 5.06 нормально переводит.
зато из 5,0645 получает 5,06... а надо 5,07
Так что этот метод тоже не подходит..
В моем случае в задаче получаются числа с 6 знаками после запятой, и их надо правильно округлить до сотых...
А это был приведен пример первый попавшийся под руку...
помоему в статье "Загадки округления" - рассматривался этот способ...
И из всех рассмотренных хотелось бы найти способ предоставленный "John Herbster"
Я думаю проблема в типе Double не знаю почему, но данный тип очень популярен в то время как со процессор работает только с типом Extended (возможно ошибаюсь но кажется это так).
Предлагаю просто функцию SimpleRoundTo перевести на тип Extended
function SimpleRoundTo(const AValue: Extended; const ADigit: TRoundToRange = -2): Extended;
var
LFactor: Extended;
begin
LFactor := IntPower(10, ADigit);
if AValue < 0 then
Result := Trunc((AValue / LFactor) - 0.5) * LFactor
else
Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
end;
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.