System::Currency и C++ Builder

Я использую Embarcadero C++ Builder XE10 для создания приложения, которое выполняет некоторые денежные расчеты.

При попытке использовать тип данных System::Currency я столкнулся с парой проблем.

Q1: Почему не удается вычислить модуль при использовании оператора «%»?

System::Currency TaxValue = 1.665;
System::Currency Rest = 0;

// Correct result: Rest will be 0.005 when converted to double
Rest = TaxValue - ( TaxValue / 100 * 100 );

// Incorrect result: Rest is the same as TaxValue
Rest = TaxValue % 100;

Редактировать: я был полностью одурачен выводом отладчика, где значение System::Currency показано в его целочисленном представлении, умноженном на 10.000.

То, что я действительно ожидал увидеть, было:

Rest = (TaxValue * 10000) % 100;

==> Теперь Rest равен 50, чего я и ожидал.

Q2: Как я могу сделать правильное банковское округление с типом данных Curreny?

Примеры:

1.664 => 1.66
1.665 => 1.67
1.666 => 1.67

Хервиг


person Herwig    schedule 24.04.2017    source источник
comment
System::Currency не является частью стандартного C++. Вы случайно не компилируете как C++/CLI вместо C++?   -  person Algirdas Preidžius    schedule 24.04.2017
comment
Я знаю. System::Currency является частью Borland/Embarcadero Framework.   -  person Herwig    schedule 24.04.2017
comment
О, я вижу. В таком случае, пожалуйста, уточните, что такое неверный результат. т.е. Чего вы ожидали, что получили, почему ожидали, что код будет вести себя по-другому.   -  person Algirdas Preidžius    schedule 24.04.2017
comment
Результат был правильным, поэтому мой вопрос был незаконным (другими словами, глупым). Меня ввел в заблуждение вывод отладчика, где каждое значение System::Currency отображается в целочисленном представлении, умноженном на 10 000.   -  person Herwig    schedule 26.04.2017


Ответы (1)


Q1: Почему не удается вычислить модуль при использовании оператора «%»?

System::Currency работает с точностью до 4 знаков после запятой. В вашем примере вместо этого требуется 2-значная точность.

System::Currency поддерживает свою точность без ошибок округления за счет внутреннего умножения входных значений на 10000 и последующего использования целочисленной математики вместо математики с плавающей запятой для управления значениями.

Когда вы инициализируете TaxValue с помощью 1.665, его внутренний элемент Val (который является __int64) устанавливается в (1.665 * 10000) = 16650. Вот как выглядит этот конструктор:

__fastcall Currency(double val) {Val = _roundToInt64(10000 * val);}

Когда вы затем выполняете TaxValue % 100, оператор % реализуется следующим образом:

Currency __fastcall operator %(int rhs) const
{return Currency(static_cast<int>(Val % (10000 * (__int64)rhs))) / 10000;}

Первая часть создает временный объект Currency, который инициализируется значением int (16650 % (10000 * 100)) = 16650, которое умножается на 10000 до 166500000 конструктором временного объекта:

__fastcall Currency(int val) {Val = 10000*(__int64)val;}

Затем вторая часть делит температуру на 10000. Оператор / реализован следующим образом:

Currency& __fastcall operator /=(const Currency& rhs)
{Val *= 10000; Val /= rhs.Val; return *this;}

Currency __fastcall operator /(int rhs) const
{Currency tmp(*this); return tmp /= Currency(rhs);}

Таким образом создается окончательный объект Currency, для которого Val установлено значение (166500000 * 10000) / (10000 * 10000) = 16650.

Когда это окончательное Currency затем присваивается Rest и преобразуется в double, значение делится на 10000, таким образом получается 1.665:

__fastcall operator double() const {return ((double)Val) / 10000;}

Q2: Как я могу сделать правильное банковское округление с типом данных Curreny?

Взгляните на функцию System::Round(), которая использует банковское округление.

Если вы хотите больше контролировать округление, используйте функцию System::Math::RoundTo() или найдите стороннюю функцию округления.

В StackOverflow есть несколько других вопросов, касающихся округления Currency, например:

Как заставить тип валюты Delphi постоянно округляться, как в Excel?

округление валюты

(System::Currency — это оболочка C++Builder для родного типа Delphi Currency).

person Remy Lebeau    schedule 25.04.2017