public static void Investigations() { double geosyncOrbitR = 42000.0; // Real example, approximate geosynchronous orbit in km double oneMillimeter = 1.0 / 1000.0 / 1000.0; // One mm in kilometers Console.WriteLine("=========== Geosync Orbit plus 1/3 millimeter (and smaller) in kilometers =========="); Console.WriteLine(" Objective is to have fractions of a millimeter correctly accumulate over many additions\n"); double large = geosyncOrbitR; double small = oneMillimeter / 3.0; Console.WriteLine("large: {0}, log10(large): {1:G5}\n", large, Math.Log10(Math.Abs(large))); for (int i = 0; i < 200; i++) { Console.WriteLine("large + small ({0,22:G17}) = {1,22:G17} - sig digits in calc: {2}, log10(small): {3,7:G5}, logs difference: {4,6:G5} ({5}) (CAP V2: {6}, V3: {7})", small, large + small, DecimalAdditionPrecision(large, small), Math.Log10(Math.Abs(small)), Math.Log10(Math.Abs(large)) - Math.Log10(Math.Abs(small)), FloatingPointUtil.AdditionMagnitudeDifference(large, small), FloatingPointUtil.CheckAdditionPrecisionV2(large, small), FloatingPointUtil.CheckAdditionPrecisionV3(large, small)); if (large + small == large) { break; } small *= 0.1; } }
public void MoveWithPrecisionCheck(SimPoint accel, double deltaT) { if (!((accel.X == 0.0) && (accel.Y == 0.0))) { // Apply linear acceleration during the time interval double deltaVX = accel.X * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(velocity.X, deltaVX)) { DisplayPrecisionIssue(velocity.X, deltaVX, "Adding DeltaV to VX", bodyNumber); } double newVelocityX = velocity.X + deltaVX; double deltaPX = (velocity.X + newVelocityX) / 2 * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(position.X, deltaPX)) { DisplayPrecisionIssue(position.X, deltaPX, "Adding to Position.X", bodyNumber); } position.X += deltaPX; velocity.X = newVelocityX; double deltaVY = accel.Y * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(velocity.Y, deltaVY)) { DisplayPrecisionIssue(velocity.Y, deltaVY, "Adding DeltaV to VY", bodyNumber); } double newVelocityY = velocity.Y + deltaVY; double deltaPY = (velocity.Y + newVelocityY) / 2 * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(position.Y, deltaPY)) { DisplayPrecisionIssue(position.Y, deltaPY, "Adding to Position.Y", bodyNumber); } position.Y += deltaPY; velocity.Y = newVelocityY; } }
public static void CAP_SpecificExample() { // Run and display calculation described by this comment from CheckAdditionPrecision(): // Specific example: // This limit allows any quantity around or above 1/100 of a millimeter to be repeatedly added to 42,000 km (geosync orbit radius) // without losing too much accuracy. E.g. 1/300 of a mm added 10,000 times should about equal 1/30 of a meter double geosyncOrbitR = 42000.0; // Real example, approximate geosynchronous orbit in km double oneMillimeter = 1.0 / 1000.0 / 1000.0; // One mm in kilometers double smallDelta = oneMillimeter / 300.0; int numAdditions = 10000; Console.WriteLine("Large value = {0:N0}, small value = {1:G17}, addition iterations = {2:N0}", geosyncOrbitR, smallDelta, numAdditions); Console.WriteLine("CheckAdditionPrecision({0:N0}, {1}) = {2}", geosyncOrbitR, smallDelta, FloatingPointUtil.CheckAdditionPrecision(geosyncOrbitR, smallDelta)); for (int i = 0; i < numAdditions; i++) { geosyncOrbitR += smallDelta; } Console.WriteLine("Result = {0:G17}", geosyncOrbitR); }
public static void RunTest(long iterations) { Stopwatch stopwatch = new Stopwatch(); long loopTime, randomGenerationTime, additionTime, checkPrecisionTimeV1, checkPrecisionTimeV2, checkPrecisionTimeV3; stopwatch.Start(); for (long i = 0; i < iterations;) { i++; } loopTime = stopwatch.ElapsedMilliseconds; Double[] randomA = new Double[iterations]; Double[] randomB = new Double[iterations]; Random random = new Random(); stopwatch.Reset(); stopwatch.Start(); for (long i = 0; i < iterations; i++) { randomA[i] = random.NextDouble(); randomB[i] = random.NextDouble(); } randomGenerationTime = stopwatch.ElapsedMilliseconds; double sum; stopwatch.Reset(); stopwatch.Start(); for (long i = 0; i < iterations; i++) { sum = randomA[i] + randomB[i]; } additionTime = stopwatch.ElapsedMilliseconds; bool result; stopwatch.Reset(); stopwatch.Start(); for (long i = 0; i < iterations; i++) { result = FloatingPointUtil.CheckAdditionPrecisionV1(randomA[i], randomB[i]); } checkPrecisionTimeV1 = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (long i = 0; i < iterations; i++) { result = FloatingPointUtil.CheckAdditionPrecisionV2(randomA[i], randomB[i]); } checkPrecisionTimeV2 = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (long i = 0; i < iterations; i++) { result = FloatingPointUtil.CheckAdditionPrecisionV3(randomA[i], randomB[i]); } checkPrecisionTimeV3 = stopwatch.ElapsedMilliseconds; Console.WriteLine("Performance Test of CheckAdditionPrecision() - running {0:N0} iterations", iterations); Console.WriteLine("Times in ms:"); Console.WriteLine(" Empty loop: {0:N0}", loopTime); Console.WriteLine(" Random number generation: {0:N0}, net: {1:N0}", randomGenerationTime, randomGenerationTime - loopTime); Console.WriteLine(" Addition: {0:N0}, net: {1:N0}", additionTime, additionTime - loopTime); Console.WriteLine(" CAP V1(): {0:N0}, net: {1:N0}", checkPrecisionTimeV1, checkPrecisionTimeV1 - loopTime); Console.WriteLine(" CAP V2(): {0:N0}, net: {1:N0}", checkPrecisionTimeV2, checkPrecisionTimeV2 - loopTime); Console.WriteLine(" CAP V3(): {0:N0}, net: {1:N0}", checkPrecisionTimeV3, checkPrecisionTimeV3 - loopTime); Console.WriteLine("CheckAdditionPrecisionV3() takes {0:N2} times longer than addition", (checkPrecisionTimeV3 - loopTime) / (additionTime - loopTime)); Console.WriteLine("CheckAdditionPrecisionV3() runs in {0:N2}% time of original version", ((float)(checkPrecisionTimeV3 - loopTime) / (float)(checkPrecisionTimeV1 - loopTime)) * 100.0f); }
// simRunning - true if sim is auto-running // false if sim is single stepping public void Step(double timeInterval, bool simRunning) { Stopwatch perfStopwatch = new Stopwatch(); long perfIntervalTicks = 0L; bool simStepping = !simRunning; double scaledTimeInterval = timeInterval * SpeedFactor; SetTimeForTrailMark(simElapsedTime); if (simStepping) { Debug.WriteLine("Elapsed times for {0} bodies:", bodies.Count()); perfStopwatch.Start(); } if (accelerations == null) { accelerations = new SimPoint[bodies.Count()]; } if (checkSim) { if (positions == null) { positions = new SimPoint[bodies.Count()]; } if (velocities == null) { velocities = new SimPoint[bodies.Count()]; } } double timeIntervalPerCycle = scaledTimeInterval / (double)simCalcSettings.CalculationCyclesPerFrame; List <SimPoint> otherPositions = new List <SimPoint>(); List <SimPoint> otherAccelerations = new List <SimPoint>(); if (checkSim) { for (int i = 0; i < bodies.Count(); i++) { positions[i] = bodies[i].Position; velocities[i] = bodies[i].Velocity; } Validate5BodyCross(positions, "Positions Before Update"); Validate5BodyCross(velocities, "Velocities Before Update"); } for (int calcCycle = 0; calcCycle < simCalcSettings.CalculationCyclesPerFrame; calcCycle++) { // Calculate NBody acceleration if (simCalcSettings.CheckAllAdditionPrecision) { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); if (simRounding > 0) { accel.X += Math.Round(accel.X, simRounding, MidpointRounding.AwayFromZero); accel.Y += Math.Round(accel.Y, simRounding, MidpointRounding.AwayFromZero); } if (FloatingPointUtil.CheckAdditionPrecision(accelerations[i].X, accel.X)) { Body.DisplayPrecisionIssue(accelerations[i].X, accel.X, "Accumulating Accel.X", i); } accelerations[i].X += accel.X; if (FloatingPointUtil.CheckAdditionPrecision(accelerations[i].Y, accel.Y)) { Body.DisplayPrecisionIssue(accelerations[i].Y, accel.Y, "Accumulating Accel.Y", i); } accelerations[i].Y += accel.Y; } } } } else if (simRounding > 0) { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); accelerations[i].X += Math.Round(accel.X, simRounding, MidpointRounding.AwayFromZero); accelerations[i].Y += Math.Round(accel.Y, simRounding, MidpointRounding.AwayFromZero); } } } } else { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); accelerations[i].X += accel.X; accelerations[i].Y += accel.Y; } } } } //if (checkSim) Validate5BodyCross(accelerations, "Accelerations Before Limit and Rounding"); if (accelerationLimitOn) { EnforceAccelerationLimit(accelerations, accelerationLimit); } if (simRounding > 0) { RoundAccelerations(accelerations, simRounding); } if (checkSim) { Validate5BodyCross(accelerations, "Accelerations After Limit and Rounding"); } // Update positons and velocities if (simCalcSettings.CheckAllAdditionPrecision) { for (int i = 0; i < bodies.Count(); i++) { bodies[i].MoveWithPrecisionCheck(accelerations[i], timeIntervalPerCycle); } } else { for (int i = 0; i < bodies.Count(); i++) { bodies[i].Move(accelerations[i], timeIntervalPerCycle); } } if (checkSim) { for (int i = 0; i < bodies.Count(); i++) { positions[i] = bodies[i].Position; velocities[i] = bodies[i].Velocity; } Validate5BodyCross(positions, "Positions After Update"); Validate5BodyCross(velocities, "Velocities After Update"); } simElapsedTime += timeIntervalPerCycle; if ((MainPage.trailsEnabled) && TimeForTrailsMark(simElapsedTime)) { DrawTrails(); } } if (simStepping) { perfIntervalTicks = DisplayPerfIntervalElapsed(perfStopwatch, perfIntervalTicks, String.Format("Compute N-body accelerations, update positions & velocities ({0} iterations)", simCalcSettings.CalculationCyclesPerFrame)); } // Update rendering renderer.BodiesMoved(bodies); simPage.UpdateMonitoredValues(bodies[monitoredBody], simElapsedTime); if (simStepping) { perfIntervalTicks = DisplayPerfIntervalElapsed(perfStopwatch, perfIntervalTicks, "Update transforms of XAML shapes & monitored values"); } if (simStepping) { Debug.WriteLine("Total elapsed time = {0:F2} ms", (double)perfIntervalTicks / (double)(Stopwatch.Frequency / 1000L)); Debug.WriteLine(""); } // stepRunning = false; }
public static void DisplayPrecisionIssue(double a, double b, string whichCalculation, int bodyNumber) { if (debugMessageCount++ < DebugMessageCountLimit) { Debug.WriteLine("Body #{4} {3}: a = {0:G17}, b = {1:G17}, mag diff = {2}", a, b, FloatingPointUtil.AdditionMagnitudeDifference(a, b), whichCalculation, bodyNumber); } if (debugMessageCount == DebugMessageCountLimit) { Debug.WriteLine(">>> Reached limit of {0:N0} precision warning messages. No more will be displayed.", DebugMessageCountLimit); } }