I'm writing an isometric game that renders objects based on their Y coordinate, using a comparable class, sorting by their Y value, which changes. I am getting the error "Comparison method violates its general contract!" and have read about how to return a negative number, 0, or a positive number so I have implemented this:
public boolean equals(Entity e) {
if ((e.y-y)==0)
return (e.id == id);
return (e.y == y);
}
public int compareTo(Entity e) {
if ((e.y-y)==0)
return (e.id - id);
return (int) (e.y - y); // Render order by y coordinate
}
But I'm still getting the error. Is the sorting not going to work if the values change or am I doing something else wrong?
The equals method is not involved in the contract, so we can ignore it.
I suspect that the problem is caused by integer overflow. The problem is that x - y does not always give you a positive answer if x > y and a negative number if x < y. If the difference between the numbers is large enough, then the express x - y will overflow and the result will have the wrong sign.
If that is the problem, then the simple solution is to use Integer.compare(x, y) instead of x - y
Another possibility is that the entities are being mutated at the same time as you are (for example) sorting them.
Float.compare(x, y) has worked much better.
I'd assumed that x and y were int. If they are float then the true cause of the problem is harder to understand. Either way, using Float.compare(x, y) is a better solution.
But if x and y are actually int, then using Float.compare(x, y) will give you incorrect answers for some x and y values. For close values of x and y with large enough magnitudes, the int to float conversion will lose precision, and Float.compare will say they are equal.
Related
We are often taught that floating-point numbers should not be compared for exact equality. However, the following function, which returns the Golden Ratio when passed any positive number, does in fact compare doubles for equality and to my surprise it seems to always work:
public static double f(double x) {
double y;
while ((y = 1 + 1 / x) != x)
x = (x + y) / 2;
return x;
}
#Test
void test() {
assertEquals((1 + sqrt(5)) / 2, f(1.0)); // Passes!
}
I thought that maybe it works for some input arguments but not others. But even if I use JQwik's property testing, it still works!
#Property
void test2(#ForAll #Positive double x) {
assertEquals((1 + sqrt(5)) / 2, f(x)); // Always passes!
}
Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?
You were just lucky, in general you don't get exact equality. Try this for example:
public static void main(String[] args) {
var s = 0.0;
for (int i = 0; i < 10; i++) {
s += 0.1;
}
System.out.println(s == 1.0);
}
In your concrete example one would have to do a careful analysis to prove that your iteration always converges to the floating point number closest to phi. If sqrt also returns the closest floating point number to the exact root we would get exact equality.
... and to my surprise it seems to always work:
Not always.
When I tried f(-1/1.6180339887498949), the x and y values oscillated between two floating point values that differed in the last few bits #Steve Summit. Thus an infinite loop.
x:-0.61803398874989490 y:-0.61803398874989468 // Decimal notation
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1 // Hex notation
x:-0.61803398874989479 y:-0.6180339887498949
x:-0x1.3c6ef372fe94fp-1 y:-0x1.3c6ef372fe950p-1
x:-0.61803398874989490 y:-0.61803398874989468
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1
f(some_starting_x) generally converges to render an x, such that 1 + 1 / x is x again and so meeting the stopping condition.
Better routines can prove that if x is reasonably close, the while loop will eventually get close to the desired answer, yet even then, an oscillation, as shown above is possible. Thus using an iteration limit or close enough test is needed. Usually the 2 oscillation values, when close, they are massaged (e.g. averaged) to form the best answer. If not close, the looping simply failed to find a stable answer.
Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?
Inadequate testing.
Morale of the story:
Do not rely on only floating point equality, except in select cases.
f() was not a select case and deserved additional stopping code.
Ref: Two x with math property: x = 1 + 1/x:
x1 = 1.6180339887498948482045868343656...
x2 = -0.61803398874989484820458683436564...
Note x1*x2 == -1. x1 is the Golden_ratio φ.
I don't think I need code here, but just so you can see what I'm looking at:
public class Valuation {
//line is a monotonic (non-decreasing. Could be constant at points)
//line in 2D space where x=0 -> y=0 and x=1 -> y=1
//the gradient cannot be infinite
//line is only defined between x=0 and x=1. Can catch when arguments to
//functions are unacceptable given this.
LineEquation line;
float cut(float from, float value){
//Using 'from' as x, return the least value x' where 'value' is the difference
//between the y value returned by x and the y value returned by x'
}
float eval(float from, float to){
//require to > from
//return the difference between the y value returned by 'to'
//and the y value returned by 'from'
}
The question I have is how do I represent a line/curve like this in Java? I can verify the lines given fit the requirements that I have, but I want to have this LineEquation class to be able to handle essentially any line that fits these requirements. These could be quadratic curves or lines where we have something like, when x is between 0 and 0.5, the equation is a, and then when x is between 0.5 and 1, the equation is b. I got frustrated thinking of all the ways you could describe a line that meets the specifications, and then how I would go through them all, and how I would have to deal with all the different types in different ways. Unfortunately I do not have the vocabulary to find a library that has what I want.
If you're using Java 8, then probably the simplest thing to do would be to store the curve as a Function<Float,Float>, which can implement any kind of equation for any kind of curve, provided y is single-valued for any given x, and x always falls within range for a float.
Your class might look like this.
public class Valuation {
final Function<Float,Float> curve;
public Valuation(final Function<Float,Float> curve) {
this.curve = curve;
}
float eval(float from, float to){
return curve.apply(to) - curve.apply(from);
}
}
Then you can create these with calls such as
new Valuation( x -> ( x * x + 2 * x + 3 ))
for a typical monotonic quadratic, or
new Valuation( x -> ( x > 0.5 ? 3 * x : 1 + x ))
for a piecewise function consisting of two linear sections.
I haven't shown the code for cut. Had to leave something up to you!
I would like to use a HashMap
to map (x, y) coordinates to values.
What is a good hashCode() function definition?
In this case, I am only storing integer coordinates of the form (x, y)
where y - x = 0, 1, ..., M - 1 for some parameter M.
To get unique Value from two numbers, you can use bijective algorithm described in here
< x; y >= x + (y + ( (( x +1 ) /2) * (( x +1 ) /2) ) )
This will give you unquie value , which can be used for hashcode
public int hashCode()
{
int tmp = ( y + ((x+1)/2));
return x + ( tmp * tmp);
}
I generally use Objects.hash(Object... value) for generating hash code for a sequence of items.
The hash code is generated as if all the input values were placed into an array, and that array were hashed by calling Arrays.hashCode(Object[]).
#Override
public int hashCode() {
return Objects.hash(x, y);
}
Use Objects.hash(x, y, z) for 3D coordinates.
If you wish to handle it manually, you could do compute hashCode using:-
// For 2D coordinates
hashCode = LARGE_PRIME * X + Y;
// For 3D coordinates
hashCode = LARGE_PRIME^2 * X + LARGE_PRIME * Y + Z;
To calculate a hash code for objects with several properties, often a generic solution is implemented. This implementation uses a constant factor to combine the properties, the value of the factor is a subject of discussions. It seems that a factor of 33 or 397 will often result in a good distribution of hash codes, so they are suited for dictionaries.
This is a small example in C#, though it should be easily adabtable to Java:
public override int GetHashCode()
{
unchecked // integer overflows are accepted here
{
int hashCode = 0;
hashCode = (hashCode * 397) ^ this.Hue.GetHashCode();
hashCode = (hashCode * 397) ^ this.Saturation.GetHashCode();
hashCode = (hashCode * 397) ^ this.Luminance.GetHashCode();
return hashCode;
}
}
This scheme should also work for your coordinates, simply replace the properties with the X and Y value. Note that we should prevent integer overflow exceptions, in DotNet this can be achieved by using the unchecked block.
Have you considered simply shifting either x or y by half the available bits?
For "classic" 8bit thats only 16 cells/axis, but with todays "standard" 32bit it grows to over 65k cells/axis.
#override
public int hashCode() {
return x | (y << 15);
}
For obvious reasons this only works as long as both x and y are in between 0 and 0xFFFF (0-65535, inclusive), but thats plenty of space, more than 4.2bio cells.
Edit:
Another option, but that requires you to know the actual size, would be to do x + y * width (where width ofc is in the direction of x)
That depends on what you intend on using the hash code for:
If you plan on using it as a sort of index, E.g. knowing x and y will hash into an index where (x, y) data is stored, it's better to use a vector for such a thing.
Coordinates[][] coordinatesBucket = new Coordinates[maxY][maxX];
But if you absolutely must have a unique hash for every (x, y) combination, then try applying the coordinates to a decimal table (rather than adding or multiplying). For example, x=20 y=40 would give you the simple and unique code xy=2040.
I was interviewed yesterday and this question arise:
public class Main {
public static void main(String[] args) {
// Code fragment must prints true if the double variables x and y are both
// strictly between 0 and 1 and false otherwise.
// Assuming args will be always correct (double) input,
// Where's the safety bug?
double x,y;
x = Double.parseDouble(args[0]);
y = Double.parseDouble(args[1]);
System.out.println( (0 < x && x < 1) && (0 < y && y < 1)? true : false );
}
}
I wrote there were no bug anywhere, I mean, code works. Back at home, tried and indeed it works. But although I did get the job, this question was my only bad one, and I can't get it out of my head (it seams fair straight simple). I believe I don't even understand what a safety bug is so, my question then is:
What's a safety bug and does this code indeed have one?
UPDATE:
After response I put some related resources:
Eric Lippert's Why does JScript have rounding errors?
Judging from the comment in the source:
Assuming args will be always correct (double) input, Where's the safety bug?
There exists a case in which the program will deliver a wrong output, and there seems to be no dubious except the expression itself:
(0 < x && x < 1) && (0 < y && y < 1)
Although I haven't tested it, there may be a problem when this expression is confronted with one of the edge cases for either x or y: NaN, +/- infinity and maybe even -0.0.
Also there exist definitively valid inputs which may violate the logical expectation of the user due to the limited precision with which number are represented in a double (e.g. x = 1E-400 is greater than 0, but parses as 0, so the expression delivers false although the user would expect true).
I would go with type safety and uncaught exception if the input somewhat happens to not be double. Then the code just throws an exception.
You can set x to 0, and you can enter 0 for y. But the values must be between.
You better use strictfp.
You better use 0d instead of 0.
I'm doing some small program for a beginners programming course and in my program I have 2 variables which hold numbers. Anyway I need to find out which number is bigger and print the appropriate message according to it, for example I have:
int x = 5;
int y = 10;
I need to print:
"it is true that y is bigger than x";
Now the thing is that I know I can use a simple if statement but I'm not allowed to use it, now it makes me wonder, is it even possible? If so, how can I do that? How can I check which number is bigger WITHOUT doing something like:
if (x > y)
answer = true;
...
Thanks in advance.
Well you can do:
boolean answer = x > y;
The expression x > y is just an expression of type boolean. While boolean expressions are often used for conditions in if statements, loops etc, they don't have to be - simple assignment works fine too.
It sounds like you want the reverse though:
boolean answer = y > x;
Then you can use the value of answer to build the string to display...
Use the ternary operator:
System.out.println(x > y ? "It is true that x is greater than y" : "");
ternary operator "?:"
String output = (x > y)? "x is greater than y":"y is greater than x";
The ternary conditional operator that others mentioned will work. Assuming you are looking for creative ways to do this rather than practical ones, here's another method:
int x = 5;
int y = 10;
while(y > x){
System.out.println("It is true that y is bigger than x.");
return;
}
System.out.println("It is false that y is bigger than x.");
The while is just acting as a fancy if, because the return means the otherwise infinite loop will only execute at most once.
Here's another example that instead relies upon short-circuit boolean evaluation:
public static void main(String...args){
int x = 5;
int y = 10;
boolean answer = (y > x);
boolean testTrue = answer && printTrue();
boolean testFalse = testTrue || printFalse();
}
private static boolean printFalse() {
System.out.println("It is false that y is bigger than x.");
return true;
}
private static boolean printTrue() {
System.out.println("It is true that y is bigger than x.");
return true;
}
Of course you shouldn't do this in real production code, but it can be fun to think of unorthodox ways to code something and it can be helpful for exploring the language.
Your question is tagged as Java but you do not specify Java in your question. In Java there are multiple ways to get the same result that involve testing the boolean expression x > y somehow, such as the ternary operator. I would consider these equivalent to an explicit if statement.
Other possibilities:
Compute the square root of x - y. This will raise an exception if y is bigger. Catch the exception in the caller and report that y is the larger quantity. If there is no exception, report that x is the larger.
In LISP, Ruby or another language that supports the symbol type, form a list ((symbol x, x), (symbol y, y)) and sort the list. Then report the second symbol as the variable with the larger value.
If using assembly, BASIC, PL/1, etc. you can use an arithmetic expression to choose the target of a GOTO statement. Depending on whether x or y is larger, execution will resume at a different part of the code. Or use the list-sorting trick in the previous bullet to select the GOTO label.
In general, the expression ((x - y) / abs(x - y) + 1) / 2 will produce 1 if x is larger and 0 if y is larger. This result could be used to choose data, a function, etc. out of a list of two alternatives, producing conditional behavior without an if statement.
You could use recursion (but I would not recommend it)
public int compare ( int a , int b )
{
switch ( a )
{
case Integer.MIN_VALUE :
switch ( b )
{
case Integer.MIN_VALUE :
return 0 ;
default :
return -1 ;
}
default :
switch ( b )
{
case INteger.Min_VALUE :
return 1 ;
default :
return compare ( a-1 , b-1 ) ;
}
}
}
(a+b)/2 + Abs(a-b)/2 is the bigger number.
I know in some languages you can use short-circuit evaluation to construct the answer.
The expression (A && B) always evaluates to B if A is true. If A is false then B is never evaluated.
Similarly (A || B) evaluates to B if A is false. If A is true B is never evaluated.
Though I'm not 100% sure of Java, the expression you want is:
String output = ((x > y) && "it is true that X is greater than Y")
|| (((x < y) && "it is true that X is less than Y")
|| "it is true that X is equal to Y");