@@ -166,17 +166,94 @@ class DecimalUtilOp {
166166 }
167167 }
168168
169+ // Convert a number of scientific notation to normal.
170+ inline static std::string getNormalNumber (const std::string& value) {
171+ size_t dotPos = value.find (' .' );
172+ size_t expPos = value.find (' E' );
173+ if (expPos == std::string::npos) {
174+ return value;
175+ }
176+
177+ std::string ints;
178+ std::string digits;
179+ // Get the integers and digits from the base number.
180+ if (dotPos == std::string::npos) {
181+ ints = value.substr (0 , expPos);
182+ digits = " " ;
183+ } else {
184+ ints = value.substr (0 , dotPos);
185+ digits = value.substr (dotPos + 1 , expPos - dotPos - 1 );
186+ }
187+
188+ size_t pos = value.find (" E+" );
189+ // Handle number with positive exponent.
190+ if (pos != std::string::npos) {
191+ int exponent = std::stoi (value.substr (pos + 2 , value.length ()));
192+ std::string number = ints;
193+ if (exponent >= digits.length ()) {
194+ // Dot is not needed.
195+ number = ints + digits;
196+ for (int i = 0 ; i < exponent - digits.length (); i++) {
197+ number += ' 0' ;
198+ }
199+ } else {
200+ number += digits.substr (0 , exponent) + ' .' +
201+ digits.substr (exponent + 1 , digits.length ());
202+ }
203+ return number;
204+ }
205+ pos = value.find (" E-" );
206+ if (pos != std::string::npos) {
207+ int exponent = std::stoi (value.substr (pos + 2 , value.length ()));
208+ std::string number;
209+ if (exponent < ints.length ()) {
210+ number = ints.substr (0 , ints.length () - exponent) + ' .' +
211+ ints.substr (ints.length () - exponent + 1 , ints.length ());
212+ } else {
213+ number = " 0." ;
214+ for (int i = 0 ; i < exponent - ints.length (); i++) {
215+ number += ' 0' ;
216+ }
217+ number += ints;
218+ number += digits;
219+ }
220+ return number;
221+ }
222+ return value;
223+ }
224+
225+ // Round double to certain precision with half up.
226+ inline static double roundTo (double value, int precision) {
227+ int charsNeeded = 1 + snprintf (NULL , 0 , " %.*f" , (int )precision, value);
228+ char * buffer = reinterpret_cast <char *>(malloc (charsNeeded));
229+ double nextValue;
230+ if (value < 0 ) {
231+ nextValue = nextafter (value, value - 0.1 );
232+ } else {
233+ nextValue = nextafter (value, value + 0.1 );
234+ }
235+ snprintf (buffer, charsNeeded, " %.*f" , (int )precision, nextValue);
236+ return atof (buffer);
237+ }
238+
169239 // return unscaled value and scale
170240 inline static std::pair<std::string, uint8_t > splitVarChar (
171- const StringView& value) {
172- std::string s = value.str ();
241+ const StringView& value,
242+ int toScale) {
243+ std::string s = getNormalNumber (value.str ());
173244 size_t pos = s.find (' .' );
174245 if (pos == std::string::npos) {
175246 return {s.substr (0 , pos), 0 };
247+ } else if (toScale < s.length () - pos - 1 ) {
248+ // If toScale is less than scales.length(), the string scales will be cut
249+ // and rounded.
250+ std::string roundedValue = std::to_string (roundTo (std::stod (s), toScale));
251+ pos = roundedValue.find (' .' );
252+ std::string scales = roundedValue.substr (pos + 1 , toScale);
253+ return {roundedValue.substr (0 , pos) + scales, scales.length ()};
176254 } else {
177- return {
178- s.substr (0 , pos) + s.substr (pos + 1 , s.length ()),
179- s.length () - pos - 1 };
255+ std::string scales = s.substr (pos + 1 , s.length ());
256+ return {s.substr (0 , pos) + scales, scales.length ()};
180257 }
181258 }
182259
@@ -237,7 +314,7 @@ class DecimalUtilOp {
237314 static_assert (
238315 std::is_same_v<TOutput, UnscaledShortDecimal> ||
239316 std::is_same_v<TOutput, UnscaledLongDecimal>);
240- auto [unscaledStr, fromScale] = splitVarChar (inputValue);
317+ auto [unscaledStr, fromScale] = splitVarChar (inputValue, toScale );
241318 uint8_t fromPrecision = unscaledStr.size ();
242319 VELOX_CHECK_LE (
243320 fromPrecision, DecimalType<TypeKind::LONG_DECIMAL>::kMaxPrecision );
0 commit comments