![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Операции с Currency
Дмитрий СС © (2014-02-07 19:34:00)Добрый день.
Пусть есть переменная Foo: Currency.
Подскажите, пожалуйста, как наиболее правильно получить:
1. Целую часть (в integer или в currency).
2. Дробную часть (в currency).
3. Округлить (в currency).
Конечно, есть множество функций Trunc, Round, и т. п., но есть сомнения, что они не очень предназначены для Currency.
И вопрос в тему.
Пусть X: Float, N: Integer; (Float - это какой-нибудь тип с плавающей точкой, например, Extended).
Всегда ли вернется TRUE?
X := N; // либо какие-либо операции над X, приводящие к значению N в математическом смысле.
Writeln(N = Trunc(X));
Дмитрий СС (2014-02-12 02:27:00) [1]
> Dimka Maslov © (11.02.14 15:52) [24]
Мы отказались (отказываемся) от этого из-за того что трудно воспринимать - слишком много нулей.
Inovet (2014-02-11 16:26:00) [2]> [25] Inovet © (11.02.14 16:24)
Или что? Вместо рублей копейки? Ну тогда да.
Inovet (2014-02-11 16:24:00) [3]> [24] Dimka Maslov © (11.02.14 15:52)
Шутишь? Квадратный рубль совсем оставим в покое, но и с простым рублём тоже не получится.
Dimka Maslov (2014-02-11 15:52:00) [4]Лучше сращу всё считать в копейках. Во-первых будет влезать в диапазон int64. Во вторых в копейках считать выгоднее: к примеру если возвести 1 рубль в квадрат - рубль и получим, а если 100 копеек - получим 10000 копеек, а это 100 рублей!!!
Inovet (2014-02-11 10:59:00) [5]Ну т.е. что должно быть с этими пресловутыми копейками в операции
1,99*0,01
?
Inovet (2014-02-11 10:57:00) [6]> [21] Мистер Хэ (11.02.14 10:52)
Ну дык тут ничего не поможет, кроме увеличения точности, очевидно же. А для фиксированной точки ака целое всё как и для целого с некоторыми приведениями в нужных местах.
Мистер Хэ (2014-02-11 10:52:00) [7]> Inovet ©
> без потери точностии выхода за диапазонInt64.
Inovet (2014-02-11 10:51:00) [8]> [19] Inovet © (11.02.14 10:50)
> в исходниках
т.е. в
include\windows\rtl\
Inovet (2014-02-11 10:50:00) [9]> [18] Inovet © (11.02.14 10:46)
Да и что выдумывать, вот оно всё в исходниках в
syscurr.h
Inovet (2014-02-11 10:46:00) [10]Они в целых 64 бита, значит так:
a*b/10000
Незнакомец (2014-02-11 10:00:00) [11]По сути Currency это число с фиксированной точкой
Поэтому вся логика для них соответствует рутине с фиксированной точкой
Ситуация усложняется размером переменных :)
Если для фикс точки от Integer умножение реализуется через Int64, то в случае Currency наверное через Int128 :)
Незнакомец (2014-02-11 09:57:00) [12]> Дмитрий СС (11.02.14 01:10) [15]
Хз :)
Но видимо сложно :)
Дмитрий СС (2014-02-11 01:10:00) [13]Интересно, а как будет выглядеть умножение и деление без потери точности и выхода за диапазон Int64.
Незнакомец (2014-02-10 22:02:00) [14]> Дмитрий СС (10.02.14 20:41) [13]
Я думаю доступнее сразу кодом показывать
Нет, это не наработки
Дмитрий СС (2014-02-10 20:41:00) [15]
> Незнакомец (10.02.14 11:09) [11]
>
Благодарю. Это наработки или специально для темы написали?
Незнакомец (2014-02-10 11:12:00) [16]> И вопрос в тему.
> Пусть X: Float, N: Integer; (Float - это какой-нибудь тип
> с плавающей точкой, например, Extended).
>
> Всегда ли вернется TRUE?
>
> X := N; // либо какие-либо операции над X, приводящие к
> значению N в математическом смысле.
> Writeln(N = Trunc(X));
Нет
У single маленькая точность
Можешь прогнать тестовый цикл по всему диапазону чисел Integer и выявить, где данное условие не сработает
Незнакомец (2014-02-10 11:09:00) [17]> Подскажите, пожалуйста, как наиболее правильно получить:
>
> 1. Целую часть (в integer или в currency).
> 2. Дробную часть (в currency).
> 3. Округлить (в currency).
procedure CurrencyTrunc(var Result: Currency; const C: Currency); overload;
var
X: int64 absolute C;
R: int64 absolute Result;
begin
R := X - (X mod 10000);
end;
procedure CurrencyTrunc(var Result: integer; const C: Currency); overload;
var
X: int64 absolute C;
begin
Result := (X div 10000);
end;
procedure CurrencyFrac(var Result: Currency; const C: Currency); overload;
var
X: int64 absolute C;
R: int64 absolute Result;
begin
R := X mod 10000;
end;
procedure CurrencyRound(var Result: Currency; const C: Currency); overload;
const
OFFSETS: array[boolean] of int64 = (5000, -5000);
var
X: int64 absolute C;
Y: int64;
R: int64 absolute Result;
begin
Y := X + OFFSETS[X < 0];
R := Y - (Y mod 10000);
end;
Результат в var, а не результат функции потому, что во втором случае происходят преобразования через Extended, что приводит к потере точности и производительности
Inovet (2014-02-08 14:10:00) [18]> [9] Дмитрий СС (08.02.14 14:08)
Да не важно, должно везде всё сходиться.
Дмитрий СС (2014-02-08 14:08:00) [19]
> Нифига себе. Люди выворачиваются, чтобы всё до копейки сходилось,
> а у тебя «не сыграет».
Отставить панику, я тоже выворачиваюсь, но в данном случае речь идет о расчете комиссии по процентам. И в какую сторону я округлю ее роли не сыграет. Все остальные суммы, зависящие от комиссии находятся с помощью сложения и вычитания ПОСЛЕ округления, поэтому суммы все сойдутся.
> Юрий Зотов © (08.02.14 12:41) [6]
> В одном проекте этот вопрос был решен так:
Здесь придется постоянно следить, где округленные данные, а где нет и не смешивать их. Хотя учитывая, что процессор и так переводит Currency-числа в Extended при расчетах - получаются что так как вы делают все:)
Юрий Зотов (2014-02-08 13:07:00) [20]
> Inovet © (08.02.14 12:44) [7]
Там сохранялось в БД, а поле было прописано как (10,2) - поэтому округление при сохранении шло автоматом.