File size: 1,740 Bytes
dec5552 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // Rounding and number formatting exactly as in the model's training data (Python's round(x, n) and f"{x:.nf}"), so
// the scene text and the tool effects match it byte for byte. Python rounds the exact binary value of x, ties to even;
// .NET's Math.Round(x, n) scales by 10^n first, so 0.45 (binary 0.45000000000000001...) became 0.4 where the training
// scenes say 0.5, and -0.04 became 0.0 where they say -0.0.
using System;
using System.Globalization;
using System.Numerics;
namespace SceneAgent
{
public static class SceneMath
{
/// <summary>Python round(x, n) for n >= 0: exact, ties to even, sign kept (round(-0.04, 1) == -0.0).</summary>
public static double Round(double x, int n)
{
if (double.IsNaN(x) || double.IsInfinity(x)) return x;
long bits = BitConverter.DoubleToInt64Bits(x);
bool neg = bits < 0;
int e = (int)((bits >> 52) & 0x7FF);
long m = bits & 0xFFFFFFFFFFFFFL;
if (e == 0) e = 1; else m |= 1L << 52;
e -= 1075; // |x| = m * 2^e exactly
BigInteger num = new BigInteger(m) * BigInteger.Pow(10, n), den = BigInteger.One;
if (e > 0) num <<= e; else den <<= -e;
BigInteger q = BigInteger.DivRem(num, den, out BigInteger r);
int c = (r * 2).CompareTo(den);
if (c > 0 || (c == 0 && !q.IsEven)) q += 1;
double v = (double)q / Math.Pow(10, n); // q and 10^n are exact doubles: the division is correctly rounded
return neg ? -v : v;
}
/// <summary>Python f"{x:.{n}f}".</summary>
public static string Fixed(double x, int n) => Round(x, n).ToString("F" + n, CultureInfo.InvariantCulture);
}
}
|