|
33 | 33 | #include "thirdparty/misc/r128.h"
|
34 | 34 |
|
35 | 35 | 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; |
51 | 38 | }
|
| 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; |
52 | 69 | return p_value;
|
53 | 70 | }
|
54 | 71 |
|
|
0 commit comments