3

According to SF documentation Decimal.format() method "Returns the String value of this Decimal using the locale of the context user.", but it leaves only 3 digits after decimal point. It doesn't even matter if you use setScale() method before calling format(). Example from SF documentation:

// U.S. locale
Decimal myDecimal = 12345.6789;
system.assertEquals('12,345.679', myDecimal.format());

As you can see it leaves only 3 digits after decimal point.

Is there a "standard" way to format number in Apex (not Visualforce) using the locale of the context user and leave all the digits after decimal point? Or it should be custom code/logic to handle this?

1

I've logged an Idea for this issue to ask Salesforce for solving this.

Please also find the following 'work-a-round' code that I've written for my current project to make this failure work either way.

/**
 * Function to print a number String or Decimal into the local Currency
 * Format() only returns up till an accuracy of 3 digits, causing a potential issue
 */
public static String formatCurrency( Object o, Integer numberOfDecimals ) {
    // Construct separators based on UserLocale (Decimal.format() is locale sensitive)
    Decimal value           = 1000.10;
    String formattedValue   = value.format();
    String thousandChar     = formattedValue.substring(1,2);
    String decimalChar      = formattedValue.substring(5,6);

    // fetch input number
    Decimal decVal = 0;
    if      ( o == null )            decVal = 0;
    else if ( o instanceof Decimal ) decVal = ( Decimal ) o;
    else if ( o instanceof String )  decVal = Decimal.valueOf( String.valueOf( o ) );

    // apply rounding or add additional 0 decimals
    decVal = decVal.setScale( numberOfDecimals, RoundingMode.CEILING );

    // use format() to print the Integer including thousand separators in user locale
    // Note: only prints up to an accuracy of 3 and does not print 'ending zeros'
    String formattedString = decVal.format();

    // fetch full decimals string
    String originalNumber = decVal.ToPlainString(); // prints with System separators 
    String decimalString  = originalNumber.substringAfter('.');

    // fetch the integer string (before decimal separator), containing thousand separators
    String integerString  = formattedString.substringBefore( decimalChar );

    // concatenate those together
    return integerString + decimalChar + decimalString;
}

Hope this can help you and please upvote the Idea :)

| improve this answer | |
0

I don't know of an API for this.

So have this ugly code in one of my projects (which is based on someone else's post that I no longer have a link to):

private static final Map<Integer, Integer> MULTIPLIERS = new Map<Integer, Integer> {
        1 => 10,
        2 => 100,
        3 => 1000,
        4 => 10000,
        5 => 100000,
        6 => 1000000
        };
public static String format(Decimal d, Integer places) {

    if (d != null) {

        // Round first
        Decimal dd = d.setScale(places);

        // Format to get thousand separators
        String w = dd.longValue().format();

        if (places != 0) {

            if (dd < 0) dd = -dd;
            Integer multiplier = MULTIPLIERS.get(places);

            // For example 0.0067 becomes 10067 then the leading 1 is dropped to get 0067
            String f = String.valueOf(multiplier + Math.mod((dd * multiplier).longValue(), multiplier));
            return w + '.' + f.substring(1);
        } else {
            return w;
        }
    } else {
        return null;
    }
}

with test:

@isTest
static void testFormat() {

    System.assertEquals(null, StringUtil.format(null, 2));

    System.assertEquals('12.34', StringUtil.format(12.34, 2));
    System.assertEquals('12.30', StringUtil.format(12.3, 2));
    System.assertEquals('12.00', StringUtil.format(12, 2));

    // Now rounds; didn't use to
    System.assertEquals('12.34', StringUtil.format(12.3444, 2));
    System.assertEquals('12.35', StringUtil.format(12.3466, 2));

    System.assertEquals('12.34', StringUtil.format(12.344, 2));
    System.assertEquals('12.35', StringUtil.format(12.346, 2));

    System.assertEquals('12.3400', StringUtil.format(12.34, 4));
    System.assertEquals('12.340', StringUtil.format(12.34, 3));
    System.assertEquals('12.34', StringUtil.format(12.34, 2));
    System.assertEquals('12.3', StringUtil.format(12.34, 1));
    System.assertEquals('12', StringUtil.format(12.34, 0));

    System.assertEquals('12.00', StringUtil.format(12, 2));
    System.assertEquals('12.0', StringUtil.format(12, 1));
    System.assertEquals('12', StringUtil.format(12, 0));

    System.assertEquals('12.3456', StringUtil.format(12.345644, 4));
    System.assertEquals('12.3457', StringUtil.format(12.345666, 4));

    System.assertEquals('123,456.12', StringUtil.format(123456.1234, 2));
    System.assertEquals('123,456.1235', StringUtil.format(123456.123488, 4));
    System.assertEquals('-55,123,456.12', StringUtil.format(-55123456.1234, 2));

    System.assertEquals('-123,456.123457', StringUtil.format(-123456.12345678, 6));

    System.assertEquals('12', StringUtil.format(12.34, 0));
    System.assertEquals('13', StringUtil.format(12.67, 0));
}
| improve this answer | |
  • I'm also thinking about some custom logic/code to handle this, but still hope there is more straight forward solution. So will also appreciate other suggestions if any. Thanks! – sfman Nov 13 '15 at 9:17
0

It does not appear that a better standard answer has come along since this was posted, but I tried making a version that converts the value to a plain string and a format string, taking advantage of the ToPlainString() method giving the correct precision, and uses the separators from format().

public static string formatPlaces(decimal dec, integer places)
{
    String rawString = dec.setScale(places).ToPlainString(), 
        formatString = (dec.round() == dec ? dec + .1 : dec).format(); //Adjust for integers
    return formatString.substring(0, lastIndexOfRegex(formatString, '\\D') + 1)
        + rawString.substring(rawString.indexOf('.') + 1);
}

//Adapted from Tomer Godinger's Java solution: https://stackoverflow.com/a/10999868/2554810
public static integer lastIndexOfRegex(String str, String toFind)
{
    Pattern regPattern = Pattern.compile(toFind);
    Matcher regMatcher = regPattern.matcher(str);

    integer lastIndex = -1;

    while (regMatcher.find())
    {
        lastIndex = regMatcher.start();
    }

    return lastIndex;
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.