1

when learning about static_cast and dynamic_cast i saw an advice about additional checking on overflow and underflow when casting double type into int one. how can i do that here?

    double value1{ 4.8967 };
    int value2 = static_cast<int>(value1);
    std::cout << value2 << std::endl;

i tried searching here and there only to found an arithmetic over-/underflow which seems not quite the case here. i'd appresiate a couple of links where i can read about it more specifically

chrkspln
  • 11
  • 4
  • 2
    To avoid [undefined behaviour](https://en.cppreference.com/w/cpp/language/ub) you have to check whether the values would over- or underflow *before* doing the conversion. You can't check if overflow happened *after* the fact, you have to avoid the possibility of it ever possibly happening in the first place. – Jesper Juhl Jul 27 '23 at 12:42
  • @JesperJuhl okay thanks – chrkspln Jul 27 '23 at 12:49
  • 2
    You can look at `boost::numeric_cast`: https://www.boost.org/doc/libs/1_82_0/libs/numeric/conversion/. – Daniel Langr Jul 27 '23 at 13:01
  • Note that this has nothing to do with `static_cast`; `int value2 = value1;` is legal and does exactly the same thing. Yes, you might have a nagging compiler that tells you that you're probably not smart enough to understand the code that you wrote, and insists that you decorate your code with a redundant cast. :-( – Pete Becker Jul 27 '23 at 13:09
  • 1
    @PeteBecker conciseness and readability are two very different concepts. An explicit cast in such a case express very clearly the intent and you don't have to wonder whether the author of this code made a mistake or not. – Zejj Jul 27 '23 at 13:38
  • @PeteBecker You might (hopefully) have a QA department that will tell you something about silent casts, ignoring compiler warnings, and not running your compiler in the strictest error-checking mode possible. – DevSolar Jul 27 '23 at 14:08
  • @Zejj Actually I'd be more likely to suspect a mistake with the explicit cast. Useless casts are a code smell that hide (by shutting up compiler warnings) significantly more serious problems – Ben Voigt Jul 27 '23 at 14:10
  • 1
    @DevSolar -- there is no such thing as a "silent cast". A cast is something you write in your source code to tell the compiler to do a conversion. Yes, sometimes an implicit conversion can be a mistake. But the compiler writer doesn't know what my code is supposed to do, and compiler writers shouldn't be dictating coding style. – Pete Becker Jul 27 '23 at 14:17
  • @DevSolar -- "QA department says do this..." isn't a valid argument. It's just restating the underlying question. – Pete Becker Jul 27 '23 at 14:19

1 Answers1

2

You could use std::numeric_limits create a helper function to avoid overflowing. You'd then get the minimum possible int value if there would be an underflow or the maximum possible int value if there would be an overflow.

#include <iostream>
#include <limits>  // numeric_limits

template <class R, class T>
R anti_overflow_clamp(T value) {
    if (value <= std::numeric_limits<R>::lowest()) [[unlikely]]
        return std::numeric_limits<R>::lowest();

    if (std::numeric_limits<R>::max() <= value) [[unlikely]]
        return std::numeric_limits<R>::max();

    return static_cast<R>(value);
}

int main() {
    double value1 = -9872034875209384574237698276453978264398576.0;

    auto value2 = anti_overflow_clamp<int>(value1);

    std::cout << value2 << '\n'; // min value for int
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • There's no guarantee that `static_cast(std::numeric_limits::max())` is exact or safe to convert back to an `int`. It is completely possible that the nearest representable value is one greater than `INT_MAX`, and you're right back in the land of undefined behavior. – Ben Voigt Jul 27 '23 at 14:11
  • @BenVoigt True. I deleted that part and made a helper function to do the clamping instead. – Ted Lyngmo Jul 27 '23 at 14:20
  • `if (std::numeric_limits::max() < value)` is still going to have an inexact left operand and fail to catch an out-of-range value. You need `<=` or else possibly `std::nexttoward` – Ben Voigt Jul 27 '23 at 14:22
  • This particular problem is quite tricksy. – Ben Voigt Jul 27 '23 at 14:24
  • @BenVoigt Indeed. Many traps :-) – Ted Lyngmo Jul 27 '23 at 14:24
  • And I think it may still be possible for the conditionals to take the wrong branch due to [excess precision](https://eel.is/c++draft/expr.pre#6) :( – Ben Voigt Jul 27 '23 at 14:30
  • @BenVoigt oh, that sounds nasty. Do you know how to setup something to display that behaviour? I can't come up with any ideas myself. – Ted Lyngmo Jul 27 '23 at 14:36
  • Actually I think excess precision can change the operands in that conditional, but should still match the entire range of representable values that needs to be clamped. The problem would only arise if `value` is carried with excess precision but the operand `std::numeric_limits::max()` is converted with the basic precision. – Ben Voigt Jul 27 '23 at 14:43
  • @BenVoigt I see. I'll have to look closer at that later. Need to run... – Ted Lyngmo Jul 27 '23 at 14:49