We often need to display numbers, in GUI, text files or maybe the console window (Matlab Desktop). Matlab has built-in support for this using the fully-documented * num2str*,

*and*

**sprintf***functions. Unfortunately, these built-in functions, useful as they are for simple needs, have several limitations:*

**format**- They do not take into account the user’s computer Locale settings. For example, the number 1234.56 is normally displayed as 1,234.56 or 1’234.56 or 1234,56 depending on your chosen Locale (which is determined by the local language and country). The functions always use the same manner to display the number, disregarding the user’s Locale.
does not enable customization of the number of decimal digits, and**format**and**num2str**‘s ability to do so is limited. Customization of the decimal sign and thousands grouping sign is even more difficult.**sprintf**

I have recently completed a consulting work for a bank in Switzerland where these limitations were very important. The bank has branches in several countries and people naturally use different Locales on their computer. Moreover, some data values (for example, FOREX exchange rates) need more than two decimal digits. I had to find a generic cross-platform way to code this in Matlab in a way that will work for all users out-of-the-box (the application is compiled and distributed as an executable).

The solution was to use Matlab’s fully-documented built-in support for using Java objects. In this case, we shall use the standard java.text.DecimalFormat:

>> % Simple formatting example >> nf = java.text.DecimalFormat >> nf.format(1234567.890123) ans = 1,234,567.89 >> % Modify some properties... >> set(nf,'GroupingSize',5) >> nf.setMaximumFractionDigits(4) >> nf.format(1234567.890123) ans = 12,34567.8901

In the simple code snippet above, note the two alternative manners in which I set the `DecimalFormat`

object’s properties, once with the * set(‘XYZ’,value)* function, then using the corresponding Java accessor method

*setXYZ(value)*. In most cases, we find that Java properties can be set using either of these manners, although using the Java accessor methods is typically safer and prevents some possible problems. Similarly, property values can be retrieved using either

*or the corresponding*

**get**(‘XYZ’)*getXYZ()*or

*isXYZ()*Java methods (which are better).

Some of the interesting gettable/settable properties of the `DecimalFormat`

object include (my Locale’s defaults are specified – your Locale might be different):

**Currency**– a java.util.Currency object that sets the currency symbol, international currency code and number of fractional decimal digits**DecimalFormatSymbols**– a java.text.DecimalFormatSymbols object that sets the character symbols for such things as the decimal sign (“.” or “,” for example), grouping separator (“,” or ” ‘ ” or whatever), percent and per-mill signs, minus sign, infinity sign etc.**DecimalSeparatorAlwaysShown**– (default=false) a boolean flag that specifies whether or not to display the decimal sign even for integer numbers. For example, “123.” rather than “123″.**GroupingSize**– (default=3) indicates how many integer digits should be grouped by the grouping (“thousands”) separator sign.**GroupingUsed**– (default=true) a boolean flag that specifies whether or not to use the grouping sign at all. Matlab’s built-indoes not use grouping.**num2str****MaximumFractionDigits**– (default=3) the maximal number of digits to display to the__right__of the decimal sign. Post-rounding trailing zeros are omitted.**MinimumFractionDigits**– (default=0) the minimum number of digits to display, padding with trailing zeros if necessary.**MaximumIntegerDigits**– (default=) the maximal number of digits to display to the**intmax**__left__of the decimal sign. Extra digits are removed.**MinimumIntegerDigits**– (default=1) the minimum number of digits to display, padding with leading zeros if necessary.**Multiplier**– (default=1) multiplies the number by the specified value before converting to string.**NegativePrefix**– (default=’-') the string to display to the__left__of a negative number.**NegativeSuffix**– default = ”) the string to display to the__right__of a negative number. Some bankers like to display negative numbers as “(value)” and this is easy to do using**NegativePrefix**and**NegativeSuffix**. There are corresponding properties**PositivePrefix**and**PositiveSuffix**.**RoundingMode**– (default=RoundingMode.HALF_EVEN) a java.math.RoundingMode object that sets the manner in which numbers are rounded to fit the required number of digits.

In addition, the `DecimalFormat`

object provides methods that enable setting/getting a pattern that directly specifies how numbers should be parsed. A description of the different pattern components can be found here. The pattern directly corresponds to the settable properties listed above, and is very similar to the custom numeric pattern that is settable in Microsoft Excel. For example:

>> % default format: >> nf.toPattern ans = #,##0.### >> str = char(nf.format(-pi)) str = -3.142 >> % non-default format: >> nf.setNegativePrefix('- (') >> nf.setNegativeSuffix(') !!!') >> % alternatively: nf.applyPattern('#,##0.###;- (#,##0.###) !!!') >> nf.toPattern ans = #,##0.###;- (#,##0.###) !!! >> str = nf.format(-pi).char str = - (3.142) !!! >> % Alternatively, we could use the pattern directly: >> str = char(java.text.DecimalFormat('#,##0.###;- (#,##0.###) !!!').format(-pi));

Note how we convert the `java.lang.String`

object returned by the *format* method into a Matlab string using the built-in * char* function.

To test how the numbers react to different Locales, we could specify a specific Locale to the `DecimalFormatSymbols`

object, as follows:

>> dfs = java.text.DecimalFormatSymbols(java.util.Locale('fr-CH')); % Swiss-French Locale >> dfs.setGroupingSeparator(''''); % use ' rather than , >> nf.setDecimalFormatSymbols(dfs) >> nf.format(123456.789012) ans = 123'456.789

Alternatively, change the computer’s current Locale (in Windows, this is done via the Control Panel’s Regional Settings dialog), open a new Matlab session and rerun the code using the default `DecimalFormat`

. Restarting Matlab is important, because the Locale is only read once by the Java engine (JVM), when it starts, and this happens when Matlab starts.

To complete the picture, the `DecimalFormat`

object can not only format a numeric value as a string, using *format()* as we have seen above, but also the reverse – convert a string to a numeric value using *parse()*:

>> value = nf.parse('123''456.789').doubleValue value = 123456.789

Related posts:

- Types of undocumented Matlab aspects This article lists the different types of undocumented/unsupported/hidden aspects in Matlab...
- Reading non-Latin text files A workaround that enables reading non-Latin text files in Matlab is shown...
- GUI automation utilities This article explains a couple of Matlab utilities that use Java's Robot class to programmatically control mouse and keyboard actions...
- GUI integrated HTML panel Simple HTML can be presented in a Java component integrated in Matlab GUI, without requiring the heavy browser control....
- Javacomponent background color This article explains how to align Java component background color with a Matlab color....
- Tab panels – uitab and relatives This article describes several undocumented Matlab functions that support tab-panels...

Do you have a solution to format doubles leaving n (say 4) significant digits for pos/neg numbers between for example [1e-5, 1e+5] and scientific notation for very large/small numbers like in example below?

0.000012345678 -> 1.235e-005

0.001234567898 -> 0.001235

0.123456789876 -> 0.1235

1.234567898765 -> 1.235

123.4567898765 -> 123.5

1234.567898765 -> 1235

123456.7898765 -> 123457

1234567.898765 -> 1.234e+006

except for your next to last example you could use the

option as follows:sprintfperhaps there are other solutions.

Donn

Pingback: Grouping Digits | Now