4444package org .jruby ;
4545
4646import java .io .ByteArrayOutputStream ;
47+ import java .math .BigInteger ;
4748import java .util .ArrayList ;
4849import java .util .Arrays ;
4950import java .util .Map ;
111112import static org .jruby .runtime .Visibility .PRIVATE ;
112113import static org .jruby .runtime .Visibility .PROTECTED ;
113114import static org .jruby .runtime .Visibility .PUBLIC ;
115+ import static org .jruby .util .RubyStringBuilder .str ;
114116
115117/**
116118 * Note: For CVS history, see KernelModule.java.
@@ -412,6 +414,103 @@ public static RubyFloat new_float(final Ruby runtime, IRubyObject object) {
412414 return (RubyFloat ) new_float (runtime .getCurrentContext (), object , true );
413415 }
414416
417+ private static BigInteger SIXTEEN = BigInteger .valueOf (16L );
418+ private static BigInteger MINUS_ONE = BigInteger .valueOf (-1L );
419+
420+ private static RaiseException floatError (Ruby runtime , ByteList string ) {
421+ throw runtime .newArgumentError (str (runtime , "invalid value for Float(): " , runtime .newString (string )));
422+ }
423+
424+ /**
425+ * Parse hexidecimal exponential notation:
426+ * <a href="https://en.wikipedia.org/wiki/Hexadecimal#Hexadecimal_exponential_notation">...</a>
427+ * <p/>
428+ * This method assumes the string is a valid-formatted string.
429+ *
430+ * @param str the bytelist to be parsed
431+ * @return the result.
432+ */
433+ public static double parseHexidecimalExponentString2 (Ruby runtime , ByteList str ) {
434+ byte [] bytes = str .unsafeBytes ();
435+ int length = str .length ();
436+ if (length <= 2 ) throw floatError (runtime , str );
437+ int sign = 1 ;
438+ int letter ;
439+ int i = str .begin ();
440+
441+ letter = bytes [i ];
442+ if (letter == '+' ) {
443+ i ++;
444+ } else if (letter == '-' ) {
445+ sign = -1 ;
446+ i ++;
447+ }
448+
449+ // Skip '0x'
450+ letter = bytes [i ++];
451+ if (letter != '0' ) throw floatError (runtime , str );
452+ letter = bytes [i ++];
453+ if (letter != 'x' ) throw floatError (runtime , str );
454+
455+ int exponent = 0 ;
456+ int explicitExponent = 0 ;
457+ int explicitExponentSign = 1 ;
458+ boolean periodFound = false ;
459+ boolean explicitExponentFound = false ;
460+ BigInteger value = BigInteger .valueOf (0L );
461+
462+ if (i == length || bytes [i ] == '_' || bytes [i ] == 'p' || bytes [i ] == 'P' ) throw floatError (runtime , str );
463+
464+ for (; i < length && !explicitExponentFound ; i ++) {
465+ letter = bytes [i ];
466+ switch (letter ) {
467+ case '.' : // Fractional part of floating point value "1(.)23"
468+ periodFound = true ;
469+ continue ;
470+ case 'p' : // Explicit exponent "1.23(p)1a"
471+ case 'P' :
472+ if (bytes [i -1 ] == '_' || i == length - 1 ) throw floatError (runtime , str );
473+ explicitExponentFound = true ;
474+ continue ;
475+ case '_' :
476+ continue ;
477+ }
478+
479+ // base 16 value representing main pieces of number
480+ int digit = Character .digit (letter , 16 );
481+ if (Character .forDigit (digit , 16 ) == 0 ) throw floatError (runtime , str );
482+ value = value .multiply (SIXTEEN ).add (BigInteger .valueOf (digit ));
483+
484+ if (periodFound ) exponent ++;
485+ }
486+
487+ if (explicitExponentFound ) {
488+ if (bytes [i ] == '-' ) {
489+ explicitExponentSign = -1 ;
490+ i ++;
491+ } else if (bytes [i ] == '+' ) {
492+ i ++;
493+ } else if (bytes [i ] == '_' ) {
494+ throw floatError (runtime , str );
495+ }
496+
497+ for (; i < length ; i ++) { // base 10 value representing base 2 exponent
498+ letter = bytes [i ];
499+ if (letter == '_' ) {
500+ if (i == length - 1 ) throw floatError (runtime , str );
501+ continue ;
502+ }
503+ int digit = Character .digit (letter , 10 );
504+ if (Character .forDigit (digit , 10 ) == 0 ) throw floatError (runtime , str );
505+ explicitExponent = explicitExponent * 10 + digit ;
506+ }
507+ }
508+
509+ // each exponent in main number is 4 bits and the value after 'p' represents a power of 2. Wacky.
510+ int scaleFactor = 4 * exponent - explicitExponent * explicitExponentSign ;
511+ return sign * Math .scalb (value .doubleValue (), -scaleFactor );
512+ }
513+
415514 public static IRubyObject new_float (ThreadContext context , IRubyObject object , boolean exception ) {
416515 Ruby runtime = context .runtime ;
417516
@@ -430,6 +529,9 @@ public static IRubyObject new_float(ThreadContext context, IRubyObject object, b
430529 }
431530
432531 if (bytes .startsWith (ZEROx )) { // startsWith("0x")
532+ if (bytes .indexOf ('p' ) != -1 || bytes .indexOf ('P' ) != -1 ) {
533+ return runtime .newFloat (parseHexidecimalExponentString2 (runtime , bytes ));
534+ }
433535 IRubyObject inum = ConvertBytes .byteListToInum (runtime , bytes , 16 , true , exception );
434536 if (!exception && inum .isNil ()) return inum ;
435537 return ((RubyInteger ) inum ).toFloat ();
0 commit comments