docs: protected static Number stringToNumber(final String val) throws NumberFormatException {
          char initial = val.charAt(0);
          if ((initial >= '0' && initial <= '9') || initial == '-') {
              // decimal representation
              if (isDecimalNotation(val)) {
                  // quick dirty way to see if we need a BigDecimal instead of a Double
                  // this only handles some cases of overflow or underflow
                  if (val.length()>14) {
                      return new BigDecimal(val);
                  }
                  final Double d = Double.valueOf(val);
                  if (d.isInfinite() || d.isNaN()) {
                      // if we can't parse it as a double, go up to BigDecimal
                      // this is probably due to underflow like 4.32e-678
                      // or overflow like 4.65e5324. The size of the string is small
                      // but can't be held in a Double.
                      return new BigDecimal(val);
                  }
                  return d;
              }
              // integer representation.
              // This will narrow any values to the smallest reasonable Object representation
              // (Integer, Long, or BigInteger)
              
              // string version
              // The compare string length method reduces GC,
              // but leads to smaller integers being placed in larger wrappers even though not
              // needed. i.e. 1,000,000,000 -> Long even though it's an Integer
              // 1,000,000,000,000,000,000 -> BigInteger even though it's a Long
              //if(val.length()<=9){
              //    return Integer.valueOf(val);
              //}
              //if(val.length()<=18){
              //    return Long.valueOf(val);
              //}
              //return new BigInteger(val);
              
              // BigInteger version: We use a similar bitLenth compare as
              // BigInteger#intValueExact uses. Increases GC, but objects hold
              // only what they need. i.e. Less runtime overhead if the value is
              // long lived. Which is the better tradeoff? This is closer to what's
              // in stringToValue.
              BigInteger bi = new BigInteger(val);
              if(bi.bitLength()<=31){
                  return Integer.valueOf(bi.intValue());
              }
              if(bi.bitLength()<=63){
                  return Long.valueOf(bi.longValue());
              }
              return bi;
          }
          throw new NumberFormatException("val ["+val+"] is not a valid number.");
      }