Implement math.pow() for all platforms, add unit tests for basic math operations.
This commit is contained in:
@@ -35,6 +35,7 @@ import org.luaj.vm.LString;
|
||||
import org.luaj.vm.LValue;
|
||||
import org.luaj.vm.LocVars;
|
||||
import org.luaj.vm.Lua;
|
||||
import org.luaj.vm.Platform;
|
||||
|
||||
|
||||
public class FuncState extends LuaC {
|
||||
@@ -840,11 +841,9 @@ public class FuncState extends LuaC {
|
||||
case OP_MOD:
|
||||
r = (LNumber) v2.luaBinOpUnknown(op, v1);
|
||||
break;
|
||||
/* TODO: replace this with something reasonable.
|
||||
case OP_POW:
|
||||
r = new LDouble( Math.pow(v1.luaAsDouble(), v2.luaAsDouble() ) );
|
||||
r = Platform.getInstance().mathPow( v1.toJavaDouble(), v2.toJavaDouble() );
|
||||
break;
|
||||
//*/
|
||||
case OP_UNM:
|
||||
r = (LNumber) v1.luaUnaryMinus();
|
||||
break;
|
||||
|
||||
@@ -51,7 +51,8 @@ public class LuaC extends Lua implements LuaCompiler {
|
||||
}
|
||||
|
||||
protected static void _assert(boolean b) {
|
||||
if (!b) throw new LuaErrorException("compiler assert failed");
|
||||
if (!b)
|
||||
throw new LuaErrorException("compiler assert failed");
|
||||
}
|
||||
|
||||
public static final int MAXSTACK = 250;
|
||||
|
||||
@@ -199,7 +199,7 @@ public class BaseLib extends LFunction {
|
||||
}
|
||||
case ASSERT: {
|
||||
if ( ! vm.toboolean(2) )
|
||||
vm.error( vm.gettop()>2? vm.tostring(3): "assertion failed!", 0 );
|
||||
vm.error( vm.gettop()>2? vm.tostring(3): "assertion failed!" );
|
||||
vm.remove(1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -39,19 +39,20 @@ public class LDouble extends LNumber {
|
||||
}
|
||||
|
||||
public String toJavaString() {
|
||||
if ( Double.isNaN(m_value) )
|
||||
return "-1.#IND";
|
||||
if ( Double.isInfinite(m_value) )
|
||||
return (m_value>0? "1.#INF": "-1.#INF");
|
||||
long l = (long) m_value;
|
||||
if ( m_value == (double) l ) {
|
||||
// TODO: is this a good idea?
|
||||
if ( (m_value == (double) l) && (m_value <= Long.MAX_VALUE) && (m_value >= Long.MIN_VALUE) ) {
|
||||
return Long.toString( l );
|
||||
} else {
|
||||
return Double.toString( m_value );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// return true if value survives as an integer
|
||||
public boolean isInteger() {
|
||||
// Cast to int and then back to double and see if the value
|
||||
// survives the round trip.
|
||||
return ( (double) ( (int) m_value ) ) == m_value;
|
||||
}
|
||||
|
||||
@@ -77,13 +78,17 @@ public class LDouble extends LNumber {
|
||||
case Lua.OP_MUL: return new LDouble( lhs * rhs );
|
||||
case Lua.OP_DIV: return new LDouble( lhs / rhs );
|
||||
case Lua.OP_MOD: return new LDouble( lhs - Math.floor(lhs/rhs) * rhs );
|
||||
case Lua.OP_POW: throw new LuaErrorException("math.pow() not implemented for doubles");
|
||||
case Lua.OP_POW: {
|
||||
// allow platform to override math.pow()
|
||||
LValue result = Platform.getInstance().mathPow(lhs, rhs);
|
||||
if ( result == null )
|
||||
return new LDouble( dpow( lhs, rhs ) );
|
||||
}
|
||||
}
|
||||
LuaState.vmerror( "bad bin opcode" );
|
||||
return null;
|
||||
}
|
||||
|
||||
/* warning: NOT TESTED
|
||||
public static double dpow(double a, double b) {
|
||||
if ( b < 0 )
|
||||
return 1 / dpow( a, -b );
|
||||
@@ -102,8 +107,6 @@ public class LDouble extends LNumber {
|
||||
}
|
||||
return p;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
public int toJavaInt() {
|
||||
return (int) m_value;
|
||||
|
||||
@@ -81,21 +81,14 @@ public class LInteger extends LNumber {
|
||||
case Lua.OP_ADD: return LInteger.valueOf( m_value + rhs );
|
||||
case Lua.OP_SUB: return LInteger.valueOf( m_value - rhs );
|
||||
case Lua.OP_MUL: return LInteger.valueOf( m_value * rhs );
|
||||
case Lua.OP_DIV: return LInteger.valueOf( m_value / rhs );
|
||||
case Lua.OP_MOD: return LInteger.valueOf( m_value - ((int) Math.floor(m_value/(double)rhs)) * rhs );
|
||||
case Lua.OP_POW: return LInteger.valueOf( ipow(m_value, rhs) );
|
||||
case Lua.OP_DIV:
|
||||
case Lua.OP_MOD:
|
||||
case Lua.OP_POW:
|
||||
return LDouble.luaBinOpDoubleDouble(opcode, m_value, rhs);
|
||||
}
|
||||
LuaState.vmerror( "bad bin opcode" );
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int ipow(int v, int rhs) {
|
||||
int p = 1;
|
||||
for ( ; rhs > 0; rhs>>=1, v=v*v )
|
||||
if ( (rhs & 1) != 0 )
|
||||
p *= v;
|
||||
return p;
|
||||
}
|
||||
|
||||
// binary operations on mixed integer, double
|
||||
public LValue luaBinOpDouble(int opcode, double rhs) {
|
||||
|
||||
@@ -139,4 +139,14 @@ abstract public class Platform {
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute math.pow() for two numbers using double math when available.
|
||||
* @param lhs LNumber base
|
||||
* @param rhs LNumber exponent
|
||||
* @return base ^ exponent as a LNumber, or null if not implemented
|
||||
*/
|
||||
public LNumber mathPow(double lhs, double rhs) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
******************************************************************************/
|
||||
package org.luaj.sample;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -29,6 +31,7 @@ import org.luaj.vm.LClosure;
|
||||
import org.luaj.vm.LPrototype;
|
||||
import org.luaj.vm.LValue;
|
||||
import org.luaj.vm.LoadState;
|
||||
import org.luaj.vm.LuaErrorException;
|
||||
import org.luaj.vm.LuaState;
|
||||
|
||||
|
||||
@@ -45,23 +48,39 @@ public class LuaRunner {
|
||||
LuaState state = LuaState.newState();
|
||||
|
||||
// get script name
|
||||
String script = (args.length>0? args[0]: "/test2.luac");
|
||||
System.out.println("loading '"+script+"'");
|
||||
|
||||
// add standard bindings
|
||||
state.installStandardLibs();
|
||||
LuaC.install();
|
||||
for ( int i=0; i<args.length; i++ ) {
|
||||
String script = args[i];
|
||||
try {
|
||||
System.out.println("loading '"+script+"'");
|
||||
|
||||
// load the file
|
||||
InputStream is = LuaRunner.class.getResourceAsStream( script );
|
||||
if ( is == null )
|
||||
throw new java.io.FileNotFoundException( "not found: "+script );
|
||||
LPrototype p = LoadState.undump(state, is, script);
|
||||
// add standard bindings
|
||||
state.installStandardLibs();
|
||||
LuaC.install();
|
||||
|
||||
// load the file
|
||||
InputStream is = null;
|
||||
File f = new File(script);
|
||||
if ( f.exists() )
|
||||
is = new FileInputStream( f );
|
||||
else
|
||||
is = LuaRunner.class.getResourceAsStream( script );
|
||||
if ( is == null )
|
||||
throw new java.io.FileNotFoundException( "not found: "+script );
|
||||
LPrototype p = LoadState.undump(state, is, script);
|
||||
|
||||
// create closure and execute
|
||||
LClosure c = new LClosure( p, state._G );
|
||||
|
||||
// create closure and execute
|
||||
LClosure c = new LClosure( p, state._G );
|
||||
|
||||
// do the call
|
||||
state.doCall( c, new LValue[0] );
|
||||
// do the call
|
||||
state.doCall( c, new LValue[0] );
|
||||
} catch ( LuaErrorException lee ) {
|
||||
System.err.println(script+" lua error, "+lee.getMessage() );
|
||||
} catch ( Throwable t ) {
|
||||
System.err.println(script+" threw "+t);
|
||||
} finally {
|
||||
System.out.flush();
|
||||
System.err.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public class AllTests {
|
||||
TestSuite vm = new TestSuite("VM");
|
||||
vm.addTestSuite(org.luaj.vm.LoadStateTest.class);
|
||||
vm.addTestSuite(org.luaj.vm.LStringTest.class);
|
||||
vm.addTestSuite(org.luaj.vm.MathLibTest.class);
|
||||
vm.addTestSuite(org.luaj.vm.LTableTest.class);
|
||||
vm.addTestSuite(org.luaj.vm.LuaJTest.class);
|
||||
suite.addTest(vm);
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
package org.luaj.compiler;
|
||||
|
||||
import org.luaj.debug.j2se.J2sePlatform;
|
||||
import org.luaj.vm.LDouble;
|
||||
import org.luaj.vm.LNumber;
|
||||
import org.luaj.vm.Platform;
|
||||
|
||||
|
||||
public class CompilerUnitTests extends AbstractUnitTests {
|
||||
|
||||
static {
|
||||
// override platform to test with standard debug features.
|
||||
Platform.setInstance( new J2sePlatform() {
|
||||
public LNumber mathPow(double lhs, double rhs) {
|
||||
double d = Math.pow(lhs, rhs);
|
||||
return LDouble.valueOf(d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public CompilerUnitTests() {
|
||||
super( "src/test/compile/lua5.1-tests.zip",
|
||||
"lua5.1-tests" );
|
||||
|
||||
29
src/test/java/org/luaj/vm/MathLibTest.java
Normal file
29
src/test/java/org/luaj/vm/MathLibTest.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package org.luaj.vm;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class MathLibTest extends TestCase {
|
||||
|
||||
public void testMathDPow() {
|
||||
assertEquals( 1, LDouble.dpow(2, 0), 0 );
|
||||
assertEquals( 2, LDouble.dpow(2, 1), 0 );
|
||||
assertEquals( 8, LDouble.dpow(2, 3), 0 );
|
||||
assertEquals( -8, LDouble.dpow(-2, 3), 0 );
|
||||
assertEquals( 1/8., LDouble.dpow(2, -3), 0 );
|
||||
assertEquals( -1/8., LDouble.dpow(-2, -3), 0 );
|
||||
assertEquals( 16, LDouble.dpow(256, .5), 0 );
|
||||
assertEquals( 4, LDouble.dpow(256, .25), 0 );
|
||||
assertEquals( 64, LDouble.dpow(256, .75), 0 );
|
||||
assertEquals( 1./16, LDouble.dpow(256, - .5), 0 );
|
||||
assertEquals( 1./ 4, LDouble.dpow(256, -.25), 0 );
|
||||
assertEquals( 1./64, LDouble.dpow(256, -.75), 0 );
|
||||
assertEquals( Double.NaN, LDouble.dpow(-256, .5), 0 );
|
||||
assertEquals( 1, LDouble.dpow(.5, 0), 0 );
|
||||
assertEquals( .5, LDouble.dpow(.5, 1), 0 );
|
||||
assertEquals(.125, LDouble.dpow(.5, 3), 0 );
|
||||
assertEquals( 2, LDouble.dpow(.5, -1), 0 );
|
||||
assertEquals( 8, LDouble.dpow(.5, -3), 0 );
|
||||
assertEquals(1, LDouble.dpow(0.0625, 0), 0 );
|
||||
assertEquals(0.00048828125, LDouble.dpow(0.0625, 2.75), 0 );
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,48 @@ print( math.sin( 0.0 ) )
|
||||
print( math.cos( math.pi ) )
|
||||
print( math.sqrt( 9.0 ) )
|
||||
print( math.modf( 5.25 ) )
|
||||
|
||||
|
||||
-- binary ops
|
||||
function binops( a, b )
|
||||
local sa = tostring(a)
|
||||
local sb = tostring(b)
|
||||
print( sa..'+'..sb..'='..tostring(a+b) )
|
||||
print( sa..'-'..sb..'='..tostring(a-b) )
|
||||
print( sa..'*'..sb..'='..tostring(a*b) )
|
||||
print( sa..'^'..sb..'='..tostring(a^b) )
|
||||
print( sa..'/'..sb..'=',pcall( function() return a / b end ) )
|
||||
print( sa..'%'..sb..'=',pcall( function() return a % b end ) )
|
||||
return '--'
|
||||
end
|
||||
print( pcall( binops, 2, 0 ) )
|
||||
print( pcall( binops, -2, 0 ) )
|
||||
print( pcall( binops, 2.5, 0 ) )
|
||||
print( pcall( binops, -2.5, 0 ) )
|
||||
print( pcall( binops, 2, 1 ) )
|
||||
print( pcall( binops, 5, 2 ) )
|
||||
print( pcall( binops, -5, 2 ) )
|
||||
print( pcall( binops, 16, -2 ) )
|
||||
print( pcall( binops, -16, -2 ) )
|
||||
print( pcall( binops, 256, .5 ) )
|
||||
print( pcall( binops, 256, .25 ) )
|
||||
print( pcall( binops, 256, .625 ) )
|
||||
print( pcall( binops, 256, -.5 ) )
|
||||
print( pcall( binops, 256, -.25 ) )
|
||||
print( pcall( binops, 256, -.625 ) )
|
||||
print( pcall( binops, -256, .5 ) )
|
||||
print( pcall( binops, .5, 0 ) )
|
||||
print( pcall( binops, .5, 1 ) )
|
||||
print( pcall( binops, .5, 2 ) )
|
||||
print( pcall( binops, .5, -1 ) )
|
||||
print( pcall( binops, .5, -2 ) )
|
||||
print( pcall( binops, 2.25, 0 ) )
|
||||
print( pcall( binops, 2.25, 2 ) )
|
||||
print( pcall( binops, 2.25, .5 ) )
|
||||
print( pcall( binops, 2.25, 2.5 ) )
|
||||
|
||||
|
||||
-- random tests
|
||||
print( math.random(5,10) )
|
||||
print( math.random(5,10) )
|
||||
print( math.random(5,10) )
|
||||
@@ -10,7 +52,7 @@ print( math.random() )
|
||||
print( math.random() )
|
||||
print( math.random() )
|
||||
print( math.random() )
|
||||
print( math.randomseed(20), math.random() )
|
||||
print( math.randomseed(20), math.random(), math.random(), math.random() )
|
||||
print( math.randomseed(20), math.random() )
|
||||
print( math.randomseed(20), math.random() )
|
||||
print( math.randomseed(30), math.random() )
|
||||
|
||||
Reference in New Issue
Block a user