Округление как в Excel

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Округление как в Excel

Сообщение hovadur » 27.12.2014 22:25:40

В fpc есть функция округления "как в Excel": SimpleRoundTo:
Код: Выделить всё
function SimpleRoundTo(const AValue: Extended; const Digits: TRoundToRange = -2): Extended;
var
  RV : Extended;
begin
  RV := IntPower(10, -Digits);
  if AValue < 0 then
    Result := Trunc((AValue*RV) - 0.5)/RV
  else
    Result := Trunc((AValue*RV) + 0.5)/RV;
end;
\0

Но она не всегда работает как надо, например, для значения -2.235 функция округлит до -2.23.
Чтобы такого не происходило, нужно в нее добавить значение 1/MaxInt:
Код: Выделить всё
function SimpleRoundTo(const AValue: Extended; const Digits: TRoundToRange = -2): Extended;
var
  RV : Extended;
begin
  RV := IntPower(10, -Digits);
  if AValue < 0 then
    Result := Trunc(((AValue-1/MaxInt)*RV) - 0.5)/RV
  else
    Result := Trunc(((AValue+1/MaxInt)*RV) + 0.5)/RV;
end;
\0

И тогда этот тест выполнится, как надо:
Код: Выделить всё
procedure TTestCommon.TestRound;
const
  CFLOAT: array[0..11] of Extended =
    (0.5, 1.5, 2.45, 2.5, 2.55, 3.45, 3.5, 445.185, 38.025, 3.55, -2.564, -2.235);
  CCUR: array[0..11] of Extended =
    (0.5, 1.5, 2.45, 2.5, 2.55, 3.45, 3.5, 445.19, 38.03, 3.55, -2.56, -2.24);
var
  i: Integer;
begin
  for i := Low(CFLOAT) to High(CFLOAT) do
    CheckEquals(CCUR[i], SimpleRoundTo(CFLOAT[i]));
end;

Эта функция уже проверена на вычислениях в течение 8 лет бухгалтерского учета и работает. Почему до сих пор в fpc, delphi нет подобной простой функции? Хотя, что я говорю, она конечно же есть, но дает неверные значения для некоторых аргументов и надо, надо, надо делать ПАТЧ. ПАТЧ! Конечно же ПАТЧ!
Но патч не примут, потому что MaxInt меньше, чем Extended и на граничных значениях Digits что-то снова не будет идти как надо. Почему в жизни так все сложно?!
Последний раз редактировалось hovadur 28.12.2014 00:43:42, всего редактировалось 1 раз.
hovadur
постоялец
 
Сообщения: 116
Зарегистрирован: 31.01.2013 15:50:41

Re: Округление как в Excel

Сообщение скалогрыз » 27.12.2014 23:23:31

хм...
а если так:
Код: Выделить всё
function SimpleRoundTo(const AValue: Extended; const Digits: TRoundToRange = -2): Extended;
var
  RV : Extended;
begin
  RV := IntPower(10, -Digits);
  if AValue < 0 then
    Result := -Trunc((abs(AValue)*RV) + 0.5)/RV
  else
    Result := Trunc((AValue*RV) + 0.5)/RV;
end;
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Округление как в Excel

Сообщение hovadur » 27.12.2014 23:41:18

скалогрыз писал(а):а если так:

Если выполнить тест TestRound, предложенный мною выше, то он упадет на значении 445.19.
hovadur
постоялец
 
Сообщения: 116
Зарегистрирован: 31.01.2013 15:50:41

Re: Округление как в Excel

Сообщение скалогрыз » 28.12.2014 03:20:49

hovadur писал(а):Если выполнить тест TestRound, предложенный мною выше, то он упадет на значении 445.19.

ага, т.е. и стандартная SimpleRoundTo() (из Math) тоже упадёт.?! код же для положительных чисел не изменился.

в чём конкретнее проблема, в округлении отрицательных чисел, или в точности округления?
В первом посте не хватает строчки: "а ожидаемый результат такой..."
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Округление как в Excel

Сообщение hovadur » 28.12.2014 08:19:27

скалогрыз писал(а):ага, т.е. и стандартная SimpleRoundTo() (из Math) тоже упадёт.?!

Черт, извини. Упадет, если поставить SetRoundMode(rmDown). Если убрать, то твоя функция упадет все так же на значении -2.24 (я проводил эксперименты с SetRoundMode).
скалогрыз писал(а):В первом посте не хватает строчки: "а ожидаемый результат такой..."

Надо, чтобы предложенный тест выполнялся как надо.
hovadur
постоялец
 
Сообщения: 116
Зарегистрирован: 31.01.2013 15:50:41

Re: Округление как в Excel

Сообщение скалогрыз » 28.12.2014 09:09:36

hovadur писал(а):Надо, чтобы предложенный тест выполнялся как надо.

что-то (ассемблер?) мне подсказывает что Trunc на RoundingMode забивает самым чудным образом, принудительно выставляя нужные ему FPU флаги.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Округление как в Excel

Сообщение Sergei I. Gorelkin » 28.12.2014 12:19:03

Нюанс в том, что RoundingMode влияет на все без исключения операции с плавающей точкой, а не только на Round().
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5

Рейтинг@Mail.ru