Skip to content

Commit 4f11b39

Browse files
committed
Java: cleanup code
C#: Port ShortenMantissa function and unit tests form Java.
1 parent dcd4c79 commit 4f11b39

File tree

5 files changed

+394
-219
lines changed

5 files changed

+394
-219
lines changed

csharp/EPAM.Deltix.DFP.Test/Decimal64Test.cs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,151 @@ public void Issue91ToFloatString()
12691269
}
12701270
}
12711271

1272+
[Test]
1273+
public void TestShortenMantissaBigDelta()
1274+
{
1275+
Assert.AreEqual(Decimal64.Parse("9000000000000000"),
1276+
Decimal64.Parse("9999000000000000").ShortenMantissa(DotNetImpl.MaxCoefficient / 2, 2));
1277+
}
1278+
1279+
[Test]
1280+
public void TestShortenMantissaCase006()
1281+
{
1282+
String testString = "0.006";
1283+
var testValue = Double.Parse(testString);
1284+
var testX = 0.005999999999998265; // Math.nextDown(testValue);
1285+
1286+
var d64 = Decimal64.FromDouble(testX).ShortenMantissa(1735, 1);
1287+
Assert.AreEqual(testString, d64.ToString());
1288+
1289+
Decimal64.FromDouble(9.060176071990028E-7).ShortenMantissa(2, 1);
1290+
}
1291+
1292+
[Test]
1293+
public void TestShortenMantissaRandom()
1294+
{
1295+
var randomSeed = new Random().Next();
1296+
var random = new Random(randomSeed);
1297+
1298+
try
1299+
{
1300+
for (int iteration = 0; iteration < N; ++iteration)
1301+
{
1302+
var mantissa = GenerateMantissa(random, Decimal64.MaxSignificandDigits);
1303+
int error = random.Next(3) - 1;
1304+
mantissa = Math.Min(DotNetImpl.MaxCoefficient, Math.Max(0, (ulong)((long)mantissa + error)));
1305+
if (mantissa <= DotNetImpl.MaxCoefficient / 10)
1306+
mantissa = mantissa * 10;
1307+
1308+
var delta = GenerateMantissa(random, 0);
1309+
if (delta > DotNetImpl.MaxCoefficient / 10)
1310+
delta = delta / 10;
1311+
1312+
CheckShortenMantissaCase(mantissa, delta);
1313+
}
1314+
}
1315+
catch (Exception e)
1316+
{
1317+
throw new Exception("Random seed " + randomSeed + " exception: " + e.Message, e);
1318+
}
1319+
}
1320+
1321+
[Test]
1322+
public void TestShortenMantissaCase()
1323+
{
1324+
CheckShortenMantissaCase(9999888877776001UL, 1000);
1325+
CheckShortenMantissaCase(1230000000000000UL, 80);
1326+
CheckShortenMantissaCase(1230000000000075UL, 80);
1327+
CheckShortenMantissaCase(1229999999999925UL, 80);
1328+
CheckShortenMantissaCase(4409286553495543UL, 900);
1329+
CheckShortenMantissaCase(4409286553495000UL, 1000);
1330+
CheckShortenMantissaCase(4409286550000000UL, 81117294);
1331+
CheckShortenMantissaCase(9010100000000001UL, 999999999999999L);
1332+
CheckShortenMantissaCase(8960196546869015UL, 1);
1333+
CheckShortenMantissaCase(4700900091799999UL, 947076117508L);
1334+
CheckShortenMantissaCase(5876471737721999UL, 91086);
1335+
CheckShortenMantissaCase(6336494570000000UL, 6092212816L);
1336+
CheckShortenMantissaCase(8960196546869011UL, 999999999999999L);
1337+
CheckShortenMantissaCase(1519453608576584UL, 3207L);
1338+
}
1339+
1340+
private static void CheckShortenMantissaCase(ulong mantissa, ulong delta)
1341+
{
1342+
try
1343+
{
1344+
var bestSolution = ShortenMantissaDirect(mantissa, delta);
1345+
1346+
var test64 = Decimal64.FromULong(mantissa).ShortenMantissa(delta, 0).ToULong();
1347+
1348+
if (test64 != bestSolution)
1349+
throw new Exception("The mantissa(=" + mantissa + ") and delta(=" + delta + ") produce test64(=" + test64 + ") != bestSolution(=" + bestSolution + ").");
1350+
}
1351+
catch (Exception e)
1352+
{
1353+
throw new Exception("The mantissa(=" + mantissa + ") and delta(=" + delta + ") produce exception.", e);
1354+
}
1355+
}
1356+
1357+
private static ulong ShortenMantissaDirect(ulong mantissaIn, ulong deltaIn)
1358+
{
1359+
var mantissa = (long)mantissaIn;
1360+
var delta = (long)deltaIn;
1361+
var rgUp = mantissa + delta;
1362+
var rgDown = mantissa - delta;
1363+
1364+
if (mantissaIn <= DotNetImpl.MaxCoefficient / 10 || mantissaIn > DotNetImpl.MaxCoefficient)
1365+
throw new ArgumentException("The mantissa(=" + mantissa + ") must be in (" + DotNetImpl.MaxCoefficient / 10 + ".." + DotNetImpl.MaxCoefficient + "] range");
1366+
1367+
long bestSolution = long.MinValue;
1368+
if (rgDown > 0)
1369+
{
1370+
var mUp = (mantissa / 10) * 10;
1371+
uint mFactor = 1;
1372+
1373+
long bestDifference = long.MaxValue;
1374+
int bestPosition = -1;
1375+
1376+
for (int replacePosition = 0;
1377+
replacePosition < Decimal64.MaxSignificandDigits + 1;
1378+
++replacePosition, mUp = (mUp / 100) * 10, mFactor *= 10)
1379+
{
1380+
for (uint d = 0; d < 10; ++d)
1381+
{
1382+
var mTest = (mUp + d) * mFactor;
1383+
if (rgDown <= mTest && mTest <= rgUp)
1384+
{
1385+
var md = Math.Abs(mantissa - mTest);
1386+
if (bestPosition < replacePosition ||
1387+
(bestPosition == replacePosition && bestDifference >= md))
1388+
{
1389+
bestPosition = replacePosition;
1390+
bestDifference = md;
1391+
bestSolution = mTest;
1392+
}
1393+
}
1394+
}
1395+
}
1396+
}
1397+
else
1398+
{
1399+
bestSolution = 0;
1400+
}
1401+
1402+
return (ulong)bestSolution;
1403+
}
1404+
1405+
private static ulong GenerateMantissa(Random random, int minimalLength)
1406+
{
1407+
int mLen = (1 + random.Next(Decimal64.MaxSignificandDigits) /*[1..16]*/);
1408+
ulong m = 1 + (ulong)random.Next(9);
1409+
int i = 1;
1410+
for (; i < mLen; ++i)
1411+
m = m * 10 + (ulong)random.Next(10);
1412+
for (; i < minimalLength; ++i)
1413+
m = m * 10;
1414+
return m;
1415+
}
1416+
12721417
readonly int N = 5000000;
12731418

12741419
static void Main()

csharp/EPAM.Deltix.DFP/Decimal64.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,35 @@ public Decimal64 RoundToNearestTiesToEven(Decimal64 multiple)
809809
return new Decimal64(NativeImpl.multiply2(ratio, multiple.Bits));
810810
}
811811

812+
/// <summary>
813+
/// Returns a <code>DFP</code> number in some neighborhood of the input value with a maximally
814+
/// reduced number of digits.
815+
/// Explanation:
816+
/// Any finite <code>DFP</code> value can be represented as 16-digits integer number (mantissa)
817+
/// multiplied by some power of ten (exponent):
818+
/// 12.3456 = 1234_5600_0000_0000 * 10^-14
819+
/// 720491.5510000001 = 7204_9155_1000_0001 * 10^-10
820+
/// 0.009889899999999999 = 9889_8999_9999_9999 * 10^-18
821+
/// 9.060176071990028E-7 = 9060_1760_7199_0028 * 10^-22
822+
/// This function modify only the mantissa and leave the exponent unchanged.
823+
/// This function attempts to find the number with the maximum count of trailing zeros
824+
/// within the neighborhood range [mantissa-delta ... mantissa+delta].
825+
/// If the number of trailing zeros is less than minZerosCount, the original value is returned.
826+
/// For the examples above the
827+
/// Decimal64.FromDouble(12.3456).ShortenMantissa(4, 1) => 12.3456
828+
/// Decimal64.FromDouble(720491.5510000001).ShortenMantissa(4, 1) => 720491.551
829+
/// Decimal64.FromDouble(0.009889899999999999).ShortenMantissa(4, 1) => 0.0098899
830+
/// Decimal64.FromDouble(9.060176071990028E-7).ShortenMantissa(4, 1) => 0.000000906017607199003
831+
/// </summary>
832+
/// <param name="value"></param>
833+
/// <param name="delta"></param>
834+
/// <param name="minZerosCount"></param>
835+
/// <returns></returns>
836+
public Decimal64 ShortenMantissa(UInt64 delta, uint minZerosCount)
837+
{
838+
return new Decimal64(DotNetImpl.ShortenMantissa(Bits, delta, minZerosCount));
839+
}
840+
812841
public Decimal64 Round(int n, RoundingMode roundType)
813842
{
814843
return new Decimal64(DotNetImpl.Round(Bits, n, roundType));

0 commit comments

Comments
 (0)