3 ms·
Note that this isn't just a gotcha when writing your own absolute value function. The code given in the example is also how java.Math.abs() works. In C++ std:
by KMag 5y ago
Note that this isn't just a gotcha when writing your own absolute value function. The code given in the example is also how java.Math.abs() works.
In C++ std::abs(std::numeric_limits<int>::min()) is undefined behavior (not even implementation-defined: nasal flying simian warning!). Most importantly, the optimizer is allowed to make optimizations that are only safe if abs() only returns non-negative values, but abs(INT_MIN) can also return a negative value (or trap to the kernel/SIGFPE/SIGBUS/etc. on some exotic architecture).
So, in C++ one might be tempted to get around this issue by writing:
static inline int my_abs(int x) {
const int result = abs(x);
if (result < 0 /* WRONG! UB! */ ) {return numeric_limits<int>::max();}
return result;
}
But the C/C++ optimizer is allowed to optimize away your conditional branch. You need to check for INT_MIN before calling abs() if you want to guarantee the optimizer won't thwart you.
More crazily, if the C/C++ optimizer discovers that some conditional branch is always conditioned on undefined behavior, it can just optimize away the branch, or even both sides of an if-else.
static const int x = std::numeric_limits<int>::min();
void f() {
puts("Before\n");
if (abs(x) >= 0) {
puts("Expected\n");
} else {
puts("Unexpected\n");
}
puts("After\n");
}
The optimizer is allowed to determine that neither the if nor the else ever executes and optimize f() to { puts("Before\n"); puts("After\n");}
When I was at Google, Michael Chastain was telling me about one of the oddest bugs he helped debug (super helpful and knowledgeable guy, btw, one of the few people trusted with code ownership of the root of the monorepo). It was basically the above, but the UB was related to static initialization order of floating point variables.
static double y = 22.0;
static double x = y / 7.0; // Uh-oh... depends on floating point static init order
void f() {
if (x > M_PI) {
...
} else {
...
}
}
When my plan for world domination succeeds, one of my first acts as World Emperor will be to appoint a commission to change a lot of this undefined behavior into implementation-defined behavior.