0

I need to write a small Java program that deals with calculations involving money. Therefore it needs to have accuracy. (Eg: No float/double variables, only long).

Unfortunately, the original value I need to use is imported through a method which can only read variables as "double".

Now, I tried casting it to a long using a method similar to:

double importedValue = x;
double importedValueConverted = (long) x;

However, when I try dividing importedValueConverted by another "long" variable I get:

required: long found: double error: possible loss of precision

Why is that?

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
MrD
  • 4,986
  • 11
  • 48
  • 90
  • why only long type? Why the constraint? – gtgaxiola Sep 28 '12 at 18:09
  • You're casting `x` to a long than trying to assign it to a double. That doesn't make sense. – jahroy Sep 28 '12 at 18:13
  • 1
    The precision limit of doubles is 15 digits; that's an error of a penny in ten trillion dollars. Doubles should be fine unless you have that kind of need for precision. – James Cronen Sep 28 '12 at 19:00
  • It's not so much the precision, it's that double can't accurately represent all decimal values, and thus are not recommended for financial values – Bohemian Oct 05 '12 at 03:52

6 Answers6

3
double importedValue = x;
double importedValueConverted = (long) x;

Note that both of these variables are declared as 'double'. This results in your error (paraphrasing): (the operation you're doing requires a) long (but when it tried it found a) double.

You want:

double importedValue = x;
long importedValueConverted = (long) x;
Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
2

Forget all the casting business. If you are working with financial calculations, you can directly use BigDecimal to wrap the doubles returned by your so called method and use an appropriate rounding mechanism provided by BigDecimal that suits your needs.

Chetan Kinger
  • 15,069
  • 6
  • 45
  • 82
1

Update:

This post raised an additional question which I don't think was ever answered-- why use int, or better yet, long or BigDecimal for currency calculations. This is answered here:

Why not to use double or float to represent currency (or where any exact calculations are needed)?

Because floats and doubles cannot accurately represent most base 10 real numbers.

And even when using BigDecimal, one must use the String constructor and not the float one.

This all said, your best bet is to:

  • Convert all values to cents and store as a long (multiply each dollar amount by 100)
  • Do the operations in cents
  • Convert back to dollars by dividing by 100 at the end

This will retain the accuracy desired. Obviously this solution has USD in mind, any conversions to foreign currencies would need appropriate consideration.


Rather than casting, consider rounding to the nearest long value:

double d = 1234.56;
long x = Math.round(d);

Tho really I ask why you'd want to go from a double to a long, as this is going to lose you the precision of the decimal values.

If you want to keep some precision (up to 3 digits, say), and you can absolutely only work with long to do so, you can multiply both doubles by 1,000, then scale all later operations by the same factor, and then scale them all back at the end, like so:

double starting = 1234.5678;
double worker = starting * 1000;
long roundedWorker = Math.round(worker);

// do other computations here...
// due to earlier scaling, adding 1000 is equivalent to adding 1 to the original
long longResult = roundedWorker + 1000;

double threeDigitPreciseResult = longResult / 1000d;
System.out.println("Adding 1 to original number as a long: " + threeDigitPreciseResult);

Update

After getting a better explanation of the problem, it sounds like what you're looking for is the functionality provided by DecimalFormat. Below is a method roundToTwoDecimals() which uses it, along with a test case demonstrating it:

import java.text.DecimalFormat;

import org.junit.Test;

public class ExampleTest {
  @Test
  public void test() {
    double num1 = 29334.32942032432;
    double num2 = 438.95940;
    double result = num1 / num2;
    System.out.println("Before rounding: " + result);

    double rounded = roundToTwoDecimals(result);
    System.out.println("After rounding: " + rounded);
  }

  public double roundToTwoDecimals(double d) {
    DecimalFormat twoDForm = new DecimalFormat("#.##");
    return Double.valueOf(twoDForm.format(d));
  }

}

Which prints out:

Before rounding: 66.82697629968585
After rounding: 66.83
Community
  • 1
  • 1
Cuga
  • 17,668
  • 31
  • 111
  • 166
  • but again... for accuracy, you should be using `double` one would think – Cuga Sep 28 '12 at 18:54
  • OK, I am sorry, I think I got my question worded wrong. What I basically have is a value submitted to the code as double, as this is assumed to be precise to exactly to decimal places. The point is, having various operations performed starting from this value, I get floating point inaccuracy. (Eg: 2.00 - 1.00 = 0.00999999) Therefore I need to find a solution to this problem. From what I understood, converting to "long" would avoid this problem, but I'd suppose also rounding to two dp would work, right? – MrD Sep 29 '12 at 00:06
  • Ok! Thank you very much! I will test this as soona as I get home! Dario – MrD Sep 29 '12 at 12:02
  • I tried doing as you suggested: DecimalFormat df = new DecimalFormat("#.00"); But then whenever I try using it: Eg: double x = df.format(y-z); java tells me it was expecting a double but obtained a string :( – MrD Sep 30 '12 at 12:59
  • Try the code in the `roundToTwoDecimals` method above. It returns `Double.valueOf()` to go from a String to a double. – Cuga Oct 01 '12 at 13:36
0

If you wanna cast double to long you do below.

    double importedValue = 8.0;
    long importedValueConverted = (long) 8.0;
    System.out.println(importedValueConverted/(long)8);

OUTPUT: 1

double importedValue = x;
double importedValueConverted = (long) x;

you were trying to cast a double to long and reassign the casted value to a double. you should assign it to long.

PermGenError
  • 45,977
  • 8
  • 87
  • 106
0

You're casting x to a long than trying to assign it to a double.

That doesn't make sense.

If you want a long, you should use a long.

long longValue = (long) 4.64;

jahroy
  • 22,322
  • 9
  • 59
  • 108
  • This is what I'm doing, but it isn't working... double userCashD = keyboard.readDouble(); long userCash = (long) userCashD; As you can see, I am assigning userCashD first t double and then attempting to cast it to long... – MrD Sep 28 '12 at 19:19
  • @DarioPanada - your question indicates otherwise. – jahroy Sep 28 '12 at 19:21
0

Why not look at BigDecimal. It works well when I have used it. Be careful using the Double ctor though as Double is not that precise (eg it cannot accurately store 0.1). It may be more useful to use the String ctor for BigDecimal

RNJ
  • 15,272
  • 18
  • 86
  • 131