Skip to content

Commit 818062a

Browse files
authored
Improve decimals scale calculation on net8.0 (#807)
Improve number formatting based on Shopify's implementation
1 parent 3f8a614 commit 818062a

File tree

2 files changed

+73
-10
lines changed

2 files changed

+73
-10
lines changed

Fluid.Tests/ParserTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,51 @@ public void ShouldChangeVariableType(string source, string expected)
516516
Assert.Equal(expected, rendered);
517517
}
518518

519+
[Theory]
520+
[InlineData("{{ 0 | times: 1 }}", "0")]
521+
[InlineData("{{ 0.0 | times: 1 }}", "0.0")]
522+
[InlineData("{{ 0.1 | times: 1 }}", "0.1")]
523+
[InlineData("{{ 0.01 | times: 1 }}", "0.01")]
524+
[InlineData("{{ 0.0123 | times: 1 }}", "0.0123")]
525+
[InlineData("{{ 0 | times: 1.0 }}", "0.0")]
526+
[InlineData("{{ 1 | times: 1 }}", "1")]
527+
[InlineData("{{ 1 | times: 1.1 }}", "1.1")]
528+
[InlineData("{{ 1 | times: 1.123 }}", "1.123")]
529+
[InlineData("{{ 1 | times: 1.1234567890 }}", "1.123456789")]
530+
[InlineData("{{ 1 | times: 1.1000 }}", "1.1")]
531+
[InlineData("{{ 1.1000 | times: 1 }}", "1.1")]
532+
[InlineData("{{ 1 | times: 1. }}", "1")]
533+
public void ShouldPreservePrecision(string source, string expected)
534+
{
535+
var result = _parser.TryParse(source, out var template, out var errors);
536+
537+
Assert.True(result);
538+
Assert.NotNull(template);
539+
Assert.Null(errors);
540+
541+
var rendered = template.Render();
542+
543+
Assert.Equal(expected, rendered);
544+
}
545+
546+
[Theory]
547+
[InlineData("{{ 0. | times: 1 }}", "0")]
548+
[InlineData("{{ 0.0 | times: 1.0 }}", "0.0")]
549+
[InlineData("{{ 0.00 | times: 1 }}", "0.0")]
550+
[InlineData("{{ 0. | times: 1.0 }}", "0.0")]
551+
public void DotsAreParsedAsIntegralValues(string source, string expected)
552+
{
553+
var result = _parser.TryParse(source, out var template, out var errors);
554+
555+
Assert.True(result);
556+
Assert.NotNull(template);
557+
Assert.Null(errors);
558+
559+
var rendered = template.Render();
560+
561+
Assert.Equal(expected, rendered);
562+
}
563+
519564
[Theory]
520565
[InlineData("{% assign my_string = 'abcd' %}{{ my_string.size }}", "4")]
521566
public void SizeAppliedToStrings(string source, string expected)

Fluid/Values/NumberValue.cs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,26 @@ public override void WriteTo(TextWriter writer, TextEncoder encoder, CultureInfo
6868
public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo)
6969
{
7070
AssertWriteToParameters(writer, encoder, cultureInfo);
71-
var task = writer.WriteAsync(encoder.Encode(_value.ToString(cultureInfo)));
7271

72+
Task task = default;
73+
74+
var scale = GetScale(_value);
75+
if (scale == 0)
76+
{
77+
// If the scale is zero, we can write the value directly without formatting
78+
task = writer.WriteAsync(encoder.Encode(_value.ToString(cultureInfo)));
79+
}
80+
else if (_value * (10 * scale) % (10 * scale) == 0)
81+
{
82+
// If the decimal part is zero(s), write one only
83+
task = writer.WriteAsync(encoder.Encode(_value.ToString("F1", cultureInfo)));
84+
}
85+
else
86+
{
87+
// For larger scales, we use G29 to avoid trailing zeros
88+
task = writer.WriteAsync(encoder.Encode(_value.ToString("G29", cultureInfo)));
89+
}
90+
7391
if (task.IsCompletedSuccessfully())
7492
{
7593
return default;
@@ -149,16 +167,16 @@ internal static NumberValue Create(int value)
149167
return new NumberValue(value);
150168
}
151169

152-
public static int GetScale(decimal value)
170+
/// <summary>
171+
/// Gets the scale of a decimal value, which is the number of digits to the right of the decimal point.
172+
/// </summary>
173+
public static byte GetScale(decimal value)
153174
{
154-
if (value == 0)
155-
{
156-
return 0;
157-
}
158-
159-
var bits = decimal.GetBits(value);
160-
161-
return (int)((bits[3] >> 16) & 0x7F);
175+
#if NET8_0_OR_GREATER
176+
return value.Scale;
177+
#else
178+
return unchecked((byte)(decimal.GetBits(value)[3] >> 16));
179+
#endif
162180
}
163181
}
164182
}

0 commit comments

Comments
 (0)