Check that the float is within the range of an int before casting.

This commit is contained in:
Kevin Backhouse 2021-08-04 13:34:21 +01:00
parent bda9471c1b
commit 7c2ab1d7de
No known key found for this signature in database
GPG Key ID: 9DD01852EE40366E

View File

@ -36,6 +36,7 @@
#include <sstream>
#include <utility>
#include <cctype>
#include <climits>
#include <ctime>
#include <cstdio>
#include <cstdlib>
@ -670,27 +671,28 @@ namespace Exiv2 {
Rational floatToRationalCast(float f)
{
#if defined(_MSC_VER) && _MSC_VER < 1800
if (!_finite(f)) {
#else
if (!std::isfinite(f)) {
#endif
return {f > 0 ? 1 : -1, 0};
// Convert f to double because it simplifies the "in_range" check
// below. (INT_MAX can be represented accurately as a double, but
// gets rounded when it's converted to float.)
const double d = f;
const bool in_range = INT_MIN <= d && d <= INT_MAX;
if (!in_range) {
return {d > 0 ? 1 : -1, 0};
}
// Beware: primitive conversion algorithm
int32_t den = 1000000;
const long f_as_long = static_cast<long>(f);
if (Safe::abs(f_as_long) > 2147) {
const long d_as_long = static_cast<long>(d);
if (Safe::abs(d_as_long) > 2147) {
den = 10000;
}
if (Safe::abs(f_as_long) > 214748) {
if (Safe::abs(d_as_long) > 214748) {
den = 100;
}
if (Safe::abs(f_as_long) > 21474836) {
if (Safe::abs(d_as_long) > 21474836) {
den = 1;
}
const float rnd = f >= 0 ? 0.5F : -0.5F;
const auto nom = static_cast<int32_t>(f * den + rnd);
const auto nom = static_cast<int32_t>(std::round(d * den));
const int32_t g = gcd(nom, den);
return {nom / g, den / g};