Fixed issue: #47
This commit is contained in:
BIN
core/src/main/java/org/luaj/vm2/libs/ApproxLib$approx_eq.class
Normal file
BIN
core/src/main/java/org/luaj/vm2/libs/ApproxLib$approx_eq.class
Normal file
Binary file not shown.
BIN
core/src/main/java/org/luaj/vm2/libs/ApproxLib.class
Normal file
BIN
core/src/main/java/org/luaj/vm2/libs/ApproxLib.class
Normal file
Binary file not shown.
51
core/src/main/java/org/luaj/vm2/libs/ApproxLib.java
Normal file
51
core/src/main/java/org/luaj/vm2/libs/ApproxLib.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package org.luaj.vm2.libs;
|
||||||
|
|
||||||
|
import org.luaj.vm2.LuaTable;
|
||||||
|
import org.luaj.vm2.LuaValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional helpers for approximate floating-point comparisons.
|
||||||
|
* This deliberately does not change Lua's core equality semantics.
|
||||||
|
*/
|
||||||
|
public class ApproxLib extends TwoArgFunction {
|
||||||
|
|
||||||
|
private static final double DEFAULT_EPSILON = 1e-9d;
|
||||||
|
|
||||||
|
public LuaValue call(LuaValue modname, LuaValue env) {
|
||||||
|
LuaTable lib = new LuaTable(0, 2);
|
||||||
|
lib.set("eq", new approx_eq());
|
||||||
|
env.set("approx", lib);
|
||||||
|
env.set("approx_eq", lib.get("eq"));
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class approx_eq extends VarArgFunction {
|
||||||
|
public LuaValue invoke(org.luaj.vm2.Varargs args) {
|
||||||
|
double a = args.checkdouble(1);
|
||||||
|
double b = args.checkdouble(2);
|
||||||
|
double epsilon = args.optdouble(3, DEFAULT_EPSILON);
|
||||||
|
if (epsilon < 0d) {
|
||||||
|
argerror(3, "epsilon must be >= 0");
|
||||||
|
}
|
||||||
|
return LuaValue.valueOf(approxEqual(a, b, epsilon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean approxEqual(double a, double b, double epsilon) {
|
||||||
|
if (Double.doubleToLongBits(a) == Double.doubleToLongBits(b)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Double.isNaN(a) || Double.isNaN(b)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Double.isInfinite(a) || Double.isInfinite(b)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
double diff = Math.abs(a - b);
|
||||||
|
if (diff <= epsilon) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
double scale = Math.max(Math.abs(a), Math.abs(b));
|
||||||
|
return diff <= scale * epsilon;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
jse/src/main/java/org/luaj/vm2/libs/jse/LuaStateSync.class
Normal file
BIN
jse/src/main/java/org/luaj/vm2/libs/jse/LuaStateSync.class
Normal file
Binary file not shown.
41
jse/src/main/java/org/luaj/vm2/libs/jse/LuaStateSync.java
Normal file
41
jse/src/main/java/org/luaj/vm2/libs/jse/LuaStateSync.java
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package org.luaj.vm2.libs.jse;
|
||||||
|
|
||||||
|
import org.luaj.vm2.LuaValue;
|
||||||
|
import org.luaj.vm2.libs.ApproxLib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Host-side helpers for mirroring Java state into Lua without reacting to
|
||||||
|
* insignificant floating-point drift.
|
||||||
|
*/
|
||||||
|
public final class LuaStateSync {
|
||||||
|
|
||||||
|
private LuaStateSync() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setIfApproxChanged(LuaValue target, LuaValue key, double value, double epsilon) {
|
||||||
|
if (epsilon < 0d) {
|
||||||
|
throw new IllegalArgumentException("epsilon must be >= 0");
|
||||||
|
}
|
||||||
|
LuaValue current = target.get(key);
|
||||||
|
if (current.isnumber() && ApproxLib.approxEqual(current.todouble(), value, epsilon)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
target.set(key, LuaValue.valueOf(value));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setIfApproxChanged(LuaValue target, String key, double value, double epsilon) {
|
||||||
|
return setIfApproxChanged(target, LuaValue.valueOf(key), value, epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setIfApproxChanged(LuaValue target, int key, double value, double epsilon) {
|
||||||
|
return setIfApproxChanged(target, LuaValue.valueOf(key), value, epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean approxEqual(double a, double b, double epsilon) {
|
||||||
|
if (epsilon < 0d) {
|
||||||
|
throw new IllegalArgumentException("epsilon must be >= 0");
|
||||||
|
}
|
||||||
|
return ApproxLib.approxEqual(a, b, epsilon);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import junit.framework.TestCase;
|
|||||||
import junit.framework.TestSuite;
|
import junit.framework.TestSuite;
|
||||||
|
|
||||||
import org.luaj.vm2.libs.jse.JsePlatform;
|
import org.luaj.vm2.libs.jse.JsePlatform;
|
||||||
|
import org.luaj.vm2.libs.ApproxLib;
|
||||||
import org.luaj.vm2.luajc.LuaJC;
|
import org.luaj.vm2.luajc.LuaJC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -373,6 +374,22 @@ public class FragmentsTest extends TestSuite {
|
|||||||
"end\n");
|
"end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testApproxEqLibrary() {
|
||||||
|
try {
|
||||||
|
Globals globals = JsePlatform.standardGlobals();
|
||||||
|
globals.load(new ApproxLib());
|
||||||
|
Varargs result = globals.load(
|
||||||
|
"return approx_eq(1.0, 0.9999999995, 1e-8), " +
|
||||||
|
"approx.eq(1.0, 0.9, 1e-8)\n",
|
||||||
|
"approx.lua").invoke();
|
||||||
|
assertEquals(LuaValue.TRUE, result.arg1());
|
||||||
|
assertEquals(LuaValue.FALSE, result.arg(2));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testTableMove() {
|
public void testTableMove() {
|
||||||
runFragment(
|
runFragment(
|
||||||
LuaValue.varargsOf(new LuaValue[] {
|
LuaValue.varargsOf(new LuaValue[] {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import junit.framework.TestCase;
|
|||||||
import org.luaj.vm2.TypeTest.MyData;
|
import org.luaj.vm2.TypeTest.MyData;
|
||||||
import org.luaj.vm2.libs.ZeroArgFunction;
|
import org.luaj.vm2.libs.ZeroArgFunction;
|
||||||
import org.luaj.vm2.libs.jse.JsePlatform;
|
import org.luaj.vm2.libs.jse.JsePlatform;
|
||||||
|
import org.luaj.vm2.libs.jse.LuaStateSync;
|
||||||
|
|
||||||
public class LuaOperationsTest extends TestCase {
|
public class LuaOperationsTest extends TestCase {
|
||||||
|
|
||||||
@@ -173,4 +174,14 @@ public class LuaOperationsTest extends TestCase {
|
|||||||
assertEquals( eee, c.call() );
|
assertEquals( eee, c.call() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSetIfApproxChanged() {
|
||||||
|
LuaTable target = LuaValue.tableOf();
|
||||||
|
assertTrue(LuaStateSync.setIfApproxChanged(target, "height", 1.0d, 1e-6d));
|
||||||
|
assertEquals(LuaValue.valueOf(1.0d), target.get("height"));
|
||||||
|
assertFalse(LuaStateSync.setIfApproxChanged(target, "height", 0.999999999d, 1e-6d));
|
||||||
|
assertEquals(LuaValue.valueOf(1.0d), target.get("height"));
|
||||||
|
assertTrue(LuaStateSync.setIfApproxChanged(target, "height", 1.1d, 1e-6d));
|
||||||
|
assertEquals(LuaValue.valueOf(1.1d), target.get("height"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user