-4

Following is my code to convert long (cents) to dollar, however, there is 1 cent difference.

My expected answer is: $123,456,789,123,456.47, but the output is $123,456,789,123,456.48

public static void main(String[] args) {
    long l = 12345678912345647L;    
    double d = l / 100.00;
    NumberFormat formatter = NumberFormat.getCurrencyInstance();
    System.out.println(formatter.format(d));
}
Thomas
  • 1,805
  • 1
  • 15
  • 31
  • 4
    probably because the number cannot be stored in a `double` lossless. – luk2302 Sep 30 '17 at 22:54
  • 2
    The answer is only accurate up to approximately $70 trillion – Dean Coakley Sep 30 '17 at 22:55
  • @Dici No, use `BigDecimal`, not double and then you can use it for everything and don't have to switch between long <> String <> double/BigDecimal. – Tom Sep 30 '17 at 23:00
  • @Dici I can't tell the exact limits of `BigDecimal` (if there are any), but I would bet that these issues were still lower than on other primitive types. `BigDecimal` is also serializable. – Tom Sep 30 '17 at 23:10
  • 1
    @Tom BigDecimal is effectively only limited by memory. – user2864740 Sep 30 '17 at 23:17
  • @Dici Your argumentation is unclear. You say one can't store infinitely large/precise numbers (which is true), but still prefer int or long, although both have much lower max/min values than BigDecimal? And you would still translate your currency from floating point to int/long and here I wonder if BigDecimal really is inable to store a decimal number which would fit into int/long when converted into that. – Tom Sep 30 '17 at 23:21
  • @Tom Hmm I think I see your point. `BigDecimal` also seems to add some safety that you'll never get from a primitive type: `If no rounding mode is specified and the exact result cannot be represented, an exception is thrown` – Dici Sep 30 '17 at 23:41
  • 1
    @Tom this page summarizes some of the points we have discussed: http://www.javapractices.com/topic/TopicAction.do?Id=13 – Dici Sep 30 '17 at 23:46

2 Answers2

1

For numbers greater than ~70 trillion BigDecimal should be used to remain accurate.

public static void main(String[] args) {
        BigDecimal l = new BigDecimal("12345678912345647");
        BigDecimal d = l.divide(new BigDecimal("100.00"));
        NumberFormat formatter = NumberFormat.getCurrencyInstance();

        System.out.println(formatter.format(d));
    }
Dean Coakley
  • 1,675
  • 2
  • 11
  • 25
  • I kind of understand why this is getting down voted (no explaination), but at the same time. PLEASE: all I want is to understand why. At least explain. – Dean Coakley Sep 30 '17 at 23:08
  • @Dici Yeah, it's fine. I understand that storing data must always be discrete in CS. So are you saying the above code is not a good solution? There is no good solution? – Dean Coakley Sep 30 '17 at 23:11
  • 1
    I tried this solution and it worked. – Thomas Sep 30 '17 at 23:40
  • @Deancoakley if you're interested http://www.javapractices.com/topic/TopicAction.do?Id=13 – Dici Sep 30 '17 at 23:46
  • 3
    `BigDecimal` is fine for storing money. It's completely accurate for any finite number of decimal places, and it's limited only by the amount of memory you have. There is no reason whatsoever to favour `long` over `BigDecimal`, except possibly performance. Indeed, `BigDecimal` is safer than `long`, insofaras you'll never get integer overflow. – Dawood ibn Kareem Sep 30 '17 at 23:47
  • Yeah, I'll remove my comments to avoid misleading future users. After reading more about it, this seems to be the recommended way to deal with money – Dici Sep 30 '17 at 23:50
  • 1
    *"it's limited only by the amount of memory you have"* - generally correct, but even if you have unlimited memory it is still limited, because it is backed by an array and that uses `int` for indexing (also see [Do Java arrays have a maximum size?](//stackoverflow.com/q/3038392)). – Tom Sep 30 '17 at 23:57
  • @KaiLiu Can this be marked as the best solution please? – Dean Coakley Oct 03 '17 at 09:51
  • 1
    Marked. This answer solves the problem rather than the [duplicate] one. – Thomas Oct 03 '17 at 12:06
-4

It is rounding like that because you are getting the output of a long divided by a number to 2 decimal places and then it's being stored in the double. If you want to get the number without it being rounded up try dividing by 100.000 or by replacing the long l with double l.

Dhowa
  • 27
  • 6
  • I tried both solutions but both didn't work. – Thomas Sep 30 '17 at 23:07
  • @KaiLiu So you tried my code and got the same off-by-one error? – Dean Coakley Sep 30 '17 at 23:13
  • @dean-coakley, I tried his both solutions but they didn't work. I tried yours and it worked and I upvote yours. – Thomas Sep 30 '17 at 23:40
  • The literals `100.00` and `100.000` are stored identically, because they're both `double`, and the `double` type does not include an indicator of how many decimal places were given in the original literal. This solution is guaranteed to be incorrect. – Dawood ibn Kareem Sep 30 '17 at 23:49