Бухгалтерское округление

Вопросы программирования и использования среды Lazarus.

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

Ответить
krab
постоялец
Сообщения: 108
Зарегистрирован: 17.02.2010 17:23:08

Бухгалтерское округление

Сообщение krab »

Как выполнить бухгалтерское округление в Lazarus?
alex_rain
новенький
Сообщения: 26
Зарегистрирован: 12.02.2010 05:56:32
Откуда: Russia

Сообщение alex_rain »

Так же, как и в Delphi:
Бухгалтерское - с помощью Round,
Математическое - с помощью Trunc, например так:

Код: Выделить всё

function RoundDoubleDigit(Value: Double; Digit: Integer): Double;
var
  Factor: Double;
begin
  Factor := Exp(Digit * Ln(10));
  if Value < 0 then
    Result := Trunc(Value * Factor - 0.5) / Factor
  else
    Result := Trunc(Value * Factor + 0.5) / Factor;
end;


Lazarus 0.9.29 FPC 2.5.1 Win32

Добавлено спустя 1 час 1 минуту 30 секунд:
Пример бухгалтерского округления:

Код: Выделить всё

function BuhDoubleDigit(Value: Double; Digit: Integer): Double;
var
  Factor: Double;
begin
  Factor := Exp(Digit * Ln(10));
  Value := Value * Factor;
  Result := Round(Value) / Factor;
end;


Lazarus 0.9.29 FPC 2.5.1 Win32
Аватара пользователя
VirtUX
энтузиаст
Сообщения: 880
Зарегистрирован: 05.02.2008 09:52:19
Откуда: Крым, Алушта

Сообщение VirtUX »

alex_rain писал(а):BuhDoubleDigit

Не всегда округляет 5 в большую сторону! Как это побороть?
Vadim
долгожитель
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Сообщение Vadim »

А если использовать тип Currency?
Аватара пользователя
Nik
энтузиаст
Сообщения: 573
Зарегистрирован: 03.02.2006 23:08:09
Откуда: Киров
Контактная информация:

Сообщение Nik »

Взято из DelphiWorld:

Код: Выделить всё

function RoundCurrency(value: currency): currency;
var
  x, y: Double;
begin
  x := int(value * 100);
  y := Frac(value * 100);
  if y >= 0.5 then
    x := x + 1;
  result := x / 100;
end;


Когда-то писал похожую функцию, но с ходу не нашёл на свалке старых исходников.
Аватара пользователя
VirtUX
энтузиаст
Сообщения: 880
Зарегистрирован: 05.02.2008 09:52:19
Откуда: Крым, Алушта

Сообщение VirtUX »

Vadim писал(а):А если использовать тип Currency?

В Currency только 4 знака после запятой, а мне нужно 15 знаков и возможность округлить до любого знака.

Добавлено спустя 2 минуты 51 секунду:
Вот:

Код: Выделить всё

function RoundN(X: Double; CountDlim: SmallInt): Double;
var
  delim: int64 =1;
  cd: Byte;
  td: Double;
begin
  if CountDlim < 0 then Exit;

  if CountDlim > 14 then cd := 15 else cd := CountDlim;

  if cd > 0 then delim:= 10**cd;

  td := X * delim;

  if frac(td) < 0.5 then Result := trunc(td) / delim
  else Result := ceil(td) / delim;
end;             

Но даже при этом раскладе 1.005 (при округлении до 2-х знаков) округляется в меньшую. 1 вместо 1.01
Отредактировал

Добавлено спустя 11 минут 19 секунд:
А после 11 знаков начинается полная ахинея :(
Vadim
долгожитель
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Сообщение Vadim »

VirtUX писал(а):а мне нужно 15 знаков

Ого!!! Уважаю... :)
alex_rain
новенький
Сообщения: 26
Зарегистрирован: 12.02.2010 05:56:32
Откуда: Russia

Сообщение alex_rain »

Не всегда округляет 5 в большую сторону! Как это побороть?

Но даже при этом раскладе 1.005 (при округлении до 2-х знаков) округляется в меньшую. 1 вместо 1.01

Бухгалтерское (банковское) округление работает таким образом:
Все числа, у которых «отбрасываемая» цифра не равна 5, округляются по правилам математического округления.
А другие по следующему правилу:
– если цифра, которая стоит перед цифрой 5, четная, то округление осуществляется в меньшую сторону,
– если цифра, которая стоит перед цифрой 5, нечетная, то округление осуществляется в большую сторону.

Цитата из Википедии:
Банковское округление (англ. banker's rounding) — округление для этого случая происходит к ближайшему чётному. Это позволяет устранить систематическую ошибку округления при суммировании большого количества чисел. То есть, 2,5 → 2, 3,5 → 4.
Аватара пользователя
Astralis
новенький
Сообщения: 45
Зарегистрирован: 06.06.2007 20:33:05
Откуда: Tvercity-Annet
Контактная информация:

Сообщение Astralis »

Если цель состоит только в избавлении систематической ошибки округления при большом количестве суммирования (и других систематических ошибках), то лучший вариант - использовать рандомное округление

Код: Выделить всё

rndround(x) = x-floor(x)<random ? ceil(x) : floor(x)

многие алгоритмы динамического программирования, требующие дискретизации используют именно такое округление.
Аватара пользователя
VirtUX
энтузиаст
Сообщения: 880
Зарегистрирован: 05.02.2008 09:52:19
Откуда: Крым, Алушта

Сообщение VirtUX »

Лично я, решил не заморачиваться. Т.к. ни Float, ни Double, ни Currency не дают возможности использовать в полной мере многорязрядность. GMP тоже странновато себя ведет. Решил отложить на попозже, чтоб написать свой модуль по работе с БОЛЬШИМИ числами - может через годик, два.
Ответить