Unable to round of a number to 2 precision.

Hi,

We are using the PSUtilities.math:roundNumber(Have pasted code below.)
When the input string is given as 581.295 for 2 precision we get the answer as 581.29 instead of 581.3.
For other numbers eg 581.395 for precision 2 we get the answer as 581.4 as required.
Could some one please suggest is it an issue with Server Patches or the code or something else.

Thanks in advance.


    IDataCursor idcPipeline = pipeline.getCursor();
    if (!idcPipeline.first("number"))
    {
        throw new ServiceException("input number was null!");
    }

    String strNumber = (String)idcPipeline.getValue();
    double dblNumber = Double.parseDouble(strNumber);


    if (idcPipeline.first("precision"))
    {
        String strPrecision = (String)idcPipeline.getValue();
        double dblPrecision = Double.parseDouble(strPrecision);

        double dblMulDivFactor = java.lang.Math.pow(10, dblPrecision);
        double dblRoundedNumber = java.lang.Math.round(dblNumber * dblMulDivFactor) / dblMulDivFactor;

        idcPipeline.insertAfter("roundedNumber", Double.toString(dblRoundedNumber));

    }
    else
    {
        idcPipeline.insertAfter("roundedNumber", strNumber);

    }

    idcPipeline.destroy();

The issue is that the PSUtilities.math services should not be used when accuracy is required. They use float/double types which have accuracy issues. There are a few threads on the forums that speak to this. Do a search for BigDecimal to find them.

You probably want to write a set of services that use BigDecimal to do add, subtract, multiply, divide and round. Here’s some source you can use if desired (no warranties/support):
[highlight=java]
// add service - Adds two numbers using java.math.BigDecimal.
IDataCursor idc = pipeline.getCursor();
String num1 = IDataUtil.getString(idc, “num1”);
String num2 = IDataUtil.getString(idc, “num2”);
java.math.BigDecimal n1 = new java.math.BigDecimal(num1);
java.math.BigDecimal n2 = new java.math.BigDecimal(num2);
IDataUtil.put(idc, “value”, n1.add(n2).toString());
idc.destroy();

// subtract service - Subtracts num2 from num1 using java.math.BigDecimal.
IDataCursor idc = pipeline.getCursor();
String num1 = IDataUtil.getString(idc, “num1”);
String num2 = IDataUtil.getString(idc, “num2”);
java.math.BigDecimal n1 = new java.math.BigDecimal(num1);
java.math.BigDecimal n2 = new java.math.BigDecimal(num2);
IDataUtil.put(idc, “value”, n1.subtract(n2).toString());
idc.destroy();

// multiply service - Multiplies two numbers using java.math.BigDecimal.
IDataCursor idc = pipeline.getCursor();
String num1 = IDataUtil.getString(idc, “num1”);
String num2 = IDataUtil.getString(idc, “num2”);
java.math.BigDecimal n1 = new java.math.BigDecimal(num1);
java.math.BigDecimal n2 = new java.math.BigDecimal(num2);
IDataUtil.put(idc, “value”, n1.multiply(n2).toString());
idc.destroy();

// divide service
// Divides two numbers using java.math.BigDecimal. The
// caller can control rounding and scale (number of decimal
// places).
// The default rounding mode is 6 (ROUND_HALF_EVEN).
// The default scale of the result is num1.scale(). For
// example, num1=42 and num2=7.5 returns 6 (5.6 rounded up).
// Use the constants from java.math.BigDecimal for roundingMode.
// The values are:
// 0 ROUND_UP
// 1 ROUND_DOWN
// 2 ROUND_CEILING
// 3 ROUND_FLOOR
// 4 ROUND_HALF_UP
// 5 ROUND_HALF_DOWN
// 6 ROUND_HALF_EVEN
// 7 ROUND_UNNECESSARY
IDataCursor idc = pipeline.getCursor();
String num1 = IDataUtil.getString(idc, “num1” );
String num2 = IDataUtil.getString(idc, “num2” );
int scale = IDataUtil.getInt(idc, “scale”, -1);
int roundingMode = IDataUtil.getInt(idc, “roundingMode”, java.math.BigDecimal.ROUND_HALF_EVEN);
java.math.BigDecimal n1 = new java.math.BigDecimal(num1);
java.math.BigDecimal n2 = new java.math.BigDecimal(num2);
java.math.BigDecimal result;
if(scale == -1)
result = n1.divide(n2, roundingMode);
else
result = n1.divide(n2, scale, roundingMode);
IDataUtil.put(idc, “value”, result.toString());
idc.destroy();

// round service
// Uses java.math.BigDecimal to round a number. This lets
// the caller control rounding and scale (number of decimal
// places).
// If scale not provided, no effect–num1 is returned unchanged.
// The default rounding mode is 6 (ROUND_HALF_EVEN).
// Use the constants from java.math.BigDecimal for roundingMode.
// The values are:
// 0 ROUND_UP
// 1 ROUND_DOWN
// 2 ROUND_CEILING
// 3 ROUND_FLOOR
// 4 ROUND_HALF_UP
// 5 ROUND_HALF_DOWN
// 6 ROUND_HALF_EVEN
// 7 ROUND_UNNECESSARY
IDataCursor idc = pipeline.getCursor();
String num1 = IDataUtil.getString(idc, “num1”);
int scale = IDataUtil.getInt(idc, “scale”, -1);
int roundingMode = IDataUtil.getInt(idc, “roundingMode”, java.math.BigDecimal.ROUND_HALF_EVEN);
java.math.BigDecimal n1 = new java.math.BigDecimal(num1);
if(scale != -1)
n1 = n1.setScale(scale, roundingMode);
IDataUtil.put(idc, “value”, n1.toString());
idc.destroy();

[/highlight]
Create separate Java services for each of these. Feel free to post any questions if anything is unclear.

Rob,

Thanks for the response…
We have two services using BigDecimal. Though both are pretty similar yet for the same input it does not work.
For input data as 581.295 and precision as 2
Any idea or pointers about the same would really help in understanding the way it works or rather why it does not work.

Thanks in advance,

-------------------------------WORKS----------------------------------

 IDataCursor cursor = pipeline.getCursor();

String     inputString     =  IDataUtil.getString( cursor, "inputString" );
String    roundOffNumber  =  IDataUtil.getString(cursor,"precesion");
if (roundOffNumber != null){
    if (roundOffNumber.length()>0) {
        int decimalPlace =  Integer.parseInt(roundOffNumber);
        IDataUtil.put(cursor, "result", ((new BigDecimal(inputString)).setScale(decimalPlace,BigDecimal.ROUND_HALF_UP)).toString());
    }
}

cursor.destroy();

---------------------------DOES NOT WORK ------------------------------

// pipeline
String result = "321";

  double r = 581.295;

    int decimalPlace = 2;
    BigDecimal bd = new BigDecimal(r);
    bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP);
    r = bd.doubleValue();
    result  =  Double.toString(r);
    
// pipeline
IDataCursor pipelineCursor = pipeline.getCursor();
IDataUtil.put( pipelineCursor, "result", result );
pipelineCursor.destroy();

The reason that the “DOES NOT WORK” example doesn’t work is because of the use of Double/double. If you want to retain accuracy never use float or double. This post covers some of the reasons why.

Rob,

Thanks for the reply and also for the refresher. It really did make the problem understandable.

Regards,