@user673679's comments about style, var names, and the weirdness of having a static int
inside a special function are all good so I won't repeat most of that.
You over-estimate the final speed by up to 0.9999... seconds of extra falling time past ground level, i.e. worst case error of almost 9.8 m/s if the previous timestep had the object above the ground by the smallest representable double
that can result from the rounding error in hi - high
when you subtract two nearby numbers1. A smaller simulation timestep, or some kind of interpolation when you detect the collision, would be much better. (The physics is simple here so you could interpolate exactly back from the height below ground, but even linear interpolation, i.e. assuming that the object moves at constant speed since the last step, would let you make a closer estimate of the time when it hit the ground, and thus to correct total falling time and thus speed.)
Or just solve the equation and calculate the correct final speed for this simple case; as @G. Sliepen commented, t = std::sqrt(2 * h / g)
, and from that t
with constant acceleration you can simply calculate velocity. So v = sqrt(2 * h / g) * g
, or taking g
inside the sqrt and simplifying: v = std::sqrt(2 * h * g);
Note 1: Subtracting two nearby FP numbers is bad for numerical precision in general; the rounding error is called "catastrophic cancellation". But in this case it appears you only want to know when to stop, so hi <= high
would avoid that. Or is it high <= hi
? Without going back to check your code, I forget which one is which because their names don't distinguish them at all.
switch
with duplicate code except for one letter bloats your code a lot.
A common lazy technique is ... second(s)
to indicate that the reader should infer singular or plural based on the number. That seems appropriate and good enough when you're mostly interested in the physics simulation. @user673679 points out that in this case you could phrase it with ordinal numbers, using the number as a label for the interval to avoid the need to pluralize. "after second 2" is ok; a bit clunky to read.
switch
is definitely the wrong approach for selecting between two things; that's what if/else
is for. If you ever have a switch
with one case
and one default:
, it should be an if/else instead. would be more compact, but you'd still be repeating yourself.
if (t == 1)
std::cout << ... << '\n';
else
std::cout << ...s << "blah blah\n";
Another approach is to just select the message, and keep the code the same. (The switch or if hopefully compile to asm that works this way, if your compiler is smart and notices that it's just different data passed to the same functions.)
auto seconds_unit = (t == 1) ? "second" : "seconds";
std::cout << "After " << t << seconds_unit << ", the ball is at a distance of " << h << " m from the ground at a speed: " << v << " m/s\n";
std::endl
is only useful if you want to force a flush, even if output is fully buffered (e.g. redirected to a file). Output to a terminal will already flush automatically when you output a newline, so you can simply include a "\n"
in a string literal if your output ends with a constant string instead of a number anyway. Even if you were doing something slow between prints, there'd be no need for std::endl
to make sure interactive "progress updates" got shown when you wanted. That's why cout / stdout is line-buffered when it's connected to a terminal, not a file.
t = std::sqrt(2 * h / g)
. \$\endgroup\$