Dr. Paul Jarvis
Dr. Paul Jarvis

Reputation: 256

Value comparision in loop, best way to break loop when values are equal?

Take a hypothetical Aircraft class that has two properties; Altitude and AltitudeChange:

public class Aircraft  
{
    public double Altitude { get; set; }
    public AltitudeChange { get; set; }
}

And Altitude change has two properties; Altitude and RateOfClimb:

public class AltitudeChange
{ 
    public double Altitude { get; set; }
    public double RateOfClimb { get; set; } //Negative for descent
}

If we have a 'Worker Thread' that updates the Aircraft's altitude based on the time ellapsed and the Rate of Climb, what's the ideal design/implementation to ensure the loop stops when the new Altitude is hit?

private void AltitudeThreadWork()
{
    var updated = DateTime.Now;

    while (Aircraft.Altitude != AltitudeChange.Altitude)
    {
        UpdateAltitude((DateTime.Now - updated).TotalMilliseconds);
        updated = DateTime.Now;
        Thread.Sleep(40);
    }
}

private void UpdateAltitude(double ellapsed)
{
    Aircraft.Altitude += ellapsed*(AltitudeChange.RateOfClimb/60000d);
}

For example, this thread does't stop the climbing process because the double precision numbers are often not going to exactly equal each other.

Even if you cast the double to an int, you still can't be 100% sure that the two values will equal.

Upvotes: 1

Views: 120

Answers (5)

Cédric Bignon
Cédric Bignon

Reputation: 13022

private void AltitudeThreadWork()
{
    var updated = DateTime.Now;

    bool above = Aircraft.Altitude > AltitudeChange.Altitude;  // Determine if it is a climbing or a descent

    while ((Aircraft.Altitude > AltitudeChange.Altitude) == above)  // Check if it is in the same side of the plane defined by Altitude 
    // (because altitude is a continuous function, if it is on the other "side" it means it has crossed the given altitude)
    {
        UpdateAltitude((DateTime.Now - updated).TotalMilliseconds);
        updated = DateTime.Now;
        Thread.Sleep(40);
    }

    Aircraft.Altitude = AltitudeChange.Altitude;  // Altitude is reached
}

Upvotes: 0

lboshuizen
lboshuizen

Reputation: 2786

    public bool AlttudeReached(double alt1, double alt2, double rateofClimb) {
        return rateofClimb > 0 ? alt1 >= alt2 : alt2 >= alt1;
    }

Upvotes: 1

Roee Gavirel
Roee Gavirel

Reputation: 19445

you can do it by looking for sign change:

private void AltitudeThreadWork()
{
    bool isOrigPositive = Aircraft.Altitude - AltitudeChange.Altitude > 0;

    do
    {
        var updated = DateTime.Now;
        UpdateAltitude((DateTime.Now - updated).TotalMilliseconds);
        Thread.Sleep(40);
        bool isNowPositive = Aircraft.Altitude - AltitudeChange.Altitude > 0;
    } 
    while (isOrigPositive == isNowPositive)
}

Upvotes: 2

DoXicK
DoXicK

Reputation: 4812

Problem with floats/doubles is that checking for equality is hard. Therefor you should use a 'range' in which you consider them "equal".

Covering both up and down in 1 line:

while(Math.Abs(Altitude - AltitudeChange.Altitude) > 0.0001)

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500275

Basically you want to make sure you don't overshoot. So instead of checking for equality, check whether applying the change would take you further away from the target altitude, or closer towards it.

You might also want to change the thread that adjusts the altitude to stop overshooting to start with. So for example, pseudocode:

double potentialAltitude = currentAltitude + AltitudeChange;
if (AltitudeChange < 0) // Going down... don't go below the "floor"
{
    newAltitude = Math.Max(potentialAltitude, TargetAltitude);
}
else // Going up... don't go above the "ceiling"
{
    newAltitude = Math.Min(potentialAltitude, TargetAltitude);
}

Upvotes: 1

Related Questions