Strange double rounding issue - java

I came across the following silly function here:
public static String findOutWhatLifeIsAllAbout() {
int meaning = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 20; j++) {
for (int k = 0; k < 300; k++) {
for (int m = 0; m < 7000; m++) {
meaning += Math.random() + 1;
}
}
}
}
return String.valueOf(meaning).replaceAll("0*$", "");
}
In summary, the expected result is a string "42", since Math.random() returns doubles "greater than or equal to 0.0 and less than 1.0". In practice, running on an i5 under Ubuntu the resulting strings are similar to "420000011", "420000008"! (meaning sometimes Math.random()'s result is getting rounded up!
To get a grip on what sorts of double values would cause Math.random()'s result to somehow round to 1, I tried this function instead, expecting to see some examples.
public static String findOutWhatLifeIsAllAboutAltered() {
int meaning = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 20; j++) {
for (int k = 0; k < 300; k++) {
for (int m = 0; m < 7000; m++) {
double randomResult = Math.random();
int converted = randomResult;
if (converted > 0) {
System.out.println("Misbehaving double = " + randomResult);
}
meaning += converted + 1;
}
}
}
}
return String.valueOf(meaning).replaceAll("0*$", "");
}
However this formulation always returns "42"! Can anyone offer insight about why the behavior changes in the altered function? Thanks.
Furthermore why does Java let the original function compile at all? Shouldn't there be a loss-of-precision error at the += call?
edit posted the code I wanted you all to see - the "original" version wasn't supposed to have a cast.

There's a small but important difference between the code in the link and the code originally posted here,
meaning += Math.random() + 1; // link
vs.
meaning += (int)Math.random() + 1; // StackOverflow
If the code posted here prints out anything but 42, it's a serious bug.
Here, the result of Math.random() is explicitly cast to int, that must result in 0, then 1 is added, resulting in 1, which then is added to meaning.
The code in the linked post, however performs an implicit cast to int after adding the result of Math.random() to 1 and that to meaning, basically
meaning = (int)(Math.random() + (double)1 + (double)meaning);
Now, if the result of Math.random() is close enough to 1.0, it occasionally happens that the result of the double addition is rounded up, so producing a final result slightly larger than immediately expected.

Shouldn't there be a loss-of-precision error at the += call?
No. In the first case, you're explicitly casting the value returned by Math.random() to an int. In the second case, meaning, converted, and 1 are all integers.
There is, however, a possible loss-of-precision at this line:
int converted = randomResult;
http://ideone.com/1ZTDi

There won't be a loss of precision error at all because they're all ints in this example - you're not actually adding any doubles!
The only line where you could be you're casting the result of Math.random() to an int - so you're still just adding two ints together.
However, even if you were adding doubles to an int then there still wouldn't be because the JLS defines an implicit cast for these types of operators:
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
Source:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.26.2

Loss of precision could only come on these lines as you're doing type conversion:
double randomResult = Math.random();
int converted = randomResult;
As Math.random() returns an double which is between 0.0 inclusive and 1.0 exclusive, the variable converted will only ever contain 0 as it's converted to an int.
For more on the casting from a double > int, see here: Different answer when converting a Double to an Int - Java vs .Net
I've tried your original program on a Java 6 system running on Mac OS X (Lion) and it only outputs 42. Which JRE/JDK are you using?

Related

Why is the lossy conversion warning inconsistent?

The code below shows an error due to lossy conversion because math.pow returns a double, and I store the answer in an integer.
int k = 0
k = k + Math.pow(2, 4);
System.out.println(k);
but this code:
int k = 0
k += Math.pow(2, 4);
System.out.println(k);
Shows no error, why?
My question is: Why is there no error in the second code?
Here, += does implicit cast, where as + operator requires
explicit cast for second operand, otherwise it won’t compile.
So in the first code, we are assigning the right side part (k + Math.pow(2,4)) which becomes double after additon and assigning it to an int type variable at last. And that's an issue of lossy conversion.
If we explain the second code snippet,
int k = 0
k += Math.pow(2, 4);
System.out.println(k);
as per the java documentation we can see it's internal working like this:
int k = 0
k = (int) (k + Math.pow(2, 4));
System.out.println(k);
You can see in the doc here.

Problems with Binary to Decimal conversion in java (arrays)

My assignment is to convert binary to decimal in a JLabel array without using pre-written methods (there's no user input). I have the right idea but for some reason the output is always a little bit off. I've gone through it countless times but I can't find anything wrong with my algorithm and I'm very confused as to why it doesn't produce the correct answer. I'd be very grateful if someone could help me out. Thanks!
Side note: I've read similar discussion threads regarding binary to decimal conversions but I don't understand how to do it with arrays.
Here is a snippet of my code:
private void convert()
{
int[] digit = new int[8]; //temporary storage array
int count = 0;
for(int x = 0; x < digit.length; x++)
{
digit[x] = Integer.parseInt(bits[x].getText()); //bits is the original array
count= count + digit[digit.length - 1 - x] * (int)(Math.pow(2, x));
}
label.setText("" + count);
}
You are following the binary number from left to right but are grabbing the wrong digit. You want the same digit but to multiply by the right power of two - first index being +n*128 and not +n*1
int count = 0;
for(int i = 0; i < bits.length; i++) {
count += Integer.parseInt(bits[i].getText()) * Math.pow(2, bits.length - i - 1);
}
Obviously there is a bug in your snippet.
You set the digit[x], but not set the digit[length - 1 - x].
for example, x = 0, you set the digit[0], but not set digit[7].
So there will be an error when you want use the digit[length - 1 -x] here :
count= count + digit[digit.length - 1 - x] * (int)(Math.pow(2, x));
This the correct code here:
private void convert()
{
int count = 0, length = 8;
for(int i = 0; i < length; count += Integer.parseInt(bits[i].getText()) * (1 << (length - 1 - i)), i++);
label.setText("" + count);
}
Have not test the code. But I think it will work.

can someone explain the steps to compute this equation? Java

Write a program that computes the following equation.
100/1+99/2+98/3+97/4+96/5...3/98+2/99+1/100
I am not asking for a solution. Yes this is a homework problem, but I am not here to copy paste the answers. I asked my professor to explain the problem or how should I approach this problem? She said "I can't tell you anything."
public static void main(String[] args){
int i;
for(i = 100; i >= 1; i--)
{
int result = i/j;
j = j+1;
System.out.println(result);
}
}
You can try to observe a "trend" or "pattern" when solving questions of this type.
Given: 100/1+99/2+98/3+97/4+96/5...3/98+2/99+1/100
We derived: Numerator/Denominator, let's call it n divide by d (n/d)
Pattern Observed:
n - 1 after every loop
d + 1 after every loop
So, if you have 100 numbers, you need to loop 100 times. Thus using a for-loop which loops 100 times will seemed appropriate:
for(int n=0; n<100; n++) //A loop which loops 100 times from 0 - 100
To let n start with 100, we change the loop a little to let n start from 100 instead of 0:
for(int n=100; n>0; n--) //A loop which loops 100 times from 100 - 0
You settled n, now d needs to start from 1.
int d = 1; //declare outside the loop
Putting everything together, you get:
int d = 1;
double result = 0.0;
for (int n=100; n>0; x--)
{
result += (double)n/d; //Cast either n or d to double, to prevent loss of precision
d ++; //add 1 to d after every loop
}
You are on the right track. You need to loop like you've done, but then you need to SUM up all the results. In your example you can try:
result = result + i/j;
or
result += i/j;
Note that the declaration of result needs to be outside the loop otherwise you are always initializing it.
Also think about the division (hint), you are dividing integers...
What you have is a series.
There is more than one way to define a series, but all things being the same it's more intuitive to have the index of a series increase rather than decrease.
In this case, you could use i from 0 to 99.
Which in java can be:
double sum = 0;
for (int i = 0; i < 100; i++) {
sum += (100 - i) / (double) (1 + i);
}
if you want the result in the same format then do :
int j = 100;
double sum=0;
for (int i = 1; i <= 100; i++) {
sum += ((double) j / i); // typecast as least one of i or j to double.
System.out.print(j + "/" + i+"+");
j--;
}
// here print the sum

Calculating Least Common Multiple (how do I know when there isn't one?)

The code below was my first attempt at a LCM (lowest common multiple) calculator with a user interface (UI code not shown) written months ago. I know there are simpler ways to write this, but I'd like help understanding why sometimes THIS specific code is not finding a common multiple (with most number sets it works fine).
When a user inputs almost any number set, the app spits out the correct LCM. But when the number set 1,234 / 2,345 / 5,432 / 4,321 is used, the app initially was stopping when x hit 536,870,912. This was because the result of x * mult was a number that couldn't be held by the int primitive. After changing x to a double and casting result = (int) (mult * x), the code continues to function as expected but seems to increment x indefinitely.
public static void compare(){
result = 0;
int mult = 0;
double x = 1;
int[] nums = UserInterface.getNums();
// finds highest number in user-input set
for(int i = 0; i < nums.length; i ++){
if (nums[i] > mult) mult = nums[i];
}
// finds lowest common multiple
for(int i = 0; i < nums.length;){
if((mult * x) % nums[i] == 0){
result = (int) (mult * x);
i++;
}
else{
result = 0;
x++;
i = 0;
}
}
}
We know the LCM of your test set must be less than or equal to 67,920,681,416,560.
In java the int datatype has a max value of 2^31-1 = 2,147,483,647 so you are obviously going to get an overflow. You can change your code to use long throughout this has a max value of 2^64-1=18,446,744,073,709,551,615 so it should be sufficient for your calculation. If you need bigger values then look at the BigInteger class.
In javascript things are more complicated. All numbers are floating point so you loose accuracy. This probably mean the condition
if((mult * x) % nums[i] == 0)
is never satisfied so your loop never quits.
Your algorithm is very basic, there are much better algorithms out there, elclanrs has one above and see https://en.wikipedia.org/wiki/Least_common_multiple for some hints.
Also you should change the title of the question. As it stands it make no sense as any set of numbers must have a LCM.

Bitwise AND with non-booleans

In the following Java program I cannot understand what this line does:
wert = (wert * mult + inkr) & 0x7FFFFFFFL;
I understand what the bitwise operators do in conditions, but there are principally two numbers (the hexadecimal is the maximum value of Integers in Java). I do not understand, why the & 0x7FFFFFFFL; has even some influence in this line. In my opinion, the variable wert should simply have the value of (wert * mult + inkr) because it's true.
Though I figured out that the & 0x7FFFFFFFL; obviously does have some influence only if (wert * mult + inkr) is negative. Why and what exactly happens in this line?
Annotation: This should be a program for simulating a lottery drawing. I am aware of the error in the program, right where the comment is. But this is not relevant for me right now.
Still it would be great if someone could me tell following not really Java-specific question: what sense got the variables mult and inkr?
public static void main(String args[]) {
int kugeln = 49;
int ziehen = 6;
int mult = 1103515245;
int inkr = 12345;
long wert = System.currentTimeMillis();
int zahlen[] = new int[kugeln];
for(int i = 0; i < kugeln; i++) {
zahlen[i] = i + 1;
wert = (wert * mult + inkr) & 0x7FFFFFFFL;
}
for(int i = 0; i < ziehen; i++) {
int index = (int)(wert / 10) % (49 - i);
int temp = zahlen[49 - i]; // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 49
zahlen[49 - i] = zahlen[index];
zahlen[index] = temp;
wert = (wert * mult + inkr) & 0x7FFFFFFFL;
}
int superzahl = (int)(wert / 10) % 10;
for(int i = 0; i < ziehen; i++) {
System.out.println(zahlen[49 - i]);
}
System.out.println(superzahl);
}
Using bitwise AND with non-booleans will bitwise AND together all of the bits of the two numbers one by one.
In this case, the number 0x7FFFFFFFL is the hexadecimal representation of a number that is a 0 bit followed by 31 1 bits:
01111111111111111111111111111111
By ANDing this with an integer, you preserve the lower 31 bits (since 1 & x = x for any x) and clear the highest bit (since 0 & x = 0 for any x). Since Java uses a 32-bit signed two's-complement representation, this has the effect of clearing the sign bit, forcing the number to be positive.
My guess is that this program is using some sort of rolling hash function where the resulting number has to be positive. To do this, the code constantly updates the integer by combining it with more and more information, and at each step forces the number to be positive by clearing the sign bit.
Hope this helps!
Anding with 0x7FFFFFFFL results in only the 31 lowest bits getting included, the others get set to 0. The result is always positive because the sign bit is masked out.
int temp = zahlen[49 - i]; // Exception in thread "main"
java.lang.ArrayIndexOutOfBoundsException: 49
i is starting from 0 so zahlen[49 ] is called
int zahlen[] = new int[kugeln];
int kugeln = 49;
int a[] = new int [49];
a[49] will throw an exception always. Fom there is your exception.

Categories