Browse Source

fix: string %q format with number

Akeit0 6 months ago
parent
commit
912dd9a213

+ 77 - 0
src/Lua/Internal/HexConverter.cs

@@ -103,4 +103,81 @@ public static class HexConverter
 
 
         return 0;
         return 0;
     }
     }
+
+    public static string FromDouble(double value)
+    {
+        if (double.IsNaN(value))
+            return "(0/0)";
+        if (double.IsPositiveInfinity(value))
+            return "1e9999";
+        if (double.IsNegativeInfinity(value))
+            return "-1e9999";
+        if (value == 0.0)
+            return BitConverter.DoubleToInt64Bits(value) < 0 ? "-0x0p+0" : "0x0p+0";
+
+        // Convert double to IEEE 754 representation
+        long bits = BitConverter.DoubleToInt64Bits(value);
+
+        // sign bit 
+        bool isNegative = (bits & (1L << 63)) != 0;
+
+        // 11 bits of exponent
+        int exponent = (int)((bits >> 52) & ((1L << 11) - 1));
+
+        // 52 bits of mantissa
+        long mantissa = bits & ((1L << 52) - 1);
+
+        string sign = isNegative ? "-" : "";
+
+        if (exponent == 0)
+        {
+            int leadingZeros = CountLeadingZeros(mantissa, 52);
+            mantissa <<= (leadingZeros + 1);
+            mantissa &= ((1L << 52) - 1); // 52ビットにマスク
+
+            int adjustedExponent = -1022 - leadingZeros;
+
+            string mantissaHex = FormatMantissa(mantissa);
+            return $"{sign}0x0.{mantissaHex}p{adjustedExponent:+0;-0}";
+        }
+        else
+        {
+            int adjustedExponent = exponent - 1023;
+            string mantissaHex = FormatMantissa(mantissa);
+
+            if (mantissa == 0)
+                return $"{sign}0x1p{adjustedExponent:+0;-0}";
+            else
+                return $"{sign}0x1.{mantissaHex}p{adjustedExponent:+0;-0}";
+        }
+
+        static string FormatMantissa(long mantissa)
+        {
+            if (mantissa == 0)
+                return "";
+
+            string hex = mantissa.ToString("x13"); // 13桁の16進数
+
+            hex = hex.TrimEnd('0');
+
+            return hex;
+        }
+
+        static int CountLeadingZeros(long value, int bitLength)
+        {
+            if (value == 0)
+                return bitLength;
+
+            int count = 0;
+            long mask = 1L << (bitLength - 1);
+
+            while ((value & mask) == 0 && count < bitLength)
+            {
+                count++;
+                mask >>= 1;
+            }
+
+            return count;
+        }
+    }
 }
 }

+ 28 - 18
src/Lua/Standard/StringLibrary.cs

@@ -15,20 +15,20 @@ public sealed class StringLibrary
         var libraryName = "string";
         var libraryName = "string";
         Functions =
         Functions =
         [
         [
-            new(libraryName,"byte", Byte),
-            new(libraryName,"char", Char),
-            new(libraryName,"dump", Dump),
-            new(libraryName,"find", Find),
-            new(libraryName,"format", Format),
-            new(libraryName,"gmatch", GMatch),
-            new(libraryName,"gsub", GSub),
-            new(libraryName,"len", Len),
-            new(libraryName,"lower", Lower),
-            new (libraryName,"match", Match),
-            new(libraryName,"rep", Rep),
-            new(libraryName,"reverse", Reverse),
-            new(libraryName,"sub", Sub),
-            new(libraryName,"upper", Upper),
+            new(libraryName, "byte", Byte),
+            new(libraryName, "char", Char),
+            new(libraryName, "dump", Dump),
+            new(libraryName, "find", Find),
+            new(libraryName, "format", Format),
+            new(libraryName, "gmatch", GMatch),
+            new(libraryName, "gsub", GSub),
+            new(libraryName, "len", Len),
+            new(libraryName, "lower", Lower),
+            new(libraryName, "match", Match),
+            new(libraryName, "rep", Rep),
+            new(libraryName, "reverse", Reverse),
+            new(libraryName, "sub", Sub),
+            new(libraryName, "upper", Upper),
         ];
         ];
     }
     }
 
 
@@ -303,8 +303,18 @@ public sealed class StringLibrary
                                 formattedValue = $"\"{StringHelper.Escape(parameter.Read<string>())}\"";
                                 formattedValue = $"\"{StringHelper.Escape(parameter.Read<string>())}\"";
                                 break;
                                 break;
                             case LuaValueType.Number:
                             case LuaValueType.Number:
-                                // TODO: floating point numbers must be in hexadecimal notation
-                                formattedValue = parameter.Read<double>().ToString(CultureInfo.InvariantCulture);
+                                formattedValue = DoubleToQFormat(parameter.Read<double>());
+
+                                static string DoubleToQFormat(double value)
+                                {
+                                    if (MathEx.IsInteger(value))
+                                    {
+                                        return value.ToString(CultureInfo.InvariantCulture);
+                                    }
+
+                                    return HexConverter.FromDouble(value);
+                                }
+
                                 break;
                                 break;
                             default:
                             default:
 
 
@@ -515,7 +525,7 @@ public sealed class StringLibrary
                     stack.Push(match.Groups[k].Value);
                     stack.Push(match.Groups[k].Value);
                 }
                 }
 
 
-                await context.Access.RunAsync(func,match.Groups.Count,cancellationToken);
+                await context.Access.RunAsync(func, match.Groups.Count, cancellationToken);
 
 
                 result = context.Thread.Stack.Get(context.ReturnFrameBase);
                 result = context.Thread.Stack.Get(context.ReturnFrameBase);
             }
             }
@@ -561,7 +571,7 @@ public sealed class StringLibrary
         var s = context.GetArgument<string>(0);
         var s = context.GetArgument<string>(0);
         return new(context.Return(s.ToLower()));
         return new(context.Return(s.ToLower()));
     }
     }
-    
+
     public ValueTask<int> Match(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Match(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         //TODO : implement string.match
         //TODO : implement string.match

+ 12 - 0
tests/Lua.Tests/HexConverterTests.cs

@@ -15,4 +15,16 @@ public class HexConverterTests
     {
     {
         Assert.That(Math.Abs(HexConverter.ToDouble(text) - expected), Is.LessThanOrEqualTo(0.00001d));
         Assert.That(Math.Abs(HexConverter.ToDouble(text) - expected), Is.LessThanOrEqualTo(0.00001d));
     }
     }
+    
+    [TestCase(1.1, "0x1.199999999999ap+0")]
+    [TestCase(double.PositiveInfinity, "1e9999")]
+    [TestCase(double.NegativeInfinity, "-1e9999")]
+    [TestCase(double.NaN, "(0/0)")]
+    [TestCase(-1.5, "-0x1.8p+0")]
+    [TestCase(1.2e-39, "0x1.a22393b33036bp-130")]
+
+    public void Test_FromDouble(double value,string expected)
+    {
+        Assert.That(HexConverter.FromDouble(value), Is.EqualTo(expected.ToLower()));
+    }
 }
 }