I have a legacy project and it has math that uses float and long variables like this:
long i = initWihtVeryBigNumber();
float multiplier = 1.3f;
i *= multiplier;
this basically cause round error due to float size and this has to be replaced with:
i = (long)( i* (double)multiplier );
or multiplier itself should be double instead of float.
So the question if I change through entire project float to double could this cause any unpredictable behavior or not ? If so can you provide any example that can produce a problem ?
So the question if I change through entire project float to double could this cause any unpredictable behavior or not ? If so can you provide any example that can produce a problem ?
Yes. Changing (float) to (double) could cause issues, whenever the method requires a float. For example,
Float.toString((double) 1.0f);
Is a compilation error.
Edit
I'm interested in run-time errors rather then in compilation errors.
Okay. You have a third party library that returns a List<Float> (so testIt() below is from a third party). If you blindly cast elements from that List to Float you will get a run-time error.
public static List<Float> testIt() {
List<Float> al = new ArrayList<Float>();
al.add(1.0f);
return al;
}
public static void main(String[] args) {
List<?> al = testIt();
for (int i = 0; i < al.size(); i++) {
Object o = al.get(i);
Double v = (Double) o;
System.out.println(v);
}
}
Another difference is that unlike float, writes to non-volatile double fields aren't guaranteed to be atomic.
It is an unlikely scenario that only affects multi-threaded systems that haven't been synchronized correctly but boy does it cause unexpected runtime problems.
Note that you're still going to get rounding errors even if you change floats to doubles. Example: suppose we set i to 3703816849309525656 (= 3 * 0x1122334455667788, just a number I tried as an experiment).
long i = 3703816849309525656L;
float multiplier = 1.3f;
i *= multiplier;
System.out.println(i);
outputs
4814961529147359232
long i = 3703816849309525656L;
double multiplier = 1.3;
i *= multiplier;
System.out.println(i);
outputs
4814961904102383616
But using a calculator program (GNU bc):
(3703816849309525656 * 13) / 10
4814961904102383352.8
So using a double gets you a lot closer, but the result is still incorrect; and when one deals with integers, one normally expects exact results. Therefore, I'd consider using BigInteger or BigDecimal, although I realize that change would require a lot more effort.
Related
I've got a really annoying task to do, and stuck with it.
So: I need to write a function which gives back the value of a floating number after the decimal.
For example: the param would be:5.456-> and the returning value should be:456.
I can not use String (of course this would be easy this way).
Do you have any suggestions?
It requires some steps to do it with primitives like float or double. If you were allowed to use BigDecimal, this would just be one line of code.
Given double d = 5.456;
first, cut off the part before the floating point.
do this by int full = (int)d; which will be 5
the subtract full from it: d-full will now be only the part after the point, so .456
now use a loop to multiply the value by 10 until the "after the point" part is 0.
The special thing here is that when you use double, you have floating point precision issues. That means that d will have the value 0.4560000000000004 in between. To solve that, let's introduce an epsilon.
The full code looks like this:
private static final double EPS = 1e-5;
public static void main(String[] args) {
double d = 5.456;
System.out.println(afterDot(d));
}
private static int afterDot(double d) {
d = getDecimals(d);
while(getDecimals(d) > EPS){ //decimals will likely never be 0 because of precision, so compare with a really small EPS instead of 0
d *= 10;
}
//cast the result to an int to cut off the double precision issues
return (int)d;
}
private static double getDecimals(double d) {
int full = (int) d;
d = d-full;
return d;
}
This prints 456. I am very sure this can be optimized somehow, but it's a working first draft.
What you want is the remainder, multiplied by 10 until the remainder is 0. Using BigDecimal to prevent rounding issues that looks like this:
final BigDecimal input = new BigDecimal("5.456");
BigDecimal x = input.remainder(BigDecimal.ONE);
while (x.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) > 0) {
x = x.multiply(new BigDecimal(10));
}
System.out.println(x.longValue());
Here are two ways that don't adjust for floating point anomalies.
double s = 5.456;
System.out.println(s%1);
or
System.out.println(s-(int)s);
both print
0.4560000000000004
Or use BigDecimal and adjust the scale and subtract the integer value.
System.out.println(BigDecimal.valueOf(s)
.setScale(3)
.subtract(BigDecimal.valueOf((int)s)));
prints
.456
I have a weird division problem with floats and doubles. I want to do a variety of divisions and multiplications.
See the following code:
List<Double> values = List.of(25.0, 25.0);
System.out.println(values);
// prints: [25.0, 25.0]
float totalValue = 0f;
for (Double value : values) {
totalValue = totalValue + value.floatValue();
}
float numValues = (float) values.size();
float completeValue = totalValue / numValues;
// completeValue: 6.25, totalValue: 50.0, numValues: 2.0
float rndValue = 50f / 2f;
// rndValue: 25.0
String oneDpValue = String.format("%.1f", completeValue);
return Double.valueOf(oneDpValue);
I'm confused as to why completeValue comes back completely wrong. I've added rndValue just for comparison when values aren't taken from variables and it returns the correct result.
Note:
Values in the comments are produced by a debugger in IntelliJ. I've also attempted at simply printing totalValue and numValues. Both come back as 50.0 and 2.0, respectively.
Could someone kindly explain why this is?
I have looked around for similar problems and tried using a suggestion (for a somewhat similar problem) to use BigDecimal, however, it still produced the same result.
Thanks!
First of all, thank you all for your input.
It turns out the completeValue was cached (even though I did reset the cache and restart my IDE). It simply took re-writing the same line and the correct result was being produced.
float completeValue = totalValue.floatValue() / numValues; // completeValue: 6.25;
float completeNewValue = totalValue.floatValue() / numValues; // completeNewvalue: 25.0
I must say I've never seen this happen in the past, however I hope it helps someone who has a similar issue in the future.
I want to use a method that requires an int. This int is determined by a division potentially solved as a double. I need to perform this as neat and short as possible and I am wondering if I can count that the method will take the double directly casted as int and if this means a single truncation with no roundings.
Do I have to necessarily use Math static methods?
Could this give errors for non int parameter entries to subList?
Could someone provide any guidance about this?
List<Integer> b = null;
List<Integer> c = null;
int size = a.size();
b.addAll(a.subList(0, size / 2)); // To hold the first half
c.addAll(a.subList(size / 2, size)); // To hold the second half [and excess]
Thank you in advance for your help.
it will take the double casted to integer and will not round it, just disregard everything after the "."
so if the division of 9.8/2 is 4.9 then you'll get 4 for doing
int x = 9.8/2;
you don't need to use the Math static methods for devision and you won't get errors for the code you gave
to conclude, you can just run your code and see if the result is as you want it.
b.addAll(a.subList(0, size / 2));
should run without problem
What's wrong with b.addAll(a.subList(0, (int)(size / 2)));?
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?
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;
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.
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
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;
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.
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
What is the best way to convert a double to a long without casting?
For example:
double d = 394.000;
long l = (new Double(d)).longValue();
System.out.println("double=" + d + ", long=" + l);
Assuming you're happy with truncating towards zero, just cast:
double d = 1234.56;
long x = (long) d; // x = 1234
This will be faster than going via the wrapper classes - and more importantly, it's more readable. Now, if you need rounding other than "always towards zero" you'll need slightly more complicated code.
... And here is the rounding way which doesn't truncate. Hurried to look it up in the Java API Manual:
double d = 1234.56;
long x = Math.round(d); //1235
The preferred approach should be:
Double.valueOf(d).longValue()
From the Double (Java Platform SE 7) documentation:
Double.valueOf(d)
Returns a Double instance representing the specified double value.
If a new Double instance is not required, this method should
generally be used in preference to the constructor Double(double),
as this method is likely to yield significantly better space and time
performance by caching frequently requested values.
(new Double(d)).longValue() internally just does a cast, so there's no reason to create a Double object.
Guava Math library has a method specially designed for converting a double to a long:
long DoubleMath.roundToLong(double x, RoundingMode mode)
You can use java.math.RoundingMode to specify the rounding behavior.
If you have a strong suspicion that the DOUBLE is actually a LONG, and you want to
1) get a handle on its EXACT value as a LONG
2) throw an error when its not a LONG
you can try something like this:
public class NumberUtils {
/**
* Convert a {#link Double} to a {#link Long}.
* Method is for {#link Double}s that are actually {#link Long}s and we just
* want to get a handle on it as one.
*/
public static long getDoubleAsLong(double specifiedNumber) {
Assert.isTrue(NumberUtils.isWhole(specifiedNumber));
Assert.isTrue(specifiedNumber <= Long.MAX_VALUE && specifiedNumber >= Long.MIN_VALUE);
// we already know its whole and in the Long range
return Double.valueOf(specifiedNumber).longValue();
}
public static boolean isWhole(double specifiedNumber) {
// http://stackoverflow.com/questions/15963895/how-to-check-if-a-double-value-has-no-decimal-part
return (specifiedNumber % 1 == 0);
}
}
Long is a subset of Double, so you might get some strange results if you unknowingly try to convert a Double that is outside of Long's range:
#Test
public void test() throws Exception {
// Confirm that LONG is a subset of DOUBLE, so numbers outside of the range can be problematic
Assert.isTrue(Long.MAX_VALUE < Double.MAX_VALUE);
Assert.isTrue(Long.MIN_VALUE > -Double.MAX_VALUE); // Not Double.MIN_VALUE => read the Javadocs, Double.MIN_VALUE is the smallest POSITIVE double, not the bottom of the range of values that Double can possible be
// Double.longValue() failure due to being out of range => results are the same even though I minus ten
System.out.println("Double.valueOf(Double.MAX_VALUE).longValue(): " + Double.valueOf(Double.MAX_VALUE).longValue());
System.out.println("Double.valueOf(Double.MAX_VALUE - 10).longValue(): " + Double.valueOf(Double.MAX_VALUE - 10).longValue());
// casting failure due to being out of range => results are the same even though I minus ten
System.out.println("(long) Double.valueOf(Double.MAX_VALUE): " + (long) Double.valueOf(Double.MAX_VALUE).doubleValue());
System.out.println("(long) Double.valueOf(Double.MAX_VALUE - 10).longValue(): " + (long) Double.valueOf(Double.MAX_VALUE - 10).doubleValue());
}
Simply by the following:
double d = 394.000;
long l = d * 1L;
Simply put, casting is more efficient than creating a Double object.