diff --git a/src/main/java/lua/io/LoadState.java b/src/main/java/lua/io/LoadState.java index 761e5007..b7cfb1fa 100644 --- a/src/main/java/lua/io/LoadState.java +++ b/src/main/java/lua/io/LoadState.java @@ -189,14 +189,33 @@ public class LoadState { return new LString( s ); } + static LNumber longBitsToLuaNumber( long bits ) { + if ( ( bits & ( ( 1L << 63 ) - 1 ) ) == 0L ) { + return new LInteger( 0 ); + } + + int e = (int)((bits >> 52) & 0x7ffL) - 1023; + + if ( e >= 0 && e < 31 ) { + long f = bits & 0xFFFFFFFFFFFFFL; + int shift = 52 - e; + long intPrecMask = ( 1L << shift ) - 1; + if ( ( f & intPrecMask ) == 0 ) { + int intValue = (int)( f >> shift ) | ( 1 << e ); + return new LInteger( ( ( bits >> 63 ) != 0 ) ? -intValue : intValue ); + } + } + + double value = Double.longBitsToDouble(bits); + return new LDouble( value ); + } + LNumber loadNumber() throws IOException { if ( this.luacIsNumberIntegral ) { int value = loadInt(); return new LInteger( value ); } else { - long bits = loadInt64(); - double value = Double.longBitsToDouble(bits); - return new LDouble( value ); + return longBitsToLuaNumber( loadInt64() ); } } // diff --git a/src/test/java/lua/io/LoadStateTest.java b/src/test/java/lua/io/LoadStateTest.java new file mode 100644 index 00000000..546d3068 --- /dev/null +++ b/src/test/java/lua/io/LoadStateTest.java @@ -0,0 +1,92 @@ +package lua.io; + +import java.util.Random; + +import junit.framework.TestCase; +import lua.value.LDouble; +import lua.value.LInteger; +import lua.value.LNumber; + +public class LoadStateTest extends TestCase { + double[] DOUBLE_VALUES = { + 0.0, + 1.0, + 2.5, + 10.0, + 16.0, + 16.125, + -1.0, + 2.0, + -2.0, + -10.0, + -0.25, + -25, + Integer.MAX_VALUE, + Integer.MAX_VALUE - 1, + (double)Integer.MAX_VALUE + 1.0, + Integer.MIN_VALUE, + Integer.MIN_VALUE + 1, + (double)Integer.MIN_VALUE - 1.0, + Double.NEGATIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.MAX_VALUE, + Double.MIN_NORMAL + }; + + public void testLongBitsToLuaNumber() { + for ( int i = 0; i < DOUBLE_VALUES.length; ++i ) { + double v = DOUBLE_VALUES[i]; + long bits = Double.doubleToLongBits( v ); + LNumber luaNumber = LoadState.longBitsToLuaNumber( bits ); + + assertEquals( v, luaNumber.luaAsDouble() ); + + if ( v != Integer.MIN_VALUE ) { + // Special case of MIN_VALUE is probably not worth dealing with. + // (Unlike zero, which is also a special case but much more common.) + assertEquals( "Value "+v+" (at index "+i+") can be represented as integer but was not", + luaNumber instanceof LInteger, v == (double)( (int) v ) ); + } + } + } + + private LNumber simpleBitsToLuaNumber( long bits ) { + double value = Double.longBitsToDouble( bits ); + int valueAsInt = (int) value; + + if ( value == (double) valueAsInt ) { + return new LInteger( valueAsInt ); + } else { + return new LDouble( value ); + } + } + + public void testLongBitsToLuaNumberSpeed() { + long[] BITS = new long[ 500000 ]; + Random r = new Random(); + + for ( int i = 0; i < DOUBLE_VALUES.length; ++i ) { + BITS[i] = Double.doubleToLongBits( DOUBLE_VALUES[i] ); + } + for ( int i = DOUBLE_VALUES.length; i < BITS.length; i += 2 ) { + BITS[i ] = r.nextLong(); + BITS[i+1] = Double.doubleToLongBits( r.nextDouble() ); + } + + long startTime = System.currentTimeMillis(); + for ( int j = 0; j < BITS.length; ++j ) { + LoadState.longBitsToLuaNumber( BITS[j] ); + } + long endTime = System.currentTimeMillis(); + long complexConversionTime = endTime - startTime; + + startTime = System.currentTimeMillis(); + for ( int j = 0; j < BITS.length; ++j ) { + simpleBitsToLuaNumber( BITS[j] ); + } + endTime = System.currentTimeMillis(); + long simpleConversionTime = endTime - startTime; + + assertTrue( complexConversionTime < simpleConversionTime ); + } +} diff --git a/src/test/res/test2.lua b/src/test/res/test2.lua index 6daf11b0..3993cbe2 100644 --- a/src/test/res/test2.lua +++ b/src/test/res/test2.lua @@ -11,7 +11,7 @@ function myfunc(x) return x*x; end -print( myfunc(0.1) ) +print( myfunc(0.25) ) do local oldMyfunc = myfunc diff --git a/src/test/res/test2.luac b/src/test/res/test2.luac index 0febd6a6..23135847 100644 Binary files a/src/test/res/test2.luac and b/src/test/res/test2.luac differ