1

I'm working on a particle engine, in Eclipse on Windows 7, and I have a problem I've never seen before. I have this in my code:

float start = startPosition.getY();
p.position.addTo(p.moveVector);

float end = startPosition.getY();
if(start != end){
System.out.println("hit");
}

And it does print hit quite often. The reason I did this was because startPosition was being changed and it was messing up my program.

I also went into the debugger and looked at the values when it printer hit, and I got this for end, all variables in position and moveVector: 1.0E-5 I have no idea what to make of that.

Here is my code.

Particle Emitter http://tinyurl.com/9ahwodx

Particle http://tinyurl.com/8rw979d

Vector 3f http://tinyurl.com/9do6v2k

The code in question is at the bottom of ParticleEmitter.

If you want to see or know anything else let me know, but I think that's all the relevant info.

Elec0
  • 2,232
  • 3
  • 19
  • 25
  • 2
    I doubt they are "changing randomly" (this would be a serious bug that **would make Java/JVM unsuitable for any serious development**). So, eliminating that line of thought for a minute, what are *other* possibilities for this behavior? Are `startPosition` and `position` *the same object*, perhaps? If not, *do they mutate/update the same variables*? Or, *is there a thread involved*? –  Aug 14 '12 at 03:12
  • 1
    Let's see now... either java is fundamentally flawed, or you have a bug in your code. Gee, that's a tough one! May I recommend putting breakpoints in the relevant object's setters and examining the call stack to see *who* is setting the values. You may just find out why they are "randomly" changing. Also, sgusc's answer has good info. Try printing the difference between `start` and `end`: You may find it is tiny and therefore an artefact of floating point arithmetic – Bohemian Aug 14 '12 at 03:16
  • 1
    Q: Is it possible that maybe your have a thread (or threads!) that you're not telling us about, that might be updating values behind your back ;)? ALSO: as sgusc correctly pointed out, you *cannot* reliably compare two floating point values for exact equality. This is true for *any* floating point implementation in *any* language: but especially true for (single-precision) "float" values. 'Hope that helps .. PSM – paulsm4 Aug 14 '12 at 03:18
  • I think there's a much more fundamental problem here. Using == or != to compare two `float` variables is never going to work like you want it to. If it still exhibits strange behavior after that is fixed, then threading is a more likely cause. – Brent Writes Code Aug 14 '12 at 03:19
  • 1
    The fact Elec is asking this on SO rather than posting a bug report suggests they're after help debugging. +1 for the links to the full source code. – Martin Aug 14 '12 at 03:31

2 Answers2

6

Your particleEmiter constructor creates a bunch of Particle objects, with startPosition as the position for each.

Java passes things by reference [sharing objects (the object "references" are passed by value, so everyone shares the same object)], so each of your Particle instances refers to the same Vector3f object for their position.

When you do p.position.addTo(...), you're changing all of your Particle's positions, as well as startPosition.

You need to clone the position when calling the Particle constructor.

Edit:

Clone is just a term for "constructing a new object that has the same values". You can do this on a case by case basis by passing:

new Vector3f(startPosition.x, startPosition.y, startPosition.z)

instead of startPosition.

That's not very robust though, because if Vector3f were to gain some state, you'd need to update those calls. See Cloneable for an interface that would let you just call startPosition.clone() instead. [Err, brain freeze]

Edit 2: Or as points out, put a constructor on Vector3f that takes a Vector3f parameter. Your code then becomes new Vector3f(startPosition) when you're passing the position to the Particle constructor.

Martin
  • 3,703
  • 2
  • 21
  • 43
  • Okay, but I thought I'd read in more than one place that Java didn't pass variables by reference ever? Obviously you're disagreeing, but I'm curious as to how this works and how to go about cloning the variable. – Elec0 Aug 14 '12 at 03:29
  • @Elec0 - He is not contradicting that. In this case, Java is passing a reference (specifically the reference to the initial position object) by value. In this case, you can clone the position by creating a new position with the same x/y/z values. – Stephen C Aug 14 '12 at 03:31
  • 1
    @Elec0 Reading this might help: http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html – Brent Writes Code Aug 14 '12 at 03:33
  • Java *always* passes objects by reference, and primitives by value. Anything you see suggesting otherwise is playing linguistic games (the references themselves are passed as a value, so you can't change the object a parameter refers to in a way that affects the caller). See http://www.javaworld.com/javaqa/2000-05/03-qa-0526-pass.html. – Martin Aug 14 '12 at 03:35
  • @Martin Well if you're going to go ahead and get the answer right, I have to at least beat you to the reference link :-P – Brent Writes Code Aug 14 '12 at 03:36
  • Okay, that makes sense now. So how would you go about cloning the variable? I already set it to a different variable, but that's obviously just using the reference. – Elec0 Aug 14 '12 at 03:36
  • 2
    More links! http://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java. The easiest thing to do is create a constructor of the form `public Vector3f(Vector3f original)` that creates a new Vector3f from the original. – Brent Writes Code Aug 14 '12 at 03:38
  • You should probably create an interface called ICloneable, so that every object implement's a reliable and known way. =) You also then have a standard. – Vaughan Hilts Aug 14 '12 at 03:41
  • Nit: Java *does not* use "Call By Reference". Java *is* "Call By Value" where the "Value" for object types that of the "Reference". The semantics can be described with "Call By [Object] Sharing". "Call By Reference" *means something else*. The Wikipediate article [Evaluate Strategy](http://en.wikipedia.org/wiki/Evaluation_strategy) is a good place to *start* to define common terms; even though it acknowledges some lee-way and various localized usages, to use "Call By Reference" to talk about Java is *incorrect* parlance per semantics and per the JLS. –  Aug 14 '12 at 03:45
  • @pst: Fair point, I'm going to start calling it call-by-object-sharing. I refuse to call it call-by-value, as that confuses novices to no end. – Martin Aug 14 '12 at 03:50
  • @Martin I prefer Call-By-Object-Sharing because it's high-level and transfers unambiguous semantics for a numerous languages :) When talking about Java though, the JLS always gets dragged in .. +1 for digging through the code. –  Aug 14 '12 at 03:51
2

To answer your first question, 1.0E-5 is essentially zero.

Overall, I think your problem is that you're trying to compare float variables for equality which is never a good thing. My guess is that the values aren't actually "changing", but rather each time you run the floating point approximation used for start and end are getting represented a bit differently. This would explain the behavior you're seeing where the same code seems to behave a bit differently each time.

Check this out for more details:

What's wrong with using == to compare floats in Java?

Community
  • 1
  • 1
Brent Writes Code
  • 19,075
  • 7
  • 52
  • 56
  • 2
    But if `startPosition.getY()` evaluated to the *same* value then it be okay .. not that I doubt this could be/is a concern. –  Aug 14 '12 at 03:13
  • No, not in this case. People are too quick to blame genuine bugs on floating point accuracy. Floating point values don't change without a re-evaluation - something this code doesn't have. If the position were not changing, then Vector3f.getY() would return the same value all the time. Additionally, 1E-5 would be a *massive* value to be out by for the magnitudes this program is dealing with. – Martin Aug 14 '12 at 03:24
  • 1
    @sgusc - you are correct, but this does not explain the problem that the OP is having. In fact, I suspect he's only put that test in there as an attempt to figure out why his particles are not moving as he expects. – Stephen C Aug 14 '12 at 03:29
  • 1
    Based on reading the code further, I agree that this isn't the root cause. @Martin is definitely right about the reference reuse. Can we at least all agree that !=/== on floats is a bad practice and then accept Martin's answer as correct? :-D – Brent Writes Code Aug 14 '12 at 03:30