Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NumberBox: Double values specified in XAML are corrupted and incorrect values are passed to code #9365

Open
NordsonDavid opened this issue Feb 26, 2024 · 3 comments
Labels
area-NumberBox NumberBox Control bug Something isn't working team-Markup Issue for the Markup team

Comments

@NordsonDavid
Copy link

NordsonDavid commented Feb 26, 2024

Describe the bug

When using double values in XAML, the values are corrupted and close, but incorrect values, are used instead.

Steps to reproduce the bug

Take the following code to specify a NumberBox with a DecimalFormatter containing a IncrementNumberRounder;

I have used a IncrementNumberRounder as the values supplied to the Increment property must be one of a very specific set of double values otherwise an ArgumentException will be thrown. Details here: https://learn.microsoft.com/en-us/uwp/api/windows.globalization.numberformatting.incrementnumberrounder.increment?view=winrt-22621
The current corruption of double values means that it is impossible to specify an IncrementNumberRounder.Increment value of "0.01" in XAML.

<NumberBox>
  <NumberBox.NumberFormatter>
    <numberFormatting:DecimalFormatter>
      <numberFormatting:DecimalFormatter.NumberRounder>
        <numberFormatting:IncrementNumberRounder Increment="0.01" />
      </numberFormatting:DecimalFormatter.NumberRounder>
    </numberFormatting:DecimalFormatter>
  </NumberBox.NumberFormatter>
</NumberBox>

The code above, will cause the following Exception to be thrown at runtime:

image

From the image you can see that the value "0.0099999997764825821" is used, not the value "0.01" as specified in the XAML.
This causes the assignment to the Increment property to correctly throw an InvalidArgument exception.

To prove that this is not caused by a rounding error, or a limitation with double precision and is an issue with the XAML specified value being corrupted; the following work-around can be used;

<NumberBox>
  <NumberBox.NumberFormatter>
    <numberFormatting:DecimalFormatter>
      <numberFormatting:DecimalFormatter.NumberRounder>
        <numberFormatting:IncrementNumberRounder Increment="{x:Bind Increment}" />
      </numberFormatting:DecimalFormatter.NumberRounder>
    </numberFormatting:DecimalFormatter>
  </NumberBox.NumberFormatter>
</NumberBox>

where in the code-behind, the property Increment is defined as:

public double Increment => 0.01;

This code moves the "0.01" double value out of the XAML and into the code-behind. This bypasses the issue and the correct "0.01" value is passed to the Increment property setter, which then works as expected.

In the debugger, it can then be seen that the correct "0.01" value is passed to the Increment property:

image

Expected behavior

Double values specified in XAML should be used as-is, not a different, but close, value.

Screenshots

No response

NuGet package version

WinUI 3 - Windows App SDK 1.4.5: 1.4.240411001

Windows version

Windows 11 (22H2): Build 22621

Additional context

No response

@NordsonDavid NordsonDavid added the bug Something isn't working label Feb 26, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added the needs-triage Issue needs to be triaged by the area owners label Feb 26, 2024
@DarranRowe
Copy link

DarranRowe commented Feb 26, 2024

One important thing to point out as an interesting aside.
Floating point numbers don't store exact values. This can be logically reasoned as how can you fit an infinite amount of numbers in a finite amount of bits. There is also the base 2 vs. base 10 representation going on. As an example, the value actually stored for 0.01 in a 32 bit IEEE 754 float is 0.00999999977648258209228515625. There tends to be a lot of rounding and other processing that takes place to make floating point numbers more usable. As you may notice, the value in the OP is a rounded version of this.
The value stored for double is closer but actually over 0.01. This makes me think that a double to float conversion is going on here.

--Edit--
As a nice little example of showing this at work, consider the following C++ application.

#include <iostream>
#include <iomanip>

int wmain()
{
	float f = 0.01f;
	double d = 0.01;

	std::cout << "Value stored as float: " << std::setprecision(27) << f << "\n";
	std::cout << "Value stored as double: " << std::setprecision(58) << d << "\n";

	double ftod = f;

	std::cout << "Value converted from float: " << std::setprecision(58) << ftod << "\n";

	double ftodm = round((f * 100.)) / 100.;

	std::cout << "Value rounded from float: " << std::setprecision(58) << ftodm << "\n";

	return 0;
}

This C++ application is compiled with strict floating point handling.

The output of this is as follows.

Value stored as float: 0.00999999977648258209228515625
Value stored as double: 0.01000000000000000020816681711721685132943093776702880859375
Value converted from float: 0.00999999977648258209228515625
Value rounded from float: 0.01000000000000000020816681711721685132943093776702880859375

These are showing the precise values stored in the variables. The two important things to note are the values for the floating point number and the value stored by directly assigning the float into a double. Once the double has lost its precision, you need to go out of your way to try to recover it. Also, depending in where the precision loss is, you may not even recover the real value.
Anyway, as stated, this shows all of the signs of a double being converted to a float somewhere. Or at the very least, Xaml stores it internally as float.

@JesseCol JesseCol changed the title Double values specified in XAML are corrupted and incorrect values are passed to code NumberBox: Double values specified in XAML are corrupted and incorrect values are passed to code Feb 27, 2024
@JesseCol JesseCol added team-Controls Issue for the Controls team area-NumberBox NumberBox Control and removed needs-triage Issue needs to be triaged by the area owners labels Feb 27, 2024
@ranjeshj ranjeshj added team-Markup Issue for the Markup team and removed team-Controls Issue for the Controls team labels Mar 26, 2024
@ranjeshj
Copy link
Contributor

This looks like it could be a xaml parser could be doing the conversion here.

@mcka-dev
Copy link

mcka-dev commented Nov 6, 2024

My temporary solution until there are no fixes

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:f="using:Windows.Globalization.NumberFormatting">

    <!-- Bug in NumberBox: Double values specified in XAML are corrupted and incorrect values are passed to code -->
    <!-- https://github.com/microsoft/microsoft-ui-xaml/issues/9365 -->
    
    <x:String x:Key="Whole">1</x:String>
    <x:String x:Key="Tenths">0.1</x:String>
    <x:String x:Key="Hundredths">0.01</x:String>
    
    <f:DecimalFormatter x:Key="IntegerRoundedDecimalFormatter" FractionDigits="0">
        <f:DecimalFormatter.NumberRounder>
            <f:IncrementNumberRounder Increment="{StaticResource Whole}" RoundingAlgorithm="RoundHalfUp" />
        </f:DecimalFormatter.NumberRounder>
    </f:DecimalFormatter>

    <f:DecimalFormatter x:Key="TenthsRoundedDecimalFormatter" FractionDigits="1">
        <f:DecimalFormatter.NumberRounder>
            <f:IncrementNumberRounder Increment="{StaticResource Tenths}" RoundingAlgorithm="RoundHalfUp" />
        </f:DecimalFormatter.NumberRounder>
    </f:DecimalFormatter>

    <f:DecimalFormatter x:Key="HundredthsRoundedDecimalFormatter" FractionDigits="2">
        <f:DecimalFormatter.NumberRounder>
            <f:IncrementNumberRounder Increment="{StaticResource Hundredths}" RoundingAlgorithm="RoundHalfUp" />
        </f:DecimalFormatter.NumberRounder>
    </f:DecimalFormatter>
</ResourceDictionary>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-NumberBox NumberBox Control bug Something isn't working team-Markup Issue for the Markup team
Projects
None yet
Development

No branches or pull requests

5 participants