Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default precision and scale #1

Closed
LeonLiuY opened this issue Oct 16, 2013 · 3 comments
Closed

Default precision and scale #1

LeonLiuY opened this issue Oct 16, 2013 · 3 comments

Comments

@LeonLiuY
Copy link

Hello, I found EvalEx is very useful and I have similar needs of BigDecimal in expression evaluator.
I really appreciate your great work.

Anyway, I found several issues when I tried to use it.

The first one is that Expression seems have an unclear default precision.
I just use this code to test:

Expression exp = new Expression(str);
System.out.println(exp.eval());

The input and output will be:
1.1234895 => 1.12349
1.1234891 => 1.123489

What I expected is:
1.1234895 => 1.1234895
1.1234891 => 1.1234891

I can get what I expected by adding:

exp.setPrecision(0);

It can solve this problem but I just get confused by the default behavior.
Can exp.setPrecision(0); be a default condition?

Another issue is that I could not control the scale during calculation.
We can control precision by setPrecision and ROUND(expression,precision) but there is no function for scale.

How do you think about these?
I'll dig into the code and try to contribute if I have time.

It is very nice of you if you could do some improvement.

Thank you very much!

@uklimaschewski
Copy link
Collaborator

Thanks alot, I am always happy to hear some applause ;-)

The default precision is set through the default MathContext:

private MathContext mc = MathContext.DECIMAL32;

According to Java, the DECIMAL32 defines a precision of 7 and a rounding mode of HALF_EVEN, which matches the IEEE 754R Decimal32 format.

I think it would be no problem to change the default MathContext to have a precision of 0, I agree with your idea.
But I'd like to keep the default rounding mode at HALF_EVEN, I think it is the most common used mode.

With precision and scale, I see a bit of a problem, as the Java implementation has IMHO a problem here.
The MathContext, which is used overall in calculation only knows about precision and not scale, whereas the BigDecimal implementation only knows about scale and not precision.

@LeonLiuY
Copy link
Author

Thank you for quick response!

I've read the code.

addFunction(new Function("ROUND", 2) {
    @Override
    public BigDecimal eval(List<BigDecimal> parameters) {
        BigDecimal toRound = parameters.get(1);
        int precision = parameters.get(0).intValue();
        return toRound.setScale(precision, mc.getRoundingMode());
    }
});

It seems that ROUND(expression,precision) actually is ROUND(expression,scale).
The test case also stands by it:

@Test
public void testRounding() {
    assertEquals("3.8", new Expression("round(3.78787,1)").eval().toString());
    assertEquals("3.788", new Expression("round(3.78787,3)").eval().toString());
    assertEquals("3.734", new Expression("round(3.7345,3)").eval().toString());
    assertEquals("-3.734", new Expression("round(-3.7345,3)").eval().toString());
    assertEquals("-3.79", new Expression("round(-3.78787,2)").eval().toString());
    assertEquals("123.79", new Expression("round(123.78787,2)").eval().toString());
}

Maybe this is somewhat different understanding of "precision" and "scale".
My definition is:
Precision is the number of significant digits of the whole BigDecimal,
while scale is the number of the digits after the decimal point.

Another problem I found is:
If we use exp.setPrecision(0);, eval 1/3 will result
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
It is obvious. To solve it, I can:

  • Do not use 0 on precision, use a positive number. But in some situation, I do not know what is the "positive number". Estimating what exactly is the maximum value of the calculation is annoying.
  • Keep 0(unlimited) precision, use a function like 1/3R2 while "R2" represents "round result to scale 2", or DIV(1, 3, 2), which is much easier to implement.

I think in financial applications. scale and rounding are important because money has a minimum unit, while precision seems not that useful. They always require an unlimited precision because they do not want to lose any money. (They control the fractional part and give freedom to the integer part)

Anyway, now I can figure out how to use EvalEx -- I'll use 0 precision and define a function DIV to avoid repeating decimal. The ROUND function is just what I need for controlling scale on other operations.

I'm not native English speaker, thank you a lot for patience.

@uklimaschewski
Copy link
Collaborator

Thanks alot for your input, I will meditate about it, when I find some time :-)

uklimaschewski pushed a commit that referenced this issue Jul 28, 2014
Converted to Maven project
uklimaschewski pushed a commit that referenced this issue Sep 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants