I am looking through some code previously written by a senior developer in my company and I am curious about the reasoning behind the somewhat convoluted looking if statement they wrote (I would ask them directly if possible).
The code is intended to check if temperature readings from a sensor are valid. I believe negative readings were valid at the time, which explains the use of std::abs() and comparison of that to 0.000001 instead of just using temperature > 0, however I am unsure why they chose to subtract 0. as well. I have included a minimum working example, but the focus of my question is on why the statement includes the - 0. (just to convert float to a double? That seems to be what VSCode thinks it does) and if my assumption about needing to accept negative values while also needing to invalidate (very near) zero readings in the form of floats sounds correct.
#include <optional>
#include <iostream>
void check_temp(std::optional<float> prev_temp, float cur_temp){
if ((!prev_temp && std::abs(cur_temp - 0.) < 1e-6) ||
(prev_temp && std::abs(prev_temp.value() - cur_temp) > 100.0))
{
std::cout << "Temperature error detected!\n";
}
return;
}
int main(int argc, char const *argv[])
{
std::optional<float> prev_temp = std::nullopt;
float cur_temp = 0;
while(1){
std::cout << "Enter temp: ";
std::cin >> cur_temp;
check_temp(prev_temp, cur_temp);
prev_temp = cur_temp;
}
return 0;
}
And to be very clear, the specific line (condition) I am looking for clarification on is:
(!prev_temp && std::abs(cur_temp - 0.) < 1e-6)
clarification on:
(!prev_temp && std::abs(cur_temp - 0.) < 1e-6)
!prev_temp certainly tests if a previous temperature exist. Since it does not (in this clause), code continues "as if" previous temperature was zero. The subtraction with 0.0 instead of 0.0f is an oversight on the original programming. The same functionality and simpler code would have been std::abs(cur_temp - 0.0f) for illustrative purposes.
There is no reason for a double constant 1.0e-6 except for a pedantic compare against 1.0e-6 vs 1.0e-6f - which are different values. IOWs, 1.0e-6f < 1.0e-6 might be true, so the equivalent replacement is std::abs(cur_temp) <= 1e-6f - note the <=.
The compare against the double 100.0 again is weak programming. Nothing gain by using double. It might not emit different code had is been 100.f with a good optimizing compiler.
IMHO
if ((!prev_temp && std::abs(cur_temp - 0.) < 1e-6) ||
(prev_temp && std::abs(prev_temp.value() - cur_temp) > 100.0))
better as
if ((!prev_temp && std::abs(0.0f - cur_temp) < 1e-6f) ||
(prev_temp && std::abs(prev_temp.value() - cur_temp) > 100.0f))
The only reason I see for masterfully coding in this fashion is to try to steer the compiler output. Such coding applies to the compiler of that vintage. Better now to code for clarity.
Related
In chapter 5.10.1 of Programming: Principles and Practice using C++, there is a "Try this" exercise for debugging for bad input of an area. The pre-conditions are if the the inputs for length and width are 0 or negative while the post-condition is checking if the area is 0 or negative. To quote the problem, "Find a pair of values so that the pre-condition of this version of area holds, but the post-condition doesn’t.". The code so far is:
#include <iostream>
#include "std_lib_facilities.h"
int area (int length, int width) {
if (length <= 0 || width <= 0) { error("area() pre-condition"); }
int a = length * width;
if(a <= 0) { error("area() post-condition"); }
return a;
}
int main() {
int a;
int b;
while (std::cin >> a >> b) {
std::cout << area(a, b) << '\n';
}
system("pause");
return 0;
}
While the code appears to work, I can't wrap my head around what inputs will get the pre-condition to succeed yet will trigger the post-condition. So far I have tried entering strings into one of the inputs but that just terminates the program and tried looking up the ascii equivalent to 0, but same result as well. Is this supposed to be some sort of trick question or am I missing something?
Consider using large values for the input so that the multiplication overflows.
Numbers which when multiplied cause signed overflow will possibly cause the value to be negative and certainly cause the result to be incorrect.
Exactly what values cause integer overflow will depend on your architecture and compiler, but the gist is that multiplying two 4 byte integers will result in an 8 byte value, which can not be stored in a 4 byte integer.
I tried this, and seems like this works: area(1000000,1000000);
The output was: -727379968
I am implementing an optimization algorithm and have diferent heuristics for cases where no or largely different lower and upper bounds for the solution are known or not.
To check, my first approach would be simply taking
if(abs(floor(log10(abs(LBD))) - floor(log10(abs(UBD)))) < 1 )
{ //(<1 e.g. for 6, 13)
//Bounds are sufficiently close for the serious stuff
}
else {
//We need some more black magic
}
But this requires previous checks to be gerneralized to NAN, ±INFINITY.
Also, in the case where LBD is negative and UBD positive we can't assume that the above check alone assures us that they are anywhere close to being of equal order of magnitude.
Is there a dedicated approach to this or am I stuck with this hackery?
Thanks to geza I realized that thw whole thing can be done without the log10:
A working solution is posted below, and a MWE including the log variant posted on ideone.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
bool closeEnough(double LBD, double UBD, uint maxOrderDiff = 1, uint cutoffOrder = 1) {
double sgn_LBD = sgn(LBD);
double sgn_UBD = sgn(UBD);
double cutoff = pow(10, cutoffOrder);
double maxDiff = pow(10, maxOrderDiff);
if(sgn_LBD == sgn_UBD) {
if(abs(LBD)<cutoff && abs(UBD)<cutoff) return true;
return LBD<UBD && abs(UBD)<abs(LBD)*maxDiff;
}
else if(sgn_UBD > 0) {
return -LBD<cutoff && UBD<cutoff;
}
// if none of the above matches LBD >= UBD or any of the two is NAN
}
As a bonus it can take cutoffs, so if both bounds lie within [-10^cutoffOrder,+10^cutoffOrder] they are considered to be close enough!
The pow computation might also be unecessary, but at least in my case this check is not in a critical code section.
If it would be, I suppose you could just hard code the cutoff and maxDiff.
How to remove "negative zero" in c/c++?
I am trying this-
#define isEq(a, b) ((abs(a-b)<EPS) ? 1 : 0)
#define NegZero(x) (x = isEq(x, -0.0) ? 0 : x)
Is it okay and safe?
or
is there any other easy and safe way?
Specially for contest coding.
please help....
With the code you posted in your comments, your approach seems correct: your answer is a negative number that is very close to zero, and because you're rounding it to three digits (using printf("... %.3lf ...", ...)), it looks like -0.000. Your approach (check if it's close to 0.0 using abs(a - b) < epsilon; if it is, use 0.0, otherwise use the value itself) is mostly correct, except for the issue that macros aren't exactly a good fit.
Rather than writing
printf("(%.3lf,%.3lf)\n", I1.x, I1.y);
I would suggest using something like
printf(
"(%.3lf,%.3lf)\n",
NegZero(I1.x),
NegZero(I1.y)
);
and define (although I'd pick different names)
static inline bool isEq(double x, double y) {
return abs(x, y) < EPS;
}
static inline NegZero(double x) {
if (isEq(x, 0.0)) {
return 0.0;
} else {
return x;
}
}
(The reason for the confused comments is that there actually is something called negative zero in IEEE floating point arithmetic. If that were the case, the suggested solutions would have worked.)
recently I bump into a problem while comparing a double in an if statement. I was trying to cout the number of whole numbers in a double. Being a beginner, I am not sure what gone wrong in my code.
This is my code:
#include <iostream>
using namespace std;
int main(){
int x=0;//convert double to int
long double Out;//Result
long double In=10;//Input double
//Loop Begin
while(In>0){
x=In;//convert double to int
Out= (x/In);//Out(test if whole number, will return 1)
//test for 1
////////////////
if(Out == 1 ){
cout<<"[Whole Number] ";
}
////////////////
//test end
cout<<"In :"<<In<<", ";
cout<<"X :"<<x<<", ";
cout<<"Out :"<<Out<<endl;
In-=0.1;//decrease to finish loop (eventually)
}
//Loop End
cin.get();
return 0;
}
This program will test and output the whole numbers in the double (In). I realized that the accuracy of the double was affecting the if statement which is why I can't get the "[Whole Number]" result. Although I found out that if I used (0.9999) in "if(Out >= 0.9999)" the comparison would work. But I am not sure of a solution, please help! Much appreciated!
Your while loop never stops , its a infinite loop . You are not doing anything with the value of "In" in the while loop hence it will always be greater than 0 ,therefore a infinite loop .
You should probably approach the problem more directly with modf:
double int_part, frac_part;
frac_part = std::modf(in, &int_part);
if (frac_part == 0) {
// int_part contains integer value.
} else {
// process the double non-integer floating point value.
}
Your code works perfectly fine. If you subtract 0.1 from 10.0, then chances are that the result is not an integer due to rounding errors, and your code tells you exactly that. The code isn't wrong, your expectations are wrong.
if (Out >= 0.9999)
is obviously not a solution, because it will always be true if In >= 10000.0.
Do to the way floating point numbers are converted to binary representation by the computer they are inherently inaccurate and thus make logical comparisons somewhat challenging (http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems). When performing such comparisons to floating point numbers you typically will do so utilizing an epsilon constant (http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm) that represents the maximum acceptable error in the comparison. In your case you need to select a suitable value for epsilon (say .000001). Then change your comparison to:
if(abs(out - 1) < epsilon){ //Take the difference between out and 1
cout<<"[Whole Number]"; //If it is "close enough" print to console
}
I am more of a Java guy but I believe you will need #include stdlib.h to utilize the abs() function.
Hope that helps!
Try using the modulus operator: http://www.cprogramming.com/tutorial/modulus.html
Something like if(In % 1 == 0) should work.
I'm looking into translating some code from C++ to Objective C and I ran into an instance that contains a function with a const notation at the end. I'm quite rusty on C++ and I don't remember what this would represent (I have been googling though). I'd like to know how to force this over to Objective-C. Currently, here's what I have:
C++ code:
float RenderingEngine1::RotationDirection() const
{
float delta = m_desiredAngle - m_currentAngle;
if (delta == 0)
return 0;
bool counterclockwise = ((delta > 0 && delta <= 180) || (delta < -180));
return counterclockwise ? +1 : -1;
}
Objective-C:
-(float)getRotationDirection{
float delta = desiredAngle - currentAngle;
if (delta == 0) {
return 0;
}
bool counterclockwise = ((delta > 0 && delta <= 180) || (delta < -180));
float test = counterclockwise ? +1 : -1;
NSLog(#"%f",test );
return counterclockwise ? +1 : -1; //problem
}
Edit: found the error of my ways and it was just an addition problem somewhere else in the program ('I love the easy ones'). That being said, I do want to ensure that the const declaration will not interfere with any further issues and want to check to ensure whether or not there should be any declarations I need to make (such as singleton methods and such). Thank you guys for the answers!
const in c++ means that this method doesn't alter the state of the object, thus it may use just const methods, and it can't alter class variables unless they're declared mutable.
If your class is immutable, you can safely assume that every method you've declared is the equivalent of a const method.
However, since you aren't altering any ivar, you're "translating" the code properly (logically speaking), as you aren't mutating any ivar.
I don't see any particular problem with your code, there shouldn't be a syntax (neither semantic) error.
PS: That's not how you should compare floating point numbers, look here.