From a0944112f96e41c5e35d30c16715b061cc9a27ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:21:24 +0000 Subject: [PATCH 1/3] Initial plan From 474fd60f3d8f7f65d026de6f4f1b3e9aa3a97e12 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:22:17 +0000 Subject: [PATCH 2/3] Add toFloat, fix toInt truncation, add MIN/MAX to Int64/Int32/UInt64 Co-authored-by: Simn <634365+Simn@users.noreply.github.com> --- std/cpp/_std/haxe/numeric/Int64Native.hx | 10 +++- std/eval/_std/haxe/numeric/Int64Native.hx | 11 +++- std/haxe/Int32.hx | 6 ++ std/haxe/Int64.hx | 18 +++++- std/haxe/UInt64.hx | 6 ++ std/haxe/numeric/Int64Native.hx | 10 +++- std/hl/_std/haxe/numeric/Int64Native.hx | 9 +-- std/jvm/_std/haxe/numeric/Int64Native.hx | 10 ++-- tests/unit/src/unit/TestInt32.hx | 9 ++- tests/unit/src/unit/TestInt64.hx | 55 +++++++++++++++++-- tests/unit/src/unit/TestUInt64.hx | 13 +++++ tests/unit/src/unit/teststd/haxe/TestInt32.hx | 4 ++ 12 files changed, 134 insertions(+), 27 deletions(-) diff --git a/std/cpp/_std/haxe/numeric/Int64Native.hx b/std/cpp/_std/haxe/numeric/Int64Native.hx index fd54a64e3f3..1efe815359c 100644 --- a/std/cpp/_std/haxe/numeric/Int64Native.hx +++ b/std/cpp/_std/haxe/numeric/Int64Native.hx @@ -140,6 +140,7 @@ private abstract Int64NativeImpl(cpp.Int64) from cpp.Int64 to cpp.Int64 { public static function ushr(a:Int64Native, b:Int):Int64Native; public function toString():String; public static function parseString(sParam:String):Int64Native; + public static function toFloat(x:Int64Native):Float; public static function fromFloat(f:Float):Int64Native; #else public var high(get, never):haxe.Int32; @@ -165,8 +166,6 @@ private abstract Int64NativeImpl(cpp.Int64) from cpp.Int64 to cpp.Int64 { } public static #if !scriptable inline #end function toInt(x:Int64Native):Int { - if (x.high != x.low >> 31) - throw "Overflow"; return x.low; } @@ -241,6 +240,13 @@ private abstract Int64NativeImpl(cpp.Int64) from cpp.Int64 to cpp.Int64 { return haxe.numeric.Int64Helper.fromFloat(f); } + public static #if !scriptable inline #end function toFloat(x:Int64Native):Float { + var f:Float = x.low; + if (f < 0) + f += 4294967296.0; + return (x.high : Float) * 4294967296.0 + f; + } + public static function udivMod(dividend:Int64Native, divisor:Int64Native):{quotient:Int64Native, modulus:Int64Native} { return haxe.numeric.UInt64Helper.udivMod(dividend, divisor); } diff --git a/std/eval/_std/haxe/numeric/Int64Native.hx b/std/eval/_std/haxe/numeric/Int64Native.hx index 89f61b3460f..19575e887c9 100644 --- a/std/eval/_std/haxe/numeric/Int64Native.hx +++ b/std/eval/_std/haxe/numeric/Int64Native.hx @@ -59,9 +59,7 @@ private abstract Int64NativeImpl(EvalInt64) from EvalInt64 to EvalInt64 { return EvalInt64.ofInt(x); } - public static function toInt(x:Int64Native):Int { - if (x.high != x.low >> 31) - throw "Overflow"; + public static inline function toInt(x:Int64Native):Int { var v:EvalInt64 = x; return v.toInt(); } @@ -151,6 +149,13 @@ private abstract Int64NativeImpl(EvalInt64) from EvalInt64 to EvalInt64 { return haxe.numeric.Int64Helper.fromFloat(f); } + public static inline function toFloat(x:Int64Native):Float { + var f:Float = x.low; + if (f < 0) + f += 4294967296.0; + return (x.high : Float) * 4294967296.0 + f; + } + public static function udivMod(dividend:Int64Native, divisor:Int64Native):{quotient:Int64Native, modulus:Int64Native} { return haxe.numeric.UInt64Helper.udivMod(dividend, divisor); } diff --git a/std/haxe/Int32.hx b/std/haxe/Int32.hx index 0c348e24831..1f08d229c43 100644 --- a/std/haxe/Int32.hx +++ b/std/haxe/Int32.hx @@ -42,6 +42,12 @@ abstract Int32(Int32Native) from Int to Int { private inline function new(x:Int32Native) this = x; + /** The greatest representable Int32 value: `2^31 - 1`. **/ + public static final MAX:Int32 = 0x7FFFFFFF; + + /** The smallest representable Int32 value: `-2^31`. **/ + public static final MIN:Int32 = 0x80000000; + @:op(-A) private static inline function neg(x:Int32):Int32 return Int32Native.neg(x); diff --git a/std/haxe/Int64.hx b/std/haxe/Int64.hx index 5c8665f2409..18535c07a50 100644 --- a/std/haxe/Int64.hx +++ b/std/haxe/Int64.hx @@ -40,6 +40,12 @@ abstract Int64(Int64Native) from Int64Native to Int64Native { private inline function new(x:Int64Native) this = x; + /** The greatest representable Int64 value: `2^63 - 1`. **/ + public static final MAX:Int64 = make(0x7FFFFFFF, 0xFFFFFFFF); + + /** The smallest representable Int64 value: `-2^63`. **/ + public static final MIN:Int64 = make(0x80000000, 0); + /** Makes a copy of `this` Int64. **/ @@ -60,8 +66,8 @@ abstract Int64(Int64Native) from Int64Native to Int64Native { return new Int64(Int64Native.ofInt(x)); /** - Returns an Int with the value of the Int64 `x`. - Throws an exception if `x` cannot be represented in 32 bits. + Returns an Int with the low 32 bits of the Int64 `x`. + The high 32 bits are discarded. **/ public static inline function toInt(x:Int64):Int return Int64Native.toInt(x); @@ -136,6 +142,14 @@ abstract Int64(Int64Native) from Int64Native to Int64Native { return Int64Native.fromFloat(f); } + /** + Converts this Int64 to a Float. + Values between -2^53 and 2^53 are exact; larger values may lose precision. + **/ + public inline function toFloat():Float { + return Int64Native.toFloat(this); + } + /** Performs signed integer division of `dividend` by `divisor`. Returns `{ quotient : Int64, modulus : Int64 }`. diff --git a/std/haxe/UInt64.hx b/std/haxe/UInt64.hx index 0916e37b2f8..b757cf6e736 100644 --- a/std/haxe/UInt64.hx +++ b/std/haxe/UInt64.hx @@ -39,6 +39,12 @@ abstract UInt64(Int64Native) from Int64Native to Int64Native { private inline function new(x:Int64Native) this = x; + /** The greatest representable UInt64 value: `2^64 - 1`. **/ + public static final MAX:UInt64 = make(0xFFFFFFFF, 0xFFFFFFFF); + + /** The smallest representable UInt64 value: `0`. **/ + public static final MIN:UInt64 = make(0, 0); + /** Makes a copy of `this` UInt64. **/ diff --git a/std/haxe/numeric/Int64Native.hx b/std/haxe/numeric/Int64Native.hx index 12d89bd5245..a0e2d7d164e 100644 --- a/std/haxe/numeric/Int64Native.hx +++ b/std/haxe/numeric/Int64Native.hx @@ -46,9 +46,6 @@ private class Int64NativeImpl { } public static inline function toInt(x:Int64Native):Int { - if (x.high != x.low >> 31) - throw "Overflow"; - return x.low; } @@ -314,6 +311,13 @@ private class Int64NativeImpl { return haxe.numeric.Int64Helper.fromFloat(f); } + public static inline function toFloat(x:Int64Native):Float { + var f:Float = x.low; + if (f < 0) + f += 4294967296.0; + return (x.high : Float) * 4294967296.0 + f; + } + @:ifFeature("dynamic_read.toString") public function toString():String { if (high == 0 && low == 0) diff --git a/std/hl/_std/haxe/numeric/Int64Native.hx b/std/hl/_std/haxe/numeric/Int64Native.hx index bd8985571b6..9e95d44a563 100644 --- a/std/hl/_std/haxe/numeric/Int64Native.hx +++ b/std/hl/_std/haxe/numeric/Int64Native.hx @@ -66,10 +66,7 @@ private abstract Int64NativeImpl(hl.I64) from hl.I64 to hl.I64 { } public static inline function toInt(x:Int64Native):Int { - var v:hl.I64 = x; - if (v < (cast -2147483648 : hl.I64) || v > (cast 2147483647 : hl.I64)) - throw "Overflow"; - return cast v; + return cast(x : hl.I64); } public static inline function isInt64(val:Dynamic):Bool @@ -148,6 +145,10 @@ private abstract Int64NativeImpl(hl.I64) from hl.I64 to hl.I64 { return haxe.numeric.Int64Helper.fromFloat(f); } + public static inline function toFloat(x:Int64Native):Float { + return cast(x : hl.I64); + } + public static function udivMod(dividend:Int64Native, divisor:Int64Native):{quotient:Int64Native, modulus:Int64Native} { return haxe.numeric.UInt64Helper.udivMod(dividend, divisor); } diff --git a/std/jvm/_std/haxe/numeric/Int64Native.hx b/std/jvm/_std/haxe/numeric/Int64Native.hx index ef022ee0181..73791420c63 100644 --- a/std/jvm/_std/haxe/numeric/Int64Native.hx +++ b/std/jvm/_std/haxe/numeric/Int64Native.hx @@ -68,11 +68,7 @@ private abstract Int64NativeImpl(jvm.Int64) from jvm.Int64 to jvm.Int64 { } public static inline function toInt(x:Int64Native):Int { - var v:jvm.Int64 = x; - if (v < ((cast -2147483648 : jvm.Int64)) - || v > ((cast 2147483647 : jvm.Int64))) - throw "Overflow"; - return cast v; + return cast(x : jvm.Int64); } public static inline function isInt64(val:Dynamic):Bool @@ -151,6 +147,10 @@ private abstract Int64NativeImpl(jvm.Int64) from jvm.Int64 to jvm.Int64 { return haxe.numeric.Int64Helper.fromFloat(f); } + public static inline function toFloat(x:Int64Native):Float { + return cast(x : jvm.Int64); + } + public static function udivMod(dividend:Int64Native, divisor:Int64Native):{quotient:Int64Native, modulus:Int64Native} { return haxe.numeric.UInt64Helper.udivMod(dividend, divisor); } diff --git a/tests/unit/src/unit/TestInt32.hx b/tests/unit/src/unit/TestInt32.hx index 78d0911bef6..5d56eed6ffe 100644 --- a/tests/unit/src/unit/TestInt32.hx +++ b/tests/unit/src/unit/TestInt32.hx @@ -4,12 +4,17 @@ import haxe.Int32; class TestInt32 extends Test { // --- Constants --- - static var MAX:Int32 = 0x7fffffff; - static var MIN:Int32 = 0x80000000; + static var MAX:Int32 = Int32.MAX; + static var MIN:Int32 = Int32.MIN; static var ZERO:Int32 = 0; static var ONE:Int32 = 1; static var NEG_ONE:Int32 = -1; + function testMinMax() { + eq((Int32.MAX : Int32), (0x7fffffff : Int32)); + eq((Int32.MIN : Int32), (0x80000000 : Int32)); + } + // --- Overflow behavior --- function testOverflowAdd() { eq((MAX + ONE : Int32), MIN); diff --git a/tests/unit/src/unit/TestInt64.hx b/tests/unit/src/unit/TestInt64.hx index 97c320927c9..fd2e68059e2 100644 --- a/tests/unit/src/unit/TestInt64.hx +++ b/tests/unit/src/unit/TestInt64.hx @@ -34,7 +34,8 @@ class TestInt64 extends Test { a = Int64.make(0,0x80000000); eq( a.high, 0 ); eq( a.low, 0x80000000 ); - exc( tryOverflow.bind(a) ); // Throws Overflow + // toInt truncates: discards high 32 bits, returns low + eq( a.toInt(), 0x80000000 ); a = Int64.make(0xFFFFFFFF,0x80000000); eq( a.high, 0xFFFFFFFF ); @@ -44,7 +45,8 @@ class TestInt64 extends Test { a = Int64.make(0xFFFFFFFF,0x7FFFFFFF); eq( a.high, 0xFFFFFFFF ); eq( a.low, 0x7FFFFFFF ); - exc( tryOverflow.bind(a) ); // Throws Overflow + // toInt truncates: discards high 32 bits, returns low + eq( a.toInt(), 0x7FFFFFFF ); } public function testNegateOverflow_Issue7485() @@ -89,10 +91,6 @@ class TestInt64 extends Test { eq(n.low, 0xefefefef); } - function tryOverflow(a:Int64) { - a.toInt(); - } - public function testIncrement() { var a:Int64, b:Int64, c:Int64; @@ -565,6 +563,51 @@ class TestInt64 extends Test { } } + public function testToFloat() { + // Zero + feq(Int64.make(0, 0).toFloat(), 0.0); + + // Positive values + feq(Int64.ofInt(1).toFloat(), 1.0); + feq(Int64.ofInt(100).toFloat(), 100.0); + + // Negative values + feq(Int64.ofInt(-1).toFloat(), -1.0); + feq(Int64.ofInt(-100).toFloat(), -100.0); + + // Boundary: MAX_SAFE_INTEGER (2^53 - 1) — exact + feq(Int64.parseString("9007199254740991").toFloat(), 9007199254740991.0); + feq(Int64.parseString("-9007199254740991").toFloat(), -9007199254740991.0); + + // Int32 boundaries + feq(Int64.ofInt(2147483647).toFloat(), 2147483647.0); + feq(Int64.ofInt(-2147483648).toFloat(), -2147483648.0); + + // Large positive: 2^32 = 4294967296 + feq(Int64.make(1, 0).toFloat(), 4294967296.0); + + // MAX and MIN: large values, may not be exact but roundtrip should be close + var maxFloat = Int64.MAX.toFloat(); + t(maxFloat > 9.22e18); + + var minFloat = Int64.MIN.toFloat(); + t(minFloat < -9.22e18); + } + + public function testMinMax() { + int64eq(Int64.MAX, Int64.make(0x7FFFFFFF, 0xFFFFFFFF)); + int64eq(Int64.MIN, Int64.make(0x80000000, 0)); + + // MAX + 1 wraps to MIN + int64eq(Int64.MAX + Int64.ofInt(1), Int64.MIN); + // MIN - 1 wraps to MAX + int64eq(Int64.MIN - Int64.ofInt(1), Int64.MAX); + + // String representations + eq(Std.string(Int64.MAX), "9223372036854775807"); + eq(Std.string(Int64.MIN), "-9223372036854775808"); + } + static function toHex(v:haxe.Int64) { return "0x" + (v.high == 0 ? StringTools.hex(v.low) : StringTools.hex(v.high) + StringTools.hex(v.low, 8)); } diff --git a/tests/unit/src/unit/TestUInt64.hx b/tests/unit/src/unit/TestUInt64.hx index 161806d2925..e3d485758f8 100644 --- a/tests/unit/src/unit/TestUInt64.hx +++ b/tests/unit/src/unit/TestUInt64.hx @@ -419,4 +419,17 @@ class TestUInt64 extends Test { function uint64eq(v:UInt64, v2:UInt64, ?pos:haxe.PosInfos) { t(v == v2, pos); } + + public function testMinMax() { + uint64eq(UInt64.MIN, UInt64.make(0, 0)); + uint64eq(UInt64.MAX, UInt64.make(0xFFFFFFFF, 0xFFFFFFFF)); + + // MAX + 1 wraps to 0 (MIN) + uint64eq(UInt64.MAX + UInt64.ofInt(1), UInt64.MIN); + // MIN - 1 wraps to MAX + uint64eq(UInt64.MIN - UInt64.ofInt(1), UInt64.MAX); + + eq(Std.string(UInt64.MIN), "0"); + eq(Std.string(UInt64.MAX), "18446744073709551615"); + } } diff --git a/tests/unit/src/unit/teststd/haxe/TestInt32.hx b/tests/unit/src/unit/teststd/haxe/TestInt32.hx index 0dc6711ce1f..e4d19df8645 100644 --- a/tests/unit/src/unit/teststd/haxe/TestInt32.hx +++ b/tests/unit/src/unit/teststd/haxe/TestInt32.hx @@ -6,6 +6,10 @@ class TestInt32 extends unit.Test { var max:haxe.Int32 = 0x7fffffff; var min:haxe.Int32 = 0x80000000; + // MIN/MAX constants match expected values + eq(haxe.Int32.MAX, max); + eq(haxe.Int32.MIN, min); + var a:haxe.Int32 = 0x7fffffff; eq(a++, max); eq(a, min); From e01f3ac1f91d7bb995a9a3a85cd585e3b1d5ef72 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 17 Mar 2026 17:20:36 +0100 Subject: [PATCH 3/3] it works for me like this --- std/cpp/cppia/HostClasses.hx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/std/cpp/cppia/HostClasses.hx b/std/cpp/cppia/HostClasses.hx index 070bc503b6f..30d56b5f2ac 100644 --- a/std/cpp/cppia/HostClasses.hx +++ b/std/cpp/cppia/HostClasses.hx @@ -173,10 +173,6 @@ class HostClasses { externs.set("Sys", true); externs.set("haxe.IMap", true); externs.set("haxe.crypto.HashMethod", true); - externs.set("haxe._Int64.Int64_Impl_", true); - externs.set("haxe._Int64.___Int64", true); - externs.set("haxe._Int32.Int32_Impl_", true); - externs.set("haxe._Int32.___Int32", true); // Hidden in implementation classes // externs.set("sys.db.RecordType",true); externs.set("sys.net._Socket.SocketInput", true); @@ -229,8 +225,6 @@ class HostClasses { Compiler.keep("haxe.crypto.HashMethod"); Compiler.keep("haxe._Int64.Int64_Impl_"); Compiler.keep("haxe._Int32.Int32_Impl_"); - Compiler.keep("haxe._Int64.___Int64"); - Compiler.keep("haxe._Int32.___Int32"); for (cls in classes) { Context.getModule(cls); Compiler.keep(cls);