MCPcopy Create free account
hub / github.com/apache/arrow / FromPositiveReal

Method FromPositiveReal

cpp/src/arrow/util/decimal.cc:114–239  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

112
113 template <typename Real>
114 static Result<DecimalType> FromPositiveReal(Real real, int32_t precision,
115 int32_t scale) {
116 constexpr int kMantissaBits = RealTraits<Real>::kMantissaBits;
117 constexpr int kMantissaDigits = RealTraits<Real>::kMantissaDigits;
118
119 // to avoid precision and rounding issues, we'll unconditionally
120 // throw Decimal32 to the approx algorithm instead. (GH-44216)
121 if constexpr (std::is_base_of_v<BasicDecimal32, DecimalType>) {
122 return Derived::FromPositiveRealApprox(real, precision, scale);
123 }
124
125 // Problem statement: construct the Decimal with the value
126 // closest to `real * 10^scale`.
127 if (scale < 0) {
128 // Negative scales are not handled below, fall back to approx algorithm
129 return Derived::FromPositiveRealApprox(real, precision, scale);
130 }
131
132 // 1. Check that `real` is within acceptable bounds.
133 const Real limit = PowerOfTen<Real>(precision - scale);
134 if (real > limit) {
135 // Checking the limit early helps ensure the computations below do not
136 // overflow.
137 // NOTE: `limit` is allowed here as rounding can make it smaller than
138 // the theoretical limit (for example, 1.0e23 < 10^23).
139 return OverflowError(real, precision, scale);
140 }
141
142 // The algorithm below requires the destination decimal type
143 // to be strictly more precise than the source float type
144 // (see `kSafeMulByTenTo` calculation).
145 if constexpr (kMaxPrecision <= kMantissaDigits) {
146 return Derived::FromPositiveRealApprox(real, precision, scale);
147 }
148
149 // 2. Losslessly convert `real` to `mant * 2**k`
150 int binary_exp = 0;
151 const Real real_mant = std::frexp(real, &binary_exp);
152 // `real_mant` is within 0.5 and 1 and has M bits of precision.
153 // Multiply it by 2^M to get an exact integer.
154 const uint64_t mant = static_cast<uint64_t>(std::ldexp(real_mant, kMantissaBits));
155 const int k = binary_exp - kMantissaBits;
156 // (note that `real = mant * 2^k`)
157
158 // 3. Start with `mant`.
159 // We want to end up with `real * 10^scale` i.e. `mant * 2^k * 10^scale`.
160 DecimalType x(mant);
161
162 if (k < 0) {
163 // k < 0 (i.e. binary_exp < kMantissaBits), is probably the common case
164 // when converting to decimal. It implies right-shifting by -k bits,
165 // while multiplying by 10^scale. We also must avoid overflow (losing
166 // bits on the left) and precision loss (losing bits on the right).
167 int right_shift_by = -k;
168 int mul_by_ten_to = scale;
169
170 // At this point, `x` has kMantissaDigits significant digits but it can
171 // fit kMaxPrecision (excluding sign). We can therefore multiply by up

Callers

nothing calls this directly

Calls 1

FitsInPrecisionMethod · 0.80

Tested by

no test coverage detected