# A Simple Function for Formatting Currency

### Putting `mnyfmt` Through its Paces

The implementation of `mnyfmt` is quite straightforward. It locates the decimal fractional character and backs up, swapping the format character 9 with the corresponding digit. Then it does the same moving forward with the decimal digits. Any other characters remain untouched. If the operation fails for any reason, `mnyfmt` returns `(char*)(0)`. As the following code demonstrates, every invocation of `mnyfmt` should ensure that a non-null value is returned.

```// test.example
char *sgn, fmtstr[96], buffer[96];
strcpy(buffer, "USD\$ "); // picture clause
strcpy(fmtstr , "99,999,999.99999");
if ((sgn = mnyfmt(fmtstr, '.', -102455,87)))
{
assertTrue(eqstr(fmtstr, "0-,102,455.87000"));
if ((*sgn=='-') && (','==*(sgn+1)))
{
++sgn; *sgn='-';
}
assertTrue(eqstr(sgn, "-102,455.87000"));
strcat(buffer, sgn);
assertTrue(eqstr(buffer, "USD\$ -102,455.87000"));
}
else
{
assertFalse("ERROR [???]: " "-102,455.87000");
}
```

In the following code, `mnyfmt` returns a null pointer `(char*)(0)`. The integer part of the value to format, 2455, requires space for four digits, but the picture clause has only three format characters before the decimal separator. The `fmtstr` remains unchanged.

```// test.too.small
char *sgn, fmtstr[96];
strcpy(fmtstr, "999.99999");
if ((sgn = mnyfmt(fmtstr, '.', 2455,87)))
{
// never executed ==> buffer too small
// 2455 has 4>3 digits [999.]
}
assertTrue(sgn == 0);
assertTrue(eqstr(fmtstr, "999.99999"));
```

Due to the flexibility of the picture buffer approach, formatting can be quite adaptable. In the following code, a variable called `buffer` is used to put parentheses around a formatted value if it is negative.

```// test.parentheses
char *sgn, fmtstr[96], buffer[96];
strcpy(buffer, "USD\$ ");
strcpy(fmtstr, "9,999,999.999");
if ((sgn = mnyfmt(fmtstr, '.',-102455,87)))
{
if (*sgn=='-')
{
// put parentheses around the formatted value
if (','==*(sgn+1))
{
// skip comma
++sgn; *sgn='-';
}
strcat(buffer, "(");
strcat(buffer, sgn);
strcat(buffer, ")");
assertTrue(eqstr(buffer, "USD\$ (-102,455.870)"));
}
else
{
strcat(buffer, sgn);
}
}
```

Sometimes the money amount must fill the whole picture clause and its leading non-significant digits must be displayed as asterisks. Here is how this is done:

```// test.asterisks
char *sgn, fmtstr[96];
strcpy(fmtstr, "\$9,999,999.999");
if ((sgn = mnyfmt(fmtstr, '.', -455,87)))
{
if ((*sgn=='-') && (','==*(sgn+1)))
{
++sgn; *sgn='-';
}
assertTrue(eqstr(sgn, "-455.870"));
for (--sgn; (sgn!=fmtstr); --sgn)
{
*sgn = '*'; // avoid writing over "\$"
}
assertTrue(eqstr(fmtstr , "\$*****-455.870"));
}
```

Dealing with floating-point values is not hard, but some care should be taken. In the example below, the double value to format is split into its integer and fractional parts with the standard function `modf`. The value written in the program is 2455.87, but the fractional part calculated by `modf` is 86, not 87. It turns out that the binary representation of this number is not exact in machines that use IEEE 754 binary floating-point arithmetic. To get the expected result, a rounding strategy must be applied.

```// test.modf
char *sgn, fmtstr[96];
double intdouble, fractdouble;
long intpart;
unsigned fractpart;
fractdouble = modf(2455.87, &intdouble);
intpart = intdouble; // 2455
fractpart = fractdouble*100; // .87
{
assertFalse(fractpart == 87 && "???");
assertTrue(fractpart == 86 && "!!!"); // binary rounding...
}
strcpy(fmtstr, "[[ 999,999.99999 ]]");
if ((sgn = mnyfmt(fmtstr, '.', intpart,fractpart)))
{
assertTrue(eqstr(fmtstr, "[[ 002,455.86000 ]]"));
assertTrue(eqstr(sgn, "2,455.86000 ]]"));
{ // std::round_toward_infinity
fractpart = ceil(fractdouble*100);
strcpy(fmtstr, "[[ 999,999.99999 ]]");
assertTrue(fractpart == 87 && "!!!");
if ((sgn = mnyfmt(fmtstr, '.', intpart, fractpart)))
{
assertTrue(eqstr(sgn,  "2,455.87000 ]]"));
}
}
}
```

### Using the Code

You should be able to compile `mnyfmt` with any C compiler. Even older C++ compilers support the `(long long)` data type, which gets implemented as a 64-bit (at least) binary number that has enough range to represent most money quantities. To make it possible to use` mnyfmt` in compilers that do not support this data type, macro `MNYFMT_NO_LONG_LONG` is used to define `mnyfmt_long` (the type of the integer part of the number) as `(long)` instead of` (long long)`:

```// test.limit
char *sgn, fmtstr[96];
#ifndef MNYFMT_NO_LONG_LONG
mnyfmt_long max = LONG_LONG_MAX;
strcpy(fmtstr, "999,999,999,999,999,999,999");
// 9,223,372,036,854,775,807
if (9223372036854775807LL == LONG_LONG_MAX)
{
if ((sgn = mnyfmt(fmtstr, ' ',  max,0)))
{
assertTrue(eqstr("9,223,372,036,854,775,807", sgn));
}
}
else
{
assertFalse("BEWARE: (long long) is not 8 bytes wide");
}
#endif
{
mnyfmt_long max = LONG_MAX;
strcpy(fmtstr, "999,999,999,999");
// 2,147,483,647
if (2147483647L == LONG_MAX)
{
if ((sgn = mnyfmt(fmtstr, ' ', max,0)))
{
assertTrue(eqstr("2,147,483,647", sgn));
}
}
else
{
assertFalse("BEWARE: (long) is not 4 bytes wide");
}
}
```

The number of trailing zeroes in the fractional part does not change the formatted value, as shown below. There is no need for a negative fractional part because the sign comes in the integer part.

```// test.fract.zero
char *sgn, fmtstr[96];
int i,tenPow;
// The fraction 643/2136 approximates
// log10(2) to 7 significant digits.
int N = ((CHAR_BIT * sizeof(int) - 1) * 643 / 2136);
tenPow = 12;
for (i=0; i<N; ++i)
{
strcpy(fmtstr, "999,999.999999999");
if ((sgn = mnyfmt(fmtstr , '.', -455,tenPow)))
{
if ((*sgn=='-') && (','==*(sgn+1)))
{
++sgn; *sgn='-';
}
assertTrue(eqstr(fmtstr, "00--455.120000000"));
assertTrue(eqstr(sgn, "-455.120000000"));
tenPow *= 10; // 12 120 1200 12000 120000 ...
}
}
```

The signature for the final version of `mnyfmt` looks like this:

```#ifndef MNYFMT_NO_LONG_LONG
typedef long long mnyfmt_long;
#else
typedef long mnyfmt_long;
#endif
char* mnyfmt
(
char *fmtstr,
char dec,
mnyfmt_long intpart,
unsigned fractpart
);
```

### More Insights

 To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.

# First C Compiler Now on Github

The earliest known C compiler by the legendary Dennis Ritchie has been published on the repository.

# HTML5 Mobile Development: Seven Good Ideas (and Three Bad Ones)

HTML5 Mobile Development: Seven Good Ideas (and Three Bad Ones)

# Building Bare Metal ARM Systems with GNU

All you need to know to get up and running... and programming on ARM

# Amazon's Vogels Challenges IT: Rethink App Dev

Amazon Web Services CTO says promised land of cloud computing requires a new generation of applications that follow different principles.

# How to Select a PaaS Partner

Eventually, the vast majority of Web applications will run on a platform-as-a-service, or PaaS, vendor's infrastructure. To help sort out the options, we sent out a matrix with more than 70 decision points to a variety of PaaS providers.

More "Best of the Web" >>