[CmdPal]Limit exprtk result to 15 total digits with proper rounding (#40194)
Some checks failed
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Report (Push) (push) Has been cancelled
Spell checking / Report (PR) (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR addresses an issue where direct double output from exprtk
evaluation led to precision artifacts (e.g., 12.2 + 10 resulting in
22.199999999999999). This behavior is due to how double represents
decimal fractions in binary.

- To improve user-facing precision, the result is now:
- Converted to decimal for higher decimal accuracy.
- Rounded to fit within 15 total significant digits (integer +
fraction).
- Formatted using "G29" to eliminate unnecessary trailing zeros.
- Cleaned up to remove dangling decimal points.

This ensures more intuitive and readable output like:

- 1.9999999999 → "1.9999999999"
- 100000.9999999999 → "100001"
- 100000.999999999 → "100000.999999999"

This change improves clarity, especially for users entering expressions
expecting decimal-accurate results.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #40097 
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
leileizhang
2025-06-24 13:42:22 +08:00
committed by GitHub
parent 0c870d68c6
commit 20dda12dbc

View File

@@ -84,11 +84,7 @@ public static class CalculateEngine
var decimalResult = Convert.ToDecimal(result, cultureInfo);
// Remove trailing zeros from the decimal string representation (e.g., "1.2300" -> "1.23")
// This is necessary because the value extracted from exprtk may contain unnecessary trailing zeros.
var formatted = decimalResult.ToString("G29", cultureInfo);
decimalResult = Convert.ToDecimal(formatted, cultureInfo);
var roundedResult = Round(decimalResult);
var roundedResult = FormatMax15Digits(decimalResult, cultureInfo);
return new CalculateResult()
{
@@ -101,4 +97,28 @@ public static class CalculateEngine
{
return Math.Round(value, RoundingDigits, MidpointRounding.AwayFromZero);
}
/// <summary>
/// Format a decimal so that the output contains **at most 15 total digits**
/// (integer + fraction, not counting the decimal point or minus sign).
/// Any extra fractional digits are rounded using “away-from-zero” rounding.
/// Trailing zeros in the fractional part—and a dangling decimal point—are removed.
/// Examples
/// 1.9999999999 → "1.9999999999"
/// 100000.9999999999 → "100001"
/// 1234567890123.45 → "1234567890123.45"
/// </summary>
private static decimal FormatMax15Digits(decimal value, CultureInfo cultureInfo)
{
var absValue = Math.Abs(value);
var integerDigits = absValue >= 1 ? (int)Math.Floor(Math.Log10((double)absValue)) + 1 : 1;
var maxDecimalDigits = Math.Max(0, 15 - integerDigits);
var rounded = Math.Round(value, maxDecimalDigits, MidpointRounding.AwayFromZero);
var formatted = rounded.ToString("G29", cultureInfo);
return Convert.ToDecimal(formatted, cultureInfo);
}
}