4

I am building a new ecommerce platform using Tomcat/JSTL/MySQL/etc.

I am aware of the various issues related to using floating point calculations - e.g. 12.3456000000000789... I know rounding vs truncation can also be an issue.

Without moving all of the business logic into Java classes, what is the best way to make reliable calculations on currency while still keeping the architecture simple?

My thoughts on possibilities include:

  • isolate the problem calculations and deal with them individually
  • represent numbers as longs and do calculations using cents
  • use BigDecimal and do all calculations in Java
  • store numbers in Strings as much as possible
  • use <fmt:formatNumber var="... instead of <c:set var="...

I might not be the first to doing this.

What is the most straight forward approach?

Yusubov
  • 5,815
  • 9
  • 32
  • 69
PrecisionPete
  • 3,139
  • 5
  • 33
  • 52
  • I've heard BigDecimal is specially designed for such things. I'm not an expert though. – Adrián Jan 15 '13 at 19:55
  • 2
    Search through the internet. This question has been already answered: [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://www.cse.msu.edu/~cse320/Documents/FloatingPoint.pdf). Short answer: use `BigDecimal`. – Luiggi Mendoza Jan 15 '13 at 21:17
  • `` is helpful for the View, because most customers won't be interested in seeing more than 2 decimals ;) Also, you can add the currency symbol with it, which is quite nice. – Adrián Jan 15 '13 at 21:21
  • Thanks. I am aware of the issues with floats and of BigDecimal. I was hoping there was an easy way to use that while staying within JSTL EL. e.g. c:set var=... value=... type="java.math.BigDecimal" then having nice objects to work with. I know this is asking a lot. Given the nature of web apps, it would have been nice for the JSTL designers to build something in... – PrecisionPete Jan 15 '13 at 21:22
  • http://stackoverflow.com/questions/10882549/bigdecimal-in-jstl-divide-by-int-or-double-returns-integer Looks like there isn't anything to make it easier :/ – Adrián Jan 15 '13 at 21:24
  • 1
    If your environment supports EL 2.2, you can just invoke BigDecimal-specific methods in EL. For the remainder, this question is offtopic/nonconstructive. Try programmers.se with a bit stronger ground instead of basically asking "I know that there's no other way than BigDecimal, but is there another way?". – BalusC Jan 15 '13 at 21:32

3 Answers3

3

isolate the problem calculations and deal with them individually

Not an answer. Whatever technique you use will come from elsewhere in your list.

represent numbers as longs and do calculations using cents

Feasible but clumsy, and there are cases in finance where fractions of a cent are valid: for example, foreign exchange.

use BigDecimal and do all calculations in Java

This is the answer. Better still, do the calculations in the database.

store numbers in Strings as much as possible

Not an answer. You still have to calculate, and when you do the technique will come from elsewhere in your list.

use <fmt:formatNumber var="... instead of <c:set var="...

Not an answer. You will certainly have to use formatNumber, but it doesn't address how you are going to calculate. I don't see what c:set has to do with it, it does a different thing. It's not an alternative to numberFormat.

user207421
  • 305,947
  • 44
  • 307
  • 483
0

Without moving all of the business logic into Java classes, what is the best way to make reliable calculations on currency while still keeping the architecture simple?

Pick simple or reliable - you can't really have both in this case - at least not that simple.

Business logic in a JSP page is a really bad code smell - and it's bad enough in a small, personal project, but why would you do that in an "ecommerce platform"?

Despite your hesitation to do so, I suggest that you move all of your business logic from your JSPs to appropriate java classes.

Better yet, given that you are developing software to deal with financial transactions, do yourself and your potential customers a big favor, and hire a programmer who knows what they are doing.

I realize that this is coming off a bit insulting, but given your obvious lack of familiarity with both the Java programming language and decent coding practices in general, you really have no business writing commercial software that deals with other people's money.

GreyBeardedGeek
  • 29,460
  • 2
  • 47
  • 67
0

Bingo. My problem wasn't in EL at all. Turns out I accidentally had some float fields in the database. When I changed them back to decimals, everything fell into place.

Seems if you pull a decimal out of MySQL, you get a BigDecimal under the covers. So the word is orderly after all.

Thanks all for your input.

see:

<c:set var="tests" value="123.45"/>
<br>tests: ${tests} -- <%= pageContext.findAttribute("tests").getClass() %>

<c:set var="testl" value="${3}"/>
<br>testl: ${testl} -- <%= pageContext.findAttribute("testl").getClass() %>

<c:set var="testd" value="${123.45}"/> 
<br>testd: ${testd} -- <%= pageContext.findAttribute("testd").getClass() %>

<c:set var="testbd" value="<%= new BigDecimal(\"123.45\") %>"/>
<br>testbd: ${testbd} -- <%= pageContext.findAttribute("testbd").getClass() %>

<c:set var="testbd" value="${testbd+1}"/>
<br>testbd: ${testbd} -- <%= pageContext.findAttribute("testbd").getClass() %>

<sql:query var="items">
SELECT * FROM items WHERE itemid=?;
<sql:param value="55" />
</sql:query>
<c:set var="testdb" value="${items.rows[0].price}"/>
<br>testdb: ${testdb} -- <%= pageContext.findAttribute("testdb").getClass() %>

<%-- output
tests: 123.45 -- class java.lang.String 
testl: 3 -- class java.lang.Long 
testd: 123.45 -- class java.lang.Double 
testbd: 123.45 -- class java.math.BigDecimal 
testbd: 124.45 -- class java.math.BigDecimal 
testdb: 129.95 -- class java.math.BigDecimal
--%>
PrecisionPete
  • 3,139
  • 5
  • 33
  • 52