Implement math.pow() for all platforms, add unit tests for basic math operations.

This commit is contained in:
James Roseborough
2007-12-11 19:47:38 +00:00
parent 42b94709f0
commit 6569563ddc
11 changed files with 154 additions and 42 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);

View File

@@ -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" );

View 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 );
}
}

View File

@@ -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() )