Skip to content

Commit b267c2f

Browse files
committed
Merge pull request godotengine#109887 from aaronfranke/range-rescale
Rescale values to better utilize R128 range before snapping
2 parents e0f17b4 + 0ad409b commit b267c2f

File tree

1 file changed

+32
-15
lines changed

1 file changed

+32
-15
lines changed

scene/gui/range.cpp

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,39 @@
3333
#include "thirdparty/misc/r128.h"
3434

3535
double Range::_snapped_r128(double p_value, double p_step) {
36-
if (p_step != 0) {
37-
// All these lines are the equivalent of: p_value = Math::floor(p_value / p_step + 0.5) * p_step;
38-
// Convert to String to force rounding to a decimal value (not a binary one).
39-
String step_str = String::num(p_step);
40-
String value_str = String::num(p_value);
41-
R128 step_r128;
42-
R128 value_r128;
43-
const R128 half_r128 = R128(0.5);
44-
r128FromString(&step_r128, step_str.ascii().get_data(), nullptr);
45-
r128FromString(&value_r128, value_str.ascii().get_data(), nullptr);
46-
r128Div(&value_r128, &value_r128, &step_r128);
47-
r128Add(&value_r128, &value_r128, &half_r128);
48-
r128Floor(&value_r128, &value_r128);
49-
r128Mul(&value_r128, &value_r128, &step_r128);
50-
p_value = value_r128;
36+
if (p_step == 0.0) {
37+
return p_value;
5138
}
39+
if (p_value > (1e18 * p_step) || p_value < -(1e18 * p_step)) {
40+
// If the value goes outside of the range R128 supports, fallback to normal snapping.
41+
return Math::snapped(p_value, p_step);
42+
}
43+
// Rescale values to better utilize R128's range before snapping.
44+
// R128 is fixed-precision with 64 bits after the decimal point, but double already uses 53 of those,
45+
// so a step size finer than 2^-11 will lose precision, and in practice even 1e-3 can be problematic.
46+
// By rescaling the value and step, we can shift precision into the higher bits (effectively turning R128 into a makeshift float).
47+
const int decimals = 14 - Math::floor(std::log10(MAX(Math::abs(p_value), p_step)));
48+
const double scale = Math::pow(10.0, decimals);
49+
p_value *= scale;
50+
p_step *= scale;
51+
// All these lines are the equivalent of: p_value = Math::floor(p_value / p_step + 0.5) * p_step;
52+
// Convert to String to force rounding to a decimal value (not a binary one).
53+
String step_str = String::num(p_step);
54+
String value_str = String::num(p_value);
55+
R128 step_r128;
56+
R128 value_r128;
57+
const R128 half_r128 = R128(0.5);
58+
r128FromString(&step_r128, step_str.ascii().get_data(), nullptr);
59+
r128FromString(&value_r128, value_str.ascii().get_data(), nullptr);
60+
r128Div(&value_r128, &value_r128, &step_r128);
61+
r128Add(&value_r128, &value_r128, &half_r128);
62+
r128Floor(&value_r128, &value_r128);
63+
r128Mul(&value_r128, &value_r128, &step_r128);
64+
if (scale != 1.0) {
65+
const R128 scale_r128 = R128(scale);
66+
r128Div(&value_r128, &value_r128, &scale_r128);
67+
}
68+
p_value = (double)value_r128;
5269
return p_value;
5370
}
5471

0 commit comments

Comments
 (0)