Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents
Bookmark and Share

Part I - Foundations
  Chapter 1 Delphi 7 and Its IDE
  Chapter 2 The Delphi Programming Language
  Chapter 3 The Run-Time Library
  Chapter 4 Core Library classes
  Chapter 5 Visual Controls
  Chapter 6 Building the User Interface
  Chapter 7 Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 The Architecture of Delphi Applications
  Chapter 9 Writing Delphi Components
  Chapter 10 Libraries and Packages
  Chapter 11 Modeling and OOP Programming (with ModelMaker)
  Chapter 12 From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 Delphi's Database Architecture
  Chapter 14 Client/Server with dbExpress
  Chapter 15 Working with ADO
  Chapter 16 Multitier DataSnap Applications
  Chapter 17 Writing Database Components
  Chapter 18 Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 Internet Programming: Sockets and Indy
  Chapter 20 Web Programming with WebBroker and WebSnap
  Chapter 21 Web Programming with IntraWeb
  Chapter 22 Using XML Technologies
  Chapter 23 Web Services and SOAP
  Chapter 24 The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 Delphi for .NET Preview: The Language and the RTL
  Appendix A Extra Delphi Tools by the Author
  Appendix B Extra Delphi Tools from Other Sources
  Appendix C Free Companion Books on Delphi
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

Previous Section Next Section

What About Currency Conversions?

Converting currencies is not exactly the same as converting measurement units, because currency rates change constantly. In theory, you can register a conversion rate with Delphi's conversion engine. From time to time, you check the new rate of exchange, unregister the existing conversion, and register a new one. However, keeping up with the actual rate means changing the conversion so often that the operation might not make a lot of sense. Also, you'll have to triangulate conversions: You must define a base unit (probably the U.S. dollar if you live in America) and convert to and from this currency even if you're converting between two different currencies.

It's more interesting to use the engine to convert member currencies of the euro, for two reasons. First, conversion rates are fixed (until the single euro currency takes over). Second, the conversion among euro currencies is legally done by converting a currency to euros first and then from the euro amount to the other currency—the exact behavior of Delphi's conversion engine. There is one small problem: You should apply a rounding algorithm at every step of the conversion. I'll consider this problem after I've provided the base code for integrating euro currencies with the Delphi conversion engine.


The ConvertIt demo available among the Delphi examples provides support for euro conversions, using a slightly different rounding approach, still not as precise as demanded by the European currency conversion rules. I've decided to keep this example, because it is instructive in showing how to create a new measurement system.

The example, called EuroConv, teaches how to register any new measurement unit with the engine. Following the template provided by the StdConvs unit, I've created a new unit (called EuroConvConst). In the interface portion, I've declared variables for the family and the specific units, as follows:

  // Euro Currency Conversion Units
  cbEuroCurrency: TConvFamily;
  cuEUR: TConvType;
  cuDEM: TConvType; // Germany
  cuESP: TConvType; // Spain
  cuFRF: TConvType; // France
  // and so on...

The implementation portion of the unit defines constants for the various official conversion rates:

  DEMPerEuros = 1.95583;
  ESPPerEuros = 166.386;
  FRFPerEuros = 6.55957;
  // and so on...

Finally, the unit initialization code registers the family and the various currencies, each with its own conversion rate and a readable name:

  // Euro Currency's family type
  cbEuroCurrency := RegisterConversionFamily('EuroCurrency');
  cuEUR := RegisterConversionType(
    cbEuroCurrency, 'EUR', 1);
  cuDEM := RegisterConversionType(
    cbEuroCurrency, 'DEM', 1 / DEMPerEuros);
  cuESP := RegisterConversionType(
    cbEuroCurrency, 'ESP', 1 / ESPPerEuros);
  cuFRF := RegisterConversionType(
    cbEuroCurrency, 'FRF', 1 / FRFPerEuros);

The engine uses as a conversion factor the amount of the base unit necessary to obtain the secondary units, with a constant like MetersPerInch, for example. The standard rate of euro currencies is defined the opposite way. For this reason, I've kept the conversion constants with the official values (like DEMPerEuros) and passed them to the engine as fractions (1/DEMPerEuros).

Having registered this unit, you can now convert 120 German marks to Italian liras as follows:

Convert (120, cuDEM, cuITL)

The demo program does a little more: It provides two list boxes with the available currencies, extracted as in the previous example, and edit boxes for the input value and final result. You can see the form at run time in Figure 3.4.

Click To expand
Figure 3.4: The output of the EuroConv unit, showing the use of Delphi's conversion engine with a custom measurement unit

The program works nicely but is not perfect, because the proper rounding is not applied; you should round not only the final result of the conversion but also the intermediate value. Using the conversion engine to accomplish this rounding directly is not easy. The engine allows you to provide either a custom conversion function or a conversion rate. But writing identical conversion functions for all the currencies seems like a bad idea, so I've decided to take a different path. (You can see examples of custom conversion functions in the StdConvs unit, in the portion related to temperatures.)

In the EuroConv example, I've added to the unit with the conversion rates a custom EuroConv function that does the proper conversion. Simply calling this function instead of the standard Convert function does the trick (and I see no drawback to this approach, because in such programs you'll rarely mix currencies with distances or temperatures). As an alternative, I could have inherited a new class from TConvTypeFactor, providing a new version of the FromCommon and ToCommon methods; or I could have called the overloaded version of the RegisterConversionType that accepts these two functions as parameters. However, neither of these techniques would have allowed me to handle special cases, such as the conversion of a currency to itself.

This is the code of the EuroConv function, which uses the internal EuroRound function to round to the number of digits specified in the Decimals parameter (which must be between 3 and 6, according to the official rules):

  TEuroDecimals = 3..6;
function EuroConvert (const AValue: Double;
  const AFrom, ATo: TConvType;
  const Decimals: TEuroDecimals = 3): Double;
  function EuroRound (const AValue: Double): Double;
    Result := AValue * Power (10, Decimals);
    Result := Round (Result);
    Result := Result / Power (10, Decimals);
  // check special case: no conversion
  if AFrom = ATo then
    Result := AValue
    // convert to Euro, then round
    Result := ConvertFrom (AFrom, AValue);
    Result := EuroRound (Result);
    // convert to currency then round again
    Result := ConvertTo (Result, ATo);
    Result := EuroRound (Result);

Of course, you might want to extend the example by providing conversion to other non-euro currencies, eventually picking the values automatically from a website. I'll leave this as a rather complex exercise for you.

Previous Section Next Section



Copyright © 2004-2016 "Delphi Sources". Delphi Programming Guide
     Twitter     Facebook