diff --git a/sjsonnet/src/sjsonnet/DecimalFormat.scala b/sjsonnet/src/sjsonnet/DecimalFormat.scala index 4390c676..d8b498b2 100644 --- a/sjsonnet/src/sjsonnet/DecimalFormat.scala +++ b/sjsonnet/src/sjsonnet/DecimalFormat.scala @@ -64,8 +64,8 @@ object DecimalFormat { case None => val precision = zeroes + hashes if (precision == 0) { - // No fractional digits needed - just round and format the integer part - val rounded = math.rint(number) + // Round half away from zero (matching go-jsonnet/jrsonnet behavior) + val rounded = if (number >= 0) math.floor(number + 0.5) else math.ceil(number - 0.5) val prefix = if (rounded.isInfinite || math.abs(rounded) > Long.MaxValue) BigDecimal(rounded).toBigInt.toString @@ -73,13 +73,14 @@ object DecimalFormat { if (alternate) prefix + "." else prefix } else { val denominator = BigDecimal(10).pow(precision) - val bd = BigDecimal(number) + val bd = BigDecimal(number).abs val scaled = (bd * denominator + BigDecimal("0.5")).setScale(0, BigDecimal.RoundingMode.FLOOR) val wholeBD = (scaled / denominator).setScale(0, BigDecimal.RoundingMode.FLOOR) val fracBD = (scaled - wholeBD * denominator).abs - val prefix = wholeBD.toBigInt.toString + val sign = if (number < 0) "-" else "" + val prefix = sign + wholeBD.toBigInt.toString val fracStr = fracBD.toBigInt.toString val frac = diff --git a/sjsonnet/test/resources/new_test_suite/format_rounding_half_away_from_zero.jsonnet b/sjsonnet/test/resources/new_test_suite/format_rounding_half_away_from_zero.jsonnet new file mode 100644 index 00000000..01b01c83 --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/format_rounding_half_away_from_zero.jsonnet @@ -0,0 +1,23 @@ +// Verify std.format uses round-half-away-from-zero (matching go-jsonnet/jrsonnet). +// Previously used HALF_EVEN (banker's rounding) which gave wrong results for .5 values. + +std.assertEqual(std.format("%.0f", [0.5]), "1") && +std.assertEqual(std.format("%.0f", [1.5]), "2") && +std.assertEqual(std.format("%.0f", [2.5]), "3") && +std.assertEqual(std.format("%.0f", [3.5]), "4") && +std.assertEqual(std.format("%.0f", [4.5]), "5") && +std.assertEqual(std.format("%.0f", [-0.5]), "-1") && +std.assertEqual(std.format("%.0f", [-1.5]), "-2") && +std.assertEqual(std.format("%.0f", [-2.5]), "-3") && +// Higher precision +std.assertEqual(std.format("%.1f", [0.25]), "0.3") && +std.assertEqual(std.format("%.1f", [0.35]), "0.4") && +std.assertEqual(std.format("%.1f", [-0.25]), "-0.3") && +std.assertEqual(std.format("%.2f", [0.005]), "0.01") && +std.assertEqual(std.format("%.2f", [0.015]), "0.02") && +std.assertEqual(std.format("%.2f", [-0.005]), "-0.01") && +// Carry case: rounding causes integer part to increment +std.assertEqual(std.format("%.2f", [9.999]), "10.00") && +// Negative rounding to zero +std.assertEqual(std.format("%.2f", [-0.001]), "-0.00") && +true diff --git a/sjsonnet/test/resources/new_test_suite/format_rounding_half_away_from_zero.jsonnet.golden b/sjsonnet/test/resources/new_test_suite/format_rounding_half_away_from_zero.jsonnet.golden new file mode 100644 index 00000000..27ba77dd --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/format_rounding_half_away_from_zero.jsonnet.golden @@ -0,0 +1 @@ +true