Skip to content

Commit

Permalink
Moving some logic to created NumberItlHelper and documenting.
Browse files Browse the repository at this point in the history
  • Loading branch information
LuisMerinoP committed Nov 19, 2023
1 parent 04e9c99 commit 1da1922
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Jint.Tests/Runtime/NumberTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void ParseFloat(string input, double result)
Assert.Equal(result, value);
}

// Results from node -v v18.13.0.
// Results from node -v v18.18.0.
[Theory]
// Thousand separators.
[InlineData("1000000", "en-US", "1,000,000")]
Expand Down
42 changes: 42 additions & 0 deletions Jint/Native/Number/NumberIntlHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Ideally, internacionalization formats implemented through the ECMAScript standards would follow this:
// https://tc39.es/ecma402/#sec-initializedatetimeformat
// https://tc39.es/ecma402/#sec-canonicalizelocalelist
// Along with the implementations of whatever is subsequenlty called.

// As this is not in place (See TODOS in NumberFormatConstructor and DateTimeFormatConstructor) we can arrange
// values that will match the JS behavior using the host logic. This bypasses the ECMAScript standards but can
// do the job for the most common use cases and cultures meanwhile.

namespace Jint.Native.Number
{
internal class NumberIntlHelper
{
// Obtined empirically. For all cultures tested, we get a maximum of 3 decimal digits.
private const int JS_MAX_DECIMAL_DIGIT_COUNT = 3;

/// <summary>
/// Checks the powers of 10 of number to count the number of decimal digits.
/// Returns a clamped JS_MAX_DECIMAL_DIGIT_COUNT count.
/// JavaScript will use the shortest representation that accurately represents the value
/// and clamp the decimal digits to JS_MAX_DECIMAL_DIGIT_COUNT.
/// C# fills the digits with zeros up to the culture's numberFormat.NumberDecimalDigits
/// and does not provide the same max (numberFormat.NumberDecimalDigits != JS_MAX_DECIMAL_DIGIT_COUNT).
/// This function matches the JS behaviour for the decimal digits returned, this is the actual decimal
/// digits for a number (with no zeros fill) clamped to JS_MAX_DECIMAL_DIGIT_COUNT.
/// </summary>
public static int GetDecimalDigitCount(double number)
{
for (int i = 0; i < JS_MAX_DECIMAL_DIGIT_COUNT; i++)
{
var powOf10 = number * System.Math.Pow(10, i);
bool isInteger = powOf10 == ((int) powOf10);
if (isInteger)
{
return i;
}
}

return JS_MAX_DECIMAL_DIGIT_COUNT;
}
}
}
28 changes: 1 addition & 27 deletions Jint/Native/Number/NumberPrototype.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System;
using Jint.Collections;
using Jint.Native.Number.Dtoa;
using Jint.Native.Object;
Expand Down Expand Up @@ -47,31 +46,6 @@ protected override void Initialize()
SetProperties(properties);
}

/// <summary>
/// Checks the powers of 10 of <number> to count the number of decimal digits.
/// Returns a clamped <maxDecimalDigitCount>.
/// JavaScript will use the shortest representation that accurately represents the value.
/// c# fills the digits with zeros up to the culture's NumberDecimalDigits.
/// Here we get the decimal digit count digits to match JS behavior and avoid extra zeros.
/// </summary>

private int GetDecimalDigitCount(double number, int maxDecimalDigitCount)
{
var counter = 0;
for (int i = 0; i < maxDecimalDigitCount; i++)
{
var powOf10 = number * System.Math.Pow(10, i);
bool isInteger = powOf10 == ((int) powOf10);
if (isInteger)
{
break;
}
counter++;
}

return counter;
}

private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
{
if (!thisObject.IsNumber() && ReferenceEquals(thisObject.TryCast<NumberInstance>(), null))
Expand Down Expand Up @@ -113,7 +87,7 @@ private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
try
{
numberFormat = (NumberFormatInfo) CultureInfo.GetCultureInfo(cultureArg).NumberFormat.Clone();
int decDigitCount = GetDecimalDigitCount(m, numberFormat.NumberDecimalDigits);
int decDigitCount = NumberIntlHelper.GetDecimalDigitCount(m);
numberFormat.NumberDecimalDigits = decDigitCount;
}
catch (CultureNotFoundException)
Expand Down

0 comments on commit 1da1922

Please sign in to comment.