docs: public Decimal toDecimal(int precision, int scale) {
  		ensureMaterialized();
  		if (precision > Decimal.MAX_LONG_DIGITS || this.sizeInBytes > Decimal.MAX_LONG_DIGITS) {
  			return toDecimalSlow(precision, scale);
  		}
  
  		// Data in Decimal is stored by one long value if `precision` <= Decimal.MAX_LONG_DIGITS.
  		// In this case we can directly extract the value from memory segment.
  		int size = getSegments()[0].size();
  		SegmentAndOffset segmentAndOffset = startSegmentAndOffset(size);
  		int totalOffset = 0;
  
  		// Remove white spaces at the beginning
  		byte b = 0;
  		while (totalOffset < this.sizeInBytes) {
  			b = segmentAndOffset.value();
  			if (b != ' ' && b != '\n' && b != '\t') {
  				break;
  			}
  			totalOffset++;
  			segmentAndOffset.nextByte(size);
  		}
  		if (totalOffset == this.sizeInBytes) {
  			// all whitespaces
  			return null;
  		}
  
  		// ======= Significand part begin =======
  		final boolean negative = b == '-';
  		if (negative || b == '+') {
  			segmentAndOffset.nextByte(size);
  			totalOffset++;
  			if (totalOffset == this.sizeInBytes) {
  				// only contains prefix plus/minus
  				return null;
  			}
  		}
  
  		long significand = 0;
  		int exp = 0;
  		int significandLen = 0, pointPos = -1;
  
  		while (totalOffset < this.sizeInBytes) {
  			b = segmentAndOffset.value();
  			totalOffset++;
  			segmentAndOffset.nextByte(size);
  
  			if (b >= '0' && b <= '9') {
  				// No need to worry about overflow, because this.sizeInBytes <= Decimal.MAX_LONG_DIGITS
  				significand = significand * 10 + (b - '0');
  				significandLen++;
  			} else if (b == '.') {
  				if (pointPos >= 0) {
  					// More than one decimal point
  					return null;
  				}
  				pointPos = significandLen;
  			} else {
  				break;
  			}
  		}
  
  		if (pointPos < 0) {
  			pointPos = significandLen;
  		}
  		if (negative) {
  			significand = -significand;
  		}
  		// ======= Significand part end =======
  
  		// ======= Exponential part begin =======
  		if ((b == 'e' || b == 'E') && totalOffset < this.sizeInBytes) {
  			b = segmentAndOffset.value();
  			final boolean expNegative = b == '-';
  			if (expNegative || b == '+') {
  				segmentAndOffset.nextByte(size);
  				totalOffset++;
  				if (totalOffset == this.sizeInBytes) {
  					return null;
  				}
  			}
  
  			int expDigits = 0;
  			// As `precision` <= 18, value absolute range is limited to 10^-18 ~ 10^18.
  			// The worst case is <18-digits>E-36
  			final int expStopValue = 40;
  
  			while (totalOffset < this.sizeInBytes) {
  				b = segmentAndOffset.value();
  				totalOffset++;
  				segmentAndOffset.nextByte(size);
  
  				if (b >= '0' && b <= '9') {
  					// No need to worry about larger exponents,
  					// because they will produce overflow or underflow
  					if (expDigits < expStopValue) {
  						expDigits = expDigits * 10 + (b - '0');
  					}
  				} else {
  					break;
  				}
  			}
  
  			if (expNegative) {
  				expDigits = -expDigits;
  			}
  			exp += expDigits;
  		}
  		exp -= significandLen - pointPos;
  		// ======= Exponential part end =======
  
  		// Check for invalid character at the end
  		while (totalOffset < this.sizeInBytes) {
  			b = segmentAndOffset.value();
  			totalOffset++;
  			segmentAndOffset.nextByte(size);
  			// White spaces are allowed at the end
  			if (b != ' ' && b != '\n' && b != '\t') {
  				return null;
  			}
  		}
  
  		// Round exp to scale
  		int change = exp + scale;
  		if (significandLen + change > precision) {
  			// Overflow
  			return null;
  		}
  		if (change >= 0) {
  			significand *= Decimal.POW10[change];
  		} else {
  			int k = negative ? -5 : 5;
  			significand = (significand + k * Decimal.POW10[-change - 1]) / Decimal.POW10[-change];
  		}
  		return Decimal.fromLong(significand, precision, scale);
  	}