4

I am currently writing an immutable multipurpose vector class in Java that essentially wraps a double[]. The class will be part of a game library, used to calculate entity positions, physics, pixel coordinates etc. I aim to make them also usable for other (non-game related) calculations.

The vector class provides basic operations such as (a and b are vectors):

  • a.length() (returning the length of the vector in Euclidean space)
  • a.add(b), a.scale(b) (adding or multiplying all vector components)
  • a.dot(b), a.cross(b) (calculating dot and cross product of a and b).

I am debating whether I should allow NaN, positive and negative infinity as vector values and whether I should distinguish between +0 and -0. Are there any reasons to why allowing non-finite doubles should be allowed? Is it even practical to implement operations for these methods - how should e.g. length() and scale(..) behave?

Another important concern I have are the methods a.hashCode() and a.equals(b). Should this be true:

new Vector(+0D, +0D, +0D).equals(new Vector(-0D, -0D, -0D));

Considering that

Arrays.equals(new double[] { +0D, +0D, +0D }, new double[] { -0D, -0D, -0D }) == false
  • Can you elaborate how your vector class would be used and what the semantics of length and scale would be? – Bart van Ingen Schenau Nov 3 '15 at 19:05
  • @BartvanIngenSchenau Thanks - I adapted the question. – Frithjof Nov 3 '15 at 19:13
  • equals() should provide the most basic, field by field equality. Basically what equals() says is that you can replace one object with the other and there would be no noticeable difference. You should however also provide an epsilon-based proximity check, like you do with plain doubles. This is not a real equality operator, as it isn't necessarily transitive, so it should never be used as equals(), but it is really useful otherwise. – biziclop Nov 27 '15 at 10:38
2

Following the principle of least surprise, I think you should make your vector behave the same as the underlying numbers. Java is explicit about how floating-point is supposed to work (unlike C, for example), referencing IEEE 754.

So, yes, do allow non-finite values (infinity and NaN) and ±0. The one who uses your class will generally know best at what point to handle special values – if they need special handling at all – and you (in your vector class) cannot do much about them anyway. This will also greatly improve your performance because otherwise, you'd need countless conditionals all over the place and you'd probably still miss some cases anyway.

The same reasoning applies to implementing equality. If u and v are vectors of your class, then u.equals(v) should be true if and only if u.at(i) == v.at(i) is true for every valid index i.

1

How would you handle arithmetic operations that would return NaN? Your options are to either:

  1. allow returning non-finite values
  2. make sure that those operations throw exceptions, or
  3. let them return null.

Number one reminds me of the null object pattern, and number three sounds like sacrilege if you're trying to make robust code without checking for null everywhere. Number two sounds feasible but exception handling sounds like clutter. It's up to you, but you'll be creating NaN's no matter what.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.