Wrong result for division of two doubles in release build - c++

When I compile my application in Release mode I get incorrect division result of 40.0 / 5 = 7.
In debug compilation it is correct, and result is 8
I tried to cast to double, from double, to int, without abs() etc, but no luck. I know this must be related to weirdness of floating point math on computers, but I have no idea what exactly. I also logged the values on console, via the qDebugs() below the code - everything looks okay, except initial steps.
//somewhere in code
double tonnageToRecover = 0.5;//actually, its QDoubleSpinBox->value(), with 0.5 step set. Anyway, the value finally reduces to 0.5 every time
double tonnagePerArmorPoint = 0.0125;//taken from .json
int minimumArmorDelta = 5;//taken from .json
...
//palace where the calculations are preformed
double armorPointsPerHalfTon = tonnageToRecover / tonnagePerArmorPoint;
int steps = abs(static_cast<int>(armorPointsPerHalfTon / minimumArmorDelta));
qDebug() << "armorPointsPerHalfTon = " << armorPointsPerHalfTon;
qDebug() << "tonnagePerArmorPoint = " << tonnagePerArmorPoint;
qDebug() << "steps initial = " << steps;
qDebug() << "minimumArmorDelta = " << minimumArmorDelta;
both 1st division parts are type double, tonnageToRecover = 0.5, tonnagePerArmorPoint = 0.0125, result is 40 which is OK
minimumArmorDelta is int = 5
So why 40/5 isn't 8??
Compiler - MinGW 32 5.3.0, from Qt 5.11 pack
Screenshots:
Release
Debug

#Julian
I suspect that too, but how can I overcome this obstacle? Will try to change steps to double, then cast to int again.
RESUT: still does not work :/
I found a solution, but I have no idea exactly why it works now. Current code it:
double armorPointsPerHalfTon = tonnageToRecover / tonnagePerArmorPoint;
// int aPHT = (int)armorPointsPerHalfTon;
// double minDelta = 5.0;//static_cast<double>(minimumArmorDelta);
QString s(QString::number(abs(armorPointsPerHalfTon / minimumArmorDelta)));
int steps = abs(armorPointsPerHalfTon / minimumArmorDelta);
#define myqDebug() qDebug() << fixed << qSetRealNumberPrecision(10)
myqDebug() << "tonnageToRecover = " << tonnageToRecover;
myqDebug() << "tonnagePerArmorPoint = " << tonnagePerArmorPoint;
myqDebug() << "armorPointsPerHalfTon = " << armorPointsPerHalfTon;
//myqDebug() << "aPHT = " << aPHT;//this was 39 in Release, 40 in Debug
myqDebug() << "steps initial = " << steps;
myqDebug() << "string version = " << s;
myqDebug() << "minimumArmorDelta = " << minimumArmorDelta;// << ", minDelta = " << minDelta;
#undef myqDebug
I suppose that creation of that QString s flushes something, and that's why calculation of steps is correct now. String has incorrect value "7", though.

Your basic problem is that you are truncating.
Suppose real number arithmetic would give an answer of exactly 8. Floating point arithmetic will give an answer that is very close to 8, but can differ from it in either direction due to rounding error. If the floating point answer is slightly greater than 8, truncating will change it to 8. If it is even slightly less than 8, truncating will change it to 7.
I suggest writing a new question on how to avoid the truncation, with discussion of why you are doing it.

I guess, the reason is that armorPointsPerHalfTon / minimumArmorDelta could be not 8 but actually 7.99999999 in the Release-version. This value then changes to 7 through the int-cast.
So, if the Debug version calculates armorPointsPerHalfTon / minimumArmorDelta = 8.0000001, the result is static_cast<int>(armorPointsPerHalfTon / minimumArmorDelta) = 8.
It's not surprising that Debug / Release yield different results (on the order of machine precision), as several optimizations occur in the Release version.
EDIT: If it suits your requirements, you could just use std::round to round your double to the nearest integer, rather than truncation decimals.

Related

Did Visual Studio 2022 17.4.3 break std::round?

(Note: This problem occurs for me only when the compiler switch /arch:AVX is set. More at the bottom)
My gtest unit tests have done this for 7 years
ASSERT_EQ(-3.0, std::round(-2.5f)); // (Note the 'f' suffix)
According to cpp-reference, std::round is supposed to round AWAY from zero, right? Yet with the current release, this test just started failing. Am I missing something? All I did was update my Visual Studio 2022 to 17.4.3 My co-worker with 17.3.3 does not have this problem
EDIT: I don't know if the problem is GTEST and its macros or assumptions my unit test makes about equality. I put the following two lines of code into my test
std::cerr << "std::round(-2.5) = " << std::round(-2.5) << std::endl;
std::cerr << "std::round(-2.5f) = " << std::round(-2.5f) << std::endl;
They produce the following output. The second one is wrong, is it not?
std::round(-2.5) = -3
std::round(-2.5f) = -2
EDIT #2: As I note above, the only occurs when I set the compiler flag /arch:AVX If just create a console app and do not set the flag of if I explicitly set it to /arch:IA32, the problem goes away. But the question then becomes: Is this a bug or am I just not supposed to use that option?
This is a known bug, see the bug report on developercommunity, which is already in the "pending release" state.
For completeness/standalone sake, the minimal example from there is (godbolt):
int main()
{
std::cout << "MSVC version: " << _MSC_FULL_VER << '\n';
std::cout << "Round 0.5f: " << std::round(0.5f) << '\n';
std::cout << "Round 0.5: " << std::round(0.5) << '\n';
}
compiled with AVX or AVX2.
The correct output e.g. with MSVC 19.33 is
MSVC version: 193331631
Round 0.5f: 1
Round 0.5: 1
while the latest MSVC 19.34 outputs
MSVC version: 193431931
Round 0.5f: 0
Round 0.5: 1

Bug in boost::math:quadrature::sinh_sinh?

I was searching for a library for numeric quadrature on the whole real line, i.e. (-inf,inf), and I found boost (version 1.70.0). The function I want to use is boost::math::quadrature:sinh_sinh. To test it I copied the example code from the documentation:
https://www.boost.org/doc/libs/1_70_0/libs/math/doc/html/math_toolkit/double_exponential/de_sinh_sinh.html
and came up with this code:
#include <iostream>
#include <boost/math/quadrature/sinh_sinh.hpp>
using namespace boost::math::quadrature;
int main()
{
sinh_sinh<double> integrator;
auto f = [](double x) { return exp(-x*x); };
double error;
double L1;
double Q = integrator.integrate(f, &error, &L1);
std::cout << Q << "\t" << error << "\t" << L1 << std::endl;
int i = 0;
std::cin >> i; // Just to make sure the console does not close automatically
}
Unfortunately this would not compile because in the documentation the second argument for "integrate" is not a pointer to a Real number, but a normal Real number. So I had to change this line:
double Q = integrator.integrate(f, &error, &L1);
into this one:
double Q = integrator.integrate(f , boost::math::tools::root_epsilon<double>() , &error, &L1);
This compiled and gave good results. But I was curious if I could just write
double Q = integrator.integrate(f);
because all arugments except the first one have default values (and are hence optional to my understanding of c++). Unfortunately this will not compile with Visual-Studio-2013. The error is:
error C2783: "T boost::math::tools::root_epsilon(void)": template-Argument für "T" konnte nicht hergeleitet werden. (in english: it was not able to derive the template-argument for "T" )
Occuring at line 33 of pathTo\boost_1_70_0\boost\math\quadrature\sinh_sinh.hpp
As I am not sure if this error is only related to Visual-Studio I wanted to ask all of you.
Now I wanted to use the working code on my function of interest, which is:
auto f = [](double x) {return pow(abs(x), 3) / cosh(x); };
This function looks like this:
https://www.wolframalpha.com/input/?i=plot+abs(x)%5E3%2Fcosh(x)
and the result of the quadrature should be approx. 23.7:
https://www.wolframalpha.com/input/?i=integrate+abs(x)%5E3%2Fcosh(x)+from+-+inf+to+inf
This program compiles with this function but it crashes, i.e. I get the "The program has stopped working" message from Windows. When I compile in debug mode and run it I get the following error message:
So my question is basically why boost::math::quadrature::sinh_sinh is not able to integrate this function. It decays to zero for plus and minus infinity and it has no singularities.
Is it possible that all these errors occur because I am using Visual-Studio?
Unfortunately, Visual studio is not being nice to you. On your second example, I get the more intelligible error message:
terminate called after throwing an instance of 'boost::wrapexcept<boost::math::evaluation_error>'
what(): Error in function boost::math::quadrature::sinh_sinh<double>::integrate: The sinh_sinh quadrature evaluated your function at a singular point, leading to the value nan.
sinh_sinh quadrature cannot handle singularities in the domain.
If you are sure your function has no singularities, please submit a bug against boost.math
I added a bit of diagnostic code to help out:
auto f = [](double x) {
double y = pow(abs(x), 3) / cosh(x);
if (!std::isfinite(y)) {
std::cout << "f(" << x << ") = " << y << "\n";
}
return y;
};
I get:
f(1.79769e+308) = nan
f(-1.79769e+308) = nan
f(2.01977e+137) = nan
f(-2.01977e+137) = nan
f(7.35294e+106) = nan
f(-7.35294e+106) = nan
Most people are very surprised to learn that sinh-sinh quadrature evaluates their function at such huge argument. It also forces them to think about things they don't usually have to, namely:
IEEE arithmetic can't take limits.
For example, you might know that as $x \to \infty$, $x^2/(1+x^4) \to 0$. But in IEEE floating point arithmetic, for sufficiently large $x$, both the numerator and denominator overflow, and what can be done? The only sensible solution is to just make inf/inf a nan.
In your case, you know that cosh(x) grows faster than pow(|x|, 3), but IEEE doesn't. So you need to explicitly tell the function about the limiting behavior as $x->\infty$ via:
#include <iostream>
#include <cmath>
#include <boost/math/quadrature/sinh_sinh.hpp>
using namespace boost::math::quadrature;
int main()
{
sinh_sinh<double> integrator;
auto f = [](double x) {
double numerator = pow(abs(x), 3);
if (!std::isfinite(numerator)) {
return double(0);
}
return numerator / cosh(x);
};
double error;
double L1;
double tolerance = std::sqrt(std::numeric_limits<double>::epsilon());
double Q = integrator.integrate(f, tolerance, &error, &L1);
std::cout << Q << "\t" << error << "\t" << L1 << std::endl;
}
One final comment: Your integrand is even, so you can use exp_sinh quadrature over [0, inf] and double the result.

Speed difference between assign function value and not assign to a variable

So this is really a mystery for me. I am Measuring time of my own sine function and comparing it to the standard sin(). There is a strange behavior though. When I use the functions just standalone like:
sin(something);
I get an average time like (measuring 1000000 calls in 10 rounds) 3.1276 ms for the standard sine function and 51.5589 ms for my implementation.
But when I use something like this:
float result = sin(something);
I get suddenly 76.5621 ms for standard sin() and 49.3675 ms for my one. I understand that it takes some time to assign the value to a variable but why doesn't it add time to my sine too? It's more or less the same while the standard one increases rapidly.
EDIT:
My code for measuring:
ofstream file("result.txt",ios::trunc);
file << "Measured " << repeat << " rounds with " << callNum << " calls in each \n";
for (int i=0;i<repeat;i++)
{
auto start = chrono::steady_clock::now();
//call the function here dattebayo!
for (int o=0; o<callNum;o++)
{
double g = sin((double)o);
}
auto end = chrono::steady_clock::now();
auto difTime = end-start;
double timeD = chrono::duration <double,milli> (difTime).count();
file << i << ": " << timeD << " ms\n";
sum += timeD;
}
In any modern compiler, the compiler will know functions such as sin, cos, printf("%s\n", str) and many more, and either translate to simpler form [constant if the value is constant, printf("%s\n", str); becomes puts(str);] or completely remove [if known that the function itself does not have "side-effects", in other words, it JUST calculates the returned value, and has no effect on the system in other ways].
This often happens even for standard function even when the compiler is in low or even no optimisation modes.
You need to make sure that the result of your function is REALLY used for it to be called in optimised mode. Add the returned values together in the loop...

Weird C++ float bug

#include <iostream>
using namespace std;
int main()
{
cout.precision(32);
float val = 268433072;
float add = 13.5;
cout << "result =" << (val + add) << endl;
}
I'm compiling the above program with standard g++ main.cc
and running it with ./a.out
The ouput I receive however, is,
result =268433088
Clearly, this is not the correct answer..Why is this happening?
EDIT: This does not occur when using double in place of float
You can reproduce your "float bug" with an even simpler piece of code
#include <iostream>
using namespace std;
int main()
{
cout.precision(32);
float val = 2684330722;
cout << "result =" << val << endl;
}
The output
result =2684330752
As you can see the output does not match the value val was initialized with.
As it has been stated many times, floating-point types have limited precision. Your code sample simply exceeded that precision, so the result got rounded. There's no "bug" here.
Aside from the reference to (1991, PDF) What Every Computer Scientist Should Know About Floating-Point Arithmetic
The short answer is, that because float has limited storage (like the other primitives too) the engineers had to make a choice: which numbers to store with which precision. For the floating point formats they decided to store numbers of small magnitude precisely (some decimal digits), but numbers of large magnitude very imprecisely, in fact starting with +-16,777,217 the representable numbers are becoming so thin that not even all integers are represented which is the thing you noticed.

inconsistent output fixed by debug statement placement

EDIT: it was an uninitialized variable... :(
Explanation:
The PointLLA constructor I used only passed through Latitude and Longitude, but I never explicitly set the internal Altitude member variable to 0. Rookie mistake...
Original Question:
I'm having a pretty horrible time with a bug in my code. I'm calculating distances between a single point and the corners of a rectangle. In this case, the point is centered over the rectangle so I should get four equal distances. I get three equal distances, and one almost equal distance value that's inconsistent (different every time it runs).
If I have a few key debug statements (pretty much just a std::cout call) that explicitly print out the location of each rectangle corner, I get the expected value for the distance and the inconsistency disappears. Here's the relevant code:
// calculate the minimum and maximum distance to
// camEye within the available lat/lon bounds
Vec3 viewBoundsNE; convLLAToECEF(PointLLA(maxLat,maxLon),viewBoundsNE);
Vec3 viewBoundsNW; convLLAToECEF(PointLLA(maxLat,minLon),viewBoundsNW);
Vec3 viewBoundsSW; convLLAToECEF(PointLLA(minLat,minLon),viewBoundsSW);
Vec3 viewBoundsSE; convLLAToECEF(PointLLA(minLat,maxLon),viewBoundsSE);
// begin comment this block out, and buggy output
OSRDEBUG << "INFO: NE (" << viewBoundsNE.x
<< " " << viewBoundsNE.y
<< " " << viewBoundsNE.z << ")";
OSRDEBUG << "INFO: NW (" << viewBoundsNW.x
<< " " << viewBoundsNW.y
<< " " << viewBoundsNW.z << ")";
OSRDEBUG << "INFO: SE (" << viewBoundsSW.x
<< " " << viewBoundsSW.y
<< " " << viewBoundsSW.z << ")";
OSRDEBUG << "INFO: SW (" << viewBoundsSE.x
<< " " << viewBoundsSE.y
<< " " << viewBoundsSE.z << ")";
// --------------- end
// to get the maximum distance, find the maxima
// of the distances to each corner of the bounding box
double distToNE = camEye.DistanceTo(viewBoundsNE);
double distToNW = camEye.DistanceTo(viewBoundsNW); // <-- inconsistent
double distToSE = camEye.DistanceTo(viewBoundsSE);
double distToSW = camEye.DistanceTo(viewBoundsSW);
std::cout << "INFO: distToNE: " << distToNE << std::endl;
std::cout << "INFO: distToNW: " << distToNW << std::endl; // <-- inconsistent
std::cout << "INFO: distToSE: " << distToSE << std::endl;
std::cout << "INFO: distToSW: " << distToSW << std::endl;
double maxDistToViewBounds = distToNE;
if(distToNW > maxDistToViewBounds)
{ maxDistToViewBounds = distToNW; }
if(distToSE > maxDistToViewBounds)
{ maxDistToViewBounds = distToSE; }
if(distToSW > maxDistToViewBounds)
{ maxDistToViewBounds = distToSW; }
OSRDEBUG << "INFO: maxDistToViewBounds: " << maxDistToViewBounds;
So if I run the code shown above, I'll get output as follows:
INFO: NE (6378137 104.12492 78.289415)
INFO: NW (6378137 -104.12492 78.289415)
INFO: SE (6378137 -104.12492 -78.289415)
INFO: SW (6378137 104.12492 -78.289415)
INFO: distToNE: 462.71851
INFO: distToNW: 462.71851
INFO: distToSE: 462.71851
INFO: distToSW: 462.71851
INFO: maxDistToViewBounds: 462.71851
Exactly as expected. Note that all the distTo* values are the same. I can run the program over and over again and I'll get exactly the same output. But now, if I comment out the block that I noted in the code above, I get something like this:
INFO: distToNE: 462.71851
INFO: distToNW: 463.85601
INFO: distToSE: 462.71851
INFO: distToSW: 462.71851
INFO: maxDistToViewBounds: 463.85601
Every run will slightly vary distToNW. Why distToNW and not the other values? I don't know. A few more runs:
463.06218
462.79352
462.76194
462.74772
463.09787
464.04648
So... what's going on here? I tried cleaning/rebuilding my project to see if there was something strange going on but it didn't help. I'm using GCC 4.6.3 with an x86 target.
EDIT: Adding the definitions of relevant functions.
void MapRenderer::convLLAToECEF(const PointLLA &pointLLA, Vec3 &pointECEF)
{
// conversion formula from...
// hxxp://www.microem.ru/pages/u_blox/tech/dataconvert/GPS.G1-X-00006.pdf
// remember to convert deg->rad
double sinLat = sin(pointLLA.lat * K_PI/180.0f);
double sinLon = sin(pointLLA.lon * K_PI/180.0f);
double cosLat = cos(pointLLA.lat * K_PI/180.0f);
double cosLon = cos(pointLLA.lon * K_PI/180.0f);
// v = radius of curvature (meters)
double v = ELL_SEMI_MAJOR / (sqrt(1-(ELL_ECC_EXP2*sinLat*sinLat)));
pointECEF.x = (v + pointLLA.alt) * cosLat * cosLon;
pointECEF.y = (v + pointLLA.alt) * cosLat * sinLon;
pointECEF.z = ((1-ELL_ECC_EXP2)*v + pointLLA.alt)*sinLat;
}
// and from the Vec3 class defn
inline double DistanceTo(Vec3 const &otherVec) const
{
return sqrt((x-otherVec.x)*(x-otherVec.x) +
(y-otherVec.y)*(y-otherVec.y) +
(z-otherVec.z)*(z-otherVec.z));
}
The inconsistent output suggests that either you're making use of an uninitialized variable somewhere in your code, or you have some memory error (accessing memory that's been deleted, double-deleting memory, etc). I don't see either of those things happening in the code you pasted, but there's lots of other code that gets called.
Does the Vec3 constructor initialize all member variables to zero (or some known state)? If not, then do so and see if that helps. If they're already initialized, take a closer look at convLLAToECEF and PointLLA to see if any variables are not getting initialized or if you have any memory errors there.
Seems to me like the DistanceTo function is bugged in some way. If you cannot work out what is up, experiment a bit, and report back.
Try reordering the outputs to see if it's still NW that is wrong.
Try redoing the NW point 2-3 times into different vars to see if they are even consistent in one run.
Try using a different camEye for each point to rule out statefulness in that class.
As much as I hate it, have you stepped through it in a debugger? I usually bias towards stdout based debugging, but it seems like it'd help. That aside, you've got side effects from something nasty kicking around.
My guess is that the fact that you expect (rightfully of course) all four values to be the same is masking a "NW/SW/NE/SE" typo someplace. First thing I'd do is isolate the block you've got here into it's own function (that takes the box and point coordinates) then run it with the point in several different locations. I think the error should likely expose itself quickly at that point.
See if the problem reproduces if you have the debug statements there, but move them after the output. Then the debug statements could help determine whether it was the Vec3 object that was corrupted, or the calculation from it.
Other ideas: run the code under valgrind.
Pore over the disassembly output.