Fix numeric fast-paths for Obj arithmetic

This commit is contained in:
Sergey Chernov 2026-02-05 17:56:01 +03:00
parent 6220e982a0
commit afbd6e45b9
2 changed files with 95 additions and 10 deletions

View File

@ -871,14 +871,21 @@ class CmdAddObj(internal val a: Int, internal val b: Int, internal val dst: Int)
frame.setInt(dst, frame.frame.getInt(la) + frame.frame.getInt(lb)) frame.setInt(dst, frame.frame.getInt(la) + frame.frame.getInt(lb))
return return
} }
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) { val aNumeric = ta == SlotType.INT.code || ta == SlotType.REAL.code
val bNumeric = tb == SlotType.INT.code || tb == SlotType.REAL.code
if (aNumeric && bNumeric && (ta == SlotType.REAL.code || tb == SlotType.REAL.code)) {
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble() val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble() val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
frame.setReal(dst, av + bv) frame.setReal(dst, av + bv)
return return
} }
} }
frame.setObj(dst, frame.slotToObj(a).plus(frame.ensureScope(), frame.slotToObj(b))) val result = frame.slotToObj(a).plus(frame.ensureScope(), frame.slotToObj(b))
when (result) {
is ObjInt -> frame.setInt(dst, result.value)
is ObjReal -> frame.setReal(dst, result.value)
else -> frame.setObj(dst, result)
}
return return
} }
} }
@ -895,14 +902,21 @@ class CmdSubObj(internal val a: Int, internal val b: Int, internal val dst: Int)
frame.setInt(dst, frame.frame.getInt(la) - frame.frame.getInt(lb)) frame.setInt(dst, frame.frame.getInt(la) - frame.frame.getInt(lb))
return return
} }
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) { val aNumeric = ta == SlotType.INT.code || ta == SlotType.REAL.code
val bNumeric = tb == SlotType.INT.code || tb == SlotType.REAL.code
if (aNumeric && bNumeric && (ta == SlotType.REAL.code || tb == SlotType.REAL.code)) {
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble() val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble() val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
frame.setReal(dst, av - bv) frame.setReal(dst, av - bv)
return return
} }
} }
frame.setObj(dst, frame.slotToObj(a).minus(frame.ensureScope(), frame.slotToObj(b))) val result = frame.slotToObj(a).minus(frame.ensureScope(), frame.slotToObj(b))
when (result) {
is ObjInt -> frame.setInt(dst, result.value)
is ObjReal -> frame.setReal(dst, result.value)
else -> frame.setObj(dst, result)
}
return return
} }
} }
@ -919,14 +933,21 @@ class CmdMulObj(internal val a: Int, internal val b: Int, internal val dst: Int)
frame.setInt(dst, frame.frame.getInt(la) * frame.frame.getInt(lb)) frame.setInt(dst, frame.frame.getInt(la) * frame.frame.getInt(lb))
return return
} }
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) { val aNumeric = ta == SlotType.INT.code || ta == SlotType.REAL.code
val bNumeric = tb == SlotType.INT.code || tb == SlotType.REAL.code
if (aNumeric && bNumeric && (ta == SlotType.REAL.code || tb == SlotType.REAL.code)) {
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble() val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble() val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
frame.setReal(dst, av * bv) frame.setReal(dst, av * bv)
return return
} }
} }
frame.setObj(dst, frame.slotToObj(a).mul(frame.ensureScope(), frame.slotToObj(b))) val result = frame.slotToObj(a).mul(frame.ensureScope(), frame.slotToObj(b))
when (result) {
is ObjInt -> frame.setInt(dst, result.value)
is ObjReal -> frame.setReal(dst, result.value)
else -> frame.setObj(dst, result)
}
return return
} }
} }
@ -943,14 +964,21 @@ class CmdDivObj(internal val a: Int, internal val b: Int, internal val dst: Int)
frame.setInt(dst, frame.frame.getInt(la) / frame.frame.getInt(lb)) frame.setInt(dst, frame.frame.getInt(la) / frame.frame.getInt(lb))
return return
} }
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) { val aNumeric = ta == SlotType.INT.code || ta == SlotType.REAL.code
val bNumeric = tb == SlotType.INT.code || tb == SlotType.REAL.code
if (aNumeric && bNumeric && (ta == SlotType.REAL.code || tb == SlotType.REAL.code)) {
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble() val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble() val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
frame.setReal(dst, av / bv) frame.setReal(dst, av / bv)
return return
} }
} }
frame.setObj(dst, frame.slotToObj(a).div(frame.ensureScope(), frame.slotToObj(b))) val result = frame.slotToObj(a).div(frame.ensureScope(), frame.slotToObj(b))
when (result) {
is ObjInt -> frame.setInt(dst, result.value)
is ObjReal -> frame.setReal(dst, result.value)
else -> frame.setObj(dst, result)
}
return return
} }
} }
@ -967,14 +995,21 @@ class CmdModObj(internal val a: Int, internal val b: Int, internal val dst: Int)
frame.setInt(dst, frame.frame.getInt(la) % frame.frame.getInt(lb)) frame.setInt(dst, frame.frame.getInt(la) % frame.frame.getInt(lb))
return return
} }
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) { val aNumeric = ta == SlotType.INT.code || ta == SlotType.REAL.code
val bNumeric = tb == SlotType.INT.code || tb == SlotType.REAL.code
if (aNumeric && bNumeric && (ta == SlotType.REAL.code || tb == SlotType.REAL.code)) {
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble() val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble() val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
frame.setReal(dst, av % bv) frame.setReal(dst, av % bv)
return return
} }
} }
frame.setObj(dst, frame.slotToObj(a).mod(frame.ensureScope(), frame.slotToObj(b))) val result = frame.slotToObj(a).mod(frame.ensureScope(), frame.slotToObj(b))
when (result) {
is ObjInt -> frame.setInt(dst, result.value)
is ObjReal -> frame.setReal(dst, result.value)
else -> frame.setObj(dst, result)
}
return return
} }
} }

View File

@ -96,4 +96,54 @@ class TypesTest {
assertNotEquals(Point(0,1), p3) assertNotEquals(Point(0,1), p3)
""".trimIndent()) """.trimIndent())
} }
@Test
fun testNumericInference() = runTest {
eval("""
val x = 1
var y = 2.0
assert( x is Int )
assert( y is Real )
assert( x + y is Real )
assert( abs(x+y) is Real )
assert( abs(x/y) is Real )
""".trimIndent())
}
@Test
fun testNumericInferenceBug1() = runTest {
eval("""
fun findSumLimit(f) {
var sum = 0.0
for( n in 1..100 ) {
val s0 = sum
sum += f(n)
assert( sum is Real )
assert( s0 is Real )
val delta = abs(sum - s0) / abs(sum)
assert( delta is Real )
println("abs(%g - %g) = %g"(sum, s0, abs(sum-s0)))
if( s0 != 0 )
assert( abs(sum-s0) < abs(sum) )
println("abs(%g) = %g"(sum, abs(sum)))
println( "delta calc: %g"(delta) )
// if( n > 3 ) assert( delta < 1.0 )
if( delta < 1.0e-4 ) {
println("limit reached after "+n+" rounds")
break sum
}
else
println("%g, delta=%g"(sum, delta))
n++
}
else {
println("limit not reached")
null
}
}
val limit = findSumLimit { n -> 1.0/n/n }
assert( limit != null )
println("Result: "+limit)
""".trimIndent())
}
} }