Vector2D class. float or double? [noob-alert]

I’m kinda new to Java so bear with the ignorance as I’m still in the “experimental” stage (currently experimenting with eclipse and how inheritance in Java works).

I generally use floats(or Fixed-point) for all my gaming data but noticed that almost all Math.* constructs return doubles. I know that on 32-bit machines, floats are generally faster than doubles (I have a 32bit machine) but I don’t know if this is true in Java being emulated (or even on jit). I also need a lot of float cast to make things work. Does casting have a speed penalty in Java since it’s emulated?

Anyway feel free to make suggestions regarding my code:



/**
 * 
 */
package net.phatcode.rel;

/**
 * @author Anya
 *
 */

//import java.lang.Math.*;
//import java.util.Random; 


public class Vector2D 
{

	public static final float EPSILON = 0.0000000001f; 

	public float x;
	public float y;
	
	public Vector2D()
	{
		x = 0;
		y = 0;
	}
	
	public Vector2D( float a, float b )
	{
		x = a;
		y = b;
	}
	
	public Vector2D( Vector2D v )
	{
		x = v.x;
		y = v.y;
	}
	
	
	public float magnitude()
	{
		return (float)Math.sqrt( (x * x) + (y * y) );
	}
	
	public float magnitudeSquared()
	{
		return ( (x * x) + (y * y) );
	}
	
	public float dot( Vector2D v )
	{
		return ( (x * v.x) + (y * v.y) );
	}
	
	public Vector2D normal( boolean side )
	{
		return ( side == false ) ? new Vector2D( -y, x ) : new Vector2D( y, -x );
	}
	
	public float normalize()
	{
		float length = magnitude();
		float invMag = 1.0f/length;
		if( length <= EPSILON ) return 0.0f;
		x *= invMag;
		y *= invMag;
		return length;
	}
	
	public void scale( float s )
	{
		x *= s;
		y *= s;
	}
	
	public float angleBetween( Vector2D v )
	{
		return (float)(Math.atan2(v.y - y, v.x - x));
	}
	
	public Vector2D reflect( Vector2D n )
	{
		// n -> normal of the vector *this would reflect from
		// Assumes normal(n) is normalized
		// -2 * (V dot N) * N + V
		// Or
		// -2 * (V dot N)/|N| *N + V
		
		float dot = (x * n.x) + (y * n.y);
		Vector2D n2 = new Vector2D( n.x * -2.0f, n.y * -2.0f );
		n2.scale(dot);
		n2.x += x;
		n2.y += y;
		
		return n2;
	}
	
	public void displayComponents()
	{
		System.out.println( "v.x = " + x + " :: v.y = " + y );
	}
	
	public String toString()
	{
		return ( "v.x = " + x + " :: v.y = " + y );
	}
	
	
	// Static 
	
	public static float magnitude( Vector2D v )
	{
		return v.magnitude();
	}
	
	public static float magnitudeSquared( Vector2D v )
	{
		return v.magnitudeSquared();
	}
	
	public static float normalize( Vector2D v )
	{
		float length = v.magnitude();
		float invMag = 1.0f/length;
		if( length <= EPSILON ) return 0.0f;
		v.x *= invMag;
		v.y *= invMag;
		return length;
	}
	
	public static float dot( Vector2D a, Vector2D b )
	{
		return ( (a.x * b.x) + (a.y * b.y) );
	}
	
	public static Vector2D Projection( Vector2D u, Vector2D v )
	{
		float dotUV = dot(u,v);
		float dotUU = dot(u,u);
		u.scale( dotUV/dotUU );
		return u;
	}
	
	public static Vector2D Normal( Vector2D v, boolean side )
	{
		return ( side == false ) ? new Vector2D( -v.y, v.x ) : new Vector2D( v.y, -v.x );
	}
	
	public static Vector2D Normal( Vector2D v1, Vector2D v2, boolean side )
	{
		Vector2D v = new Vector2D( v2.x - v1.x, v2.y - v1.y );
		return ( side == false ) ? new Vector2D( -v.y, v.x ) : new Vector2D( v.y, -v.x );
	}
	
	public static float Distance( Vector2D a, Vector2D b )
	{
		Vector2D d = new Vector2D( a.x - b.x, a.y - b.y );
		return d.magnitude();
	}

	public static float angleBetween( Vector2D a, Vector2D b )
	{
		return (float)(Math.atan2(b.y - a.y, b.x - a.x));
	}
	
	public static Vector2D reflect( Vector2D v, Vector2D n )
	{
		// Assumes normal(n) is normalized
		// -2 * (V dot N) * N + V
		// Or
		// -2 * (V dot N)/|N| *N + V
		
		float dot = (v.x * n.x) + (v.y * n.y);
		Vector2D n2 = new Vector2D( n.x * -2.0f, n.y * -2.0f );
		n2.scale(dot);
		n2.x += v.x;
		n2.y += v.y;
		
		return n2;
	}
	
	public static Vector2D midPoint( Vector2D a, Vector2D b )
	{
		return new Vector2D( (b.x - a.x)/2.0f, (b.y - a.y)/2.0f );
	}
	
	public static Vector2D randomVector( float maxX, float maxY )
	{
		return new Vector2D( (float)(Math.random() * maxX), (float)(Math.random() * maxY) );
	}

	
}  // end class


Thanks!

You can make your own float Math class. I suggest you using mostly floats, use doubles only when you need bigger precision.

I have no idea how to make that (unless you are referring to something like a native code using FPU calls) for Trig transcendental functions like sin, cos, etc.

I don’t know why the Math.* stuff returns mostly doubles, but generally we all simply cast those doubles into floats.

With “creating your own Math class” Mac70 probably means, you could create your own Math class ontop of Math. So it uses Math.sin, but actually caches the results in an array, so you could access them without actually computing them. LibGDX has done something similar: “MathUtils” on github

You mean LUTs(LookUp Tables)? I used to do that during the DOS days and when coding for the Nintendo DS but I thought FPU calls are now much faster than RAM access these days. I’m assuming that Math.* directly calls the FPU for trig funks.

Thanks!

Just found out that casting from a double to float has a speed penalty. ;*(

In most real world usages, look-up tables will be much slower than basic computation.

[icode]atan2(y, x)[/icode] is one exception of them

I disagree. Look up tables are usually faster because there is no computation needed.

@matheus23: kids and their angles. I don’t see why you need a table lookup for atan2.

@SwampChicken: Basic ALU ops are very fast. Memory access is very slow. The fastest memory read is cached 32-bit data chunk from L1 into a register, which takes 5 cycles. A cache miss might (there a abillion factors here) cost you around 500 cycles. Take a sandybridge machine which (again this is horseshoes and hand-grenades) can retire 4 ops per cycle, you perform 20 basic ops for the first case (simple load of 32-bits of cached data) and around 2000 in the second.