From 05fd5738b44a950f37f88076a5c6567f375d2fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Sat, 17 Jan 2026 14:02:27 +0100 Subject: [PATCH] Fix unsigned int to floating point number conversion Simplest repro: Convert(Constant(uint.MaxValue), typeof(double)) Previously it would return -1, since the conv.r.un instruction wasn't used. It was only applied to uint->float, but not for double or any other integer type. Only uint/ulong was problematic, but the test covers all integer types. (nuint/nint are not supported by Linq.Expression conversions) --- .../FastExpressionCompiler.cs | 4 ++- .../UnaryExpressionTests.cs | 30 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 2ee422ec..4bb42329 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -3508,10 +3508,12 @@ private static bool TryEmitPrimitiveValueConvert(Type sourceType, Type targetTyp il.Demit(isChecked ? OpCodes.Conv_Ovf_I8 : OpCodes.Conv_I8); break; case TypeCode.Double: + if (sourceType.IsUnsigned()) + il.Demit(OpCodes.Conv_R_Un); il.Demit(OpCodes.Conv_R8); break; case TypeCode.Single: - if (sourceType == typeof(uint)) + if (sourceType.IsUnsigned()) il.Demit(OpCodes.Conv_R_Un); il.Demit(OpCodes.Conv_R4); break; diff --git a/test/FastExpressionCompiler.UnitTests/UnaryExpressionTests.cs b/test/FastExpressionCompiler.UnitTests/UnaryExpressionTests.cs index 484f3d02..ae277f66 100644 --- a/test/FastExpressionCompiler.UnitTests/UnaryExpressionTests.cs +++ b/test/FastExpressionCompiler.UnitTests/UnaryExpressionTests.cs @@ -28,6 +28,7 @@ public int Run() ArrayLength_compiles(); Convert_compiles(); ConvertChecked_compiles(); + ConvertToFloat(); Increment_Constant_compiles(); Decrement_compiles(); Increment_compiles(); @@ -52,7 +53,7 @@ public int Run() UnaryPlus_compiles(); Unbox_compiles(); - return 27; + return 28; } @@ -95,6 +96,33 @@ public void ConvertChecked_compiles() Asserts.AreEqual(1, result); } + public void ConvertToFloat() + { + var tests = new object[] { + sbyte.MinValue, sbyte.MaxValue, + short.MinValue, short.MaxValue, + int.MinValue, int.MaxValue, + long.MinValue, long.MaxValue, + uint.MaxValue, uint.MaxValue - 1, + ulong.MaxValue, ulong.MaxValue-10000, + ushort.MaxValue, byte.MaxValue, + char.MaxValue, 'a', + true, false, + double.MaxValue, float.MaxValue, (float)float.Epsilon, +#if NET7_0_OR_GREATER + Int128.MaxValue, Int128.MinValue, UInt128.MaxValue, // these use op_Explicit, but there isn't much coverage of that anyway +#endif + }; + foreach (var constant in tests) + { + var toFloat32 = Lambda>(Convert(Constant(constant), typeof(float))); + var toFloat64 = Lambda>(Convert(Constant(constant), typeof(double))); + + Asserts.AreEqual(toFloat32.CompileSys()(), toFloat32.CompileFast(true)(), $"(float){constant.ToCode()}"); + Asserts.AreEqual(toFloat64.CompileSys()(), toFloat64.CompileFast(true)(), $"(double){constant.ToCode()}"); + } + } + public void Increment_Constant_compiles() {