Jeroen Wiert Pluimers
Jeroen Wiert Pluimers

Reputation: 24493

Strange Format result in Delphi XE2 when using Currency data types

In Delphi XE2, I bumped against a strange formatting difference when formatting Currency. Using Double works as expected.

It looks that when using %F or %N (floating point or numeric) you always get 3 decimal digits, even if you request fewer.

I wrote the below quick DUnit test case, and will investigate further tomorrow.

This particular project cannot be changed to anything other than Delphi XE2 (big corporates are not flexible in what tools they use), so I'm looking for a solution that solves this in Delphi XE2.

In the mean time: what are your thoughts?

unit TestSysUtilsFormatUnit;

interface

uses
  TestFramework, System.SysUtils;

type
  TestSysUtilsFormat = class(TTestCase)
  strict private
    DoublePi: Double;
    CurrencyPi: Currency;
    FloatFormat: string;
    NumericFormat: string;
    Expected_Format_F: string;
    Expected_Format_N: string;
  public
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure Test_Format_F_Double;
    procedure Test_Format_F_Currency;
    procedure Test_Format_N_Double;
    procedure Test_Format_N_Currency;
  end;

implementation

procedure TestSysUtilsFormat.Test_Format_F_Double;
var
  ReturnValue: string;
begin
  ReturnValue := System.SysUtils.Format(FloatFormat, [DoublePi]);
  Self.CheckEqualsString(Expected_Format_F, ReturnValue); // actual '3.1'
end;

procedure TestSysUtilsFormat.Test_Format_F_Currency;
var
  ReturnValue: string;
begin
  ReturnValue := System.SysUtils.Format(FloatFormat, [CurrencyPi]);
  Self.CheckEqualsString(Expected_Format_F, ReturnValue); // actual '3.142'
end;

procedure TestSysUtilsFormat.Test_Format_N_Double;
var
  ReturnValue: string;
begin
  ReturnValue := System.SysUtils.Format(NumericFormat, [DoublePi]);
  Self.CheckEqualsString(Expected_Format_N, ReturnValue); // actual '   3'
end;

procedure TestSysUtilsFormat.Test_Format_N_Currency;
var
  ReturnValue: string;
begin
  ReturnValue := System.SysUtils.Format(NumericFormat, [CurrencyPi]);
  Self.CheckEqualsString(Expected_Format_N, ReturnValue); // actual '3.142'
end;

procedure TestSysUtilsFormat.SetUp;
begin
  DoublePi := 3.1415;
  CurrencyPi := 3.1415;
  FloatFormat := '%.1f';
  Expected_Format_F := '3.1';
  NumericFormat := '%4.0n';
  Expected_Format_N := '   3';
end;

procedure TestSysUtilsFormat.TearDown;
begin
end;

initialization
  RegisterTest(TestSysUtilsFormat.Suite);
end.

Upvotes: 1

Views: 1331

Answers (2)

Jeroen Wiert Pluimers
Jeroen Wiert Pluimers

Reputation: 24493

This was a bug in early Delphi XE 2 versions in these methods:

function WideFormatBuf(var Buffer; BufLen: Cardinal; const Format;
  FmtLen: Cardinal; const Args: array of const;
  const AFormatSettings: TFormatSettings): Cardinal;

function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  FmtLen: Cardinal; const Args: array of const;
  const AFormatSettings: TFormatSettings): Cardinal;

Fails:

  • Embarcadero® RAD Studio XE2 Version 16.0.4256.43595 (Update 2)

(The odd thing is: that version indicates "no updates available" with starting the "check for updates")

I did not have time to check intermediate versions.

Works:

  • Embarcadero® RAD Studio XE2 Version 16.0.4429.46931 (Update 4))
  • Embarcadero® Delphi® XE2 Version 16.0.4504.48759 (Update 4 hotfix 1)

One of the things that XE2 Update 4 (with or without the hotfix) breaks is the creation of a standard (non-IntraWeb) unit test project.

This menu entry is missing: File -> New -> Other -> Unit Test -> Test Project.

As a reminder to myself, this is the skeleton code to quickly get started with the missing Test Project entry:

program UnitTest1;
{

  Delphi DUnit Test Project
  -------------------------
  This project contains the DUnit test framework and the GUI/Console test runners.
  Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
  to use the console test runner.  Otherwise the GUI test runner will be used by
  default.

}

{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}

uses
  Forms,
  TestFramework,
  GUITestRunner,
  TextTestRunner;

{$R *.RES}

begin
  Application.Initialize;
  if IsConsole then
    with TextTestRunner.RunRegisteredTests do
      Free
  else
    GUITestRunner.RunRegisteredTests;
end.

Upvotes: 0

Ken White
Ken White

Reputation: 125708

Posting this as an answer on the request of the asker in the comments to the question above.)

I can't reproduce the issue on either XE2 or XE3, with a plain console application. (It was just quicker to set up for me.)

Here's the code I used in it's entirely (on both XE2/XE3):

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils;

const
  DoublePi: Double = 3.1415;
  CurrencyPi: Currency = 3.1415;
  FloatFormat = '%.1f';
  NumericFormat = '%4.0n';
begin
  WriteLn(Format('Double   (.1f) : '#9 + FloatFormat, [DoublePi]));
  WriteLn(Format('Currency (.1f) : '#9 + FloatFormat, [CurrencyPi]));
  WriteLn(Format('Currency (4.0n): '#9 + NumericFormat, [CurrencyPi]));
  ReadLn;
end.

Here's the output from the XE2 run (Delphi® XE2 Version 16.0.4429.46931): :

Format Currency Console App Output

Upvotes: 1

Related Questions