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 org.luaj.vm2.libs.jse.JsePlatform;
|
||||
import org.luaj.vm2.libs.ApproxLib;
|
||||
import org.luaj.vm2.luajc.LuaJC;
|
||||
|
||||
/**
|
||||
@@ -373,6 +374,22 @@ public class FragmentsTest extends TestSuite {
|
||||
"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() {
|
||||
runFragment(
|
||||
LuaValue.varargsOf(new LuaValue[] {
|
||||
|
||||
@@ -30,6 +30,7 @@ import junit.framework.TestCase;
|
||||
import org.luaj.vm2.TypeTest.MyData;
|
||||
import org.luaj.vm2.libs.ZeroArgFunction;
|
||||
import org.luaj.vm2.libs.jse.JsePlatform;
|
||||
import org.luaj.vm2.libs.jse.LuaStateSync;
|
||||
|
||||
public class LuaOperationsTest extends TestCase {
|
||||
|
||||
@@ -173,4 +174,14 @@ public class LuaOperationsTest extends TestCase {
|
||||
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