Home > Net >  Different Result from ToString - Method for small negative double (net Framework vs. net 6.0)
Different Result from ToString - Method for small negative double (net Framework vs. net 6.0)

Time:08-19

Given the following Program C#

using System;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine((-2.8E-15).ToString("P2"));
        }
    }
}
  1. Running with .net Framework 4.8 yields "0.00%"
  2. Running with .net 6.0 yields "-0.00%"

How can I ensure globally that .net 6.0 also results in:

"0.00%"

Without changing (-2.8E-15).ToString("P2") e.g. rounding here.

Our Problem is that a WPF Binding has a StringFormat displays "-0.00%" values too, given the following Binding "{Binding DoubleValue, StringFormat=P2}"

I do not like to introduce a Converter here or round the value in the ViewModel. If there is another way. A subclass of Binding might be an idea which came to my mind this morning. But i would prefer not do so...

CodePudding user response:

.NET Core introduced a lot of floating point parsing and formatting improvements in IEEE floating point compliance. One of them is IEEE 754-2008 formatting compliance.

That is why you are now seeing near negative results resulting in a negative 0. Since -0 and 0 are essentially the same, however, IEEE 754 standard says that positive zero and negative zero are two separate numbers, but this can potentially cause issues if you cannot handle it. This change was made back in .net core 3 and you can read more about it here: https://devblogs.microsoft.com/dotnet/floating-point-parsing-and-formatting-improvements-in-net-core-3-0/

For integers, there is no binary representation that makes a difference between 0 and -0, so they are by definition equal.

For IEEE floating-point numbers, there is a distinction of negative and positive zero. Someone made some tests (CLR of .NET Framework 2.0, C# 3) and it seems that they are considered equal, which is actually the behavior expected according to the IEEE 754 standard.

Here are test:

   double minusOne = -1.0;
    double positiveZero = 0.0;
    double negativeZero = minusOne*positiveZero;
    Console.WriteLine("{0} == {1} -> {2}", positiveZero, negativeZero, positiveZero == negativeZero);
    Console.WriteLine("Binary representation is equal: {0}", BitConverter.DoubleToInt64Bits(positiveZero) == BitConverter.DoubleToInt64Bits(negativeZero));

Results are:

0 == 0 -> True
Binary representation is equal: False

Sooooo long story short if you want to ensure you never return a "-0.00%" then you will need to do a check on it if you are going to continue using floating points. You could create an extension method to check this or something as simple as

void Main()
{
    var num = (-2.8E-15).ToString("P2");
    
    if(num == "-0.00%" || num == "-0.0%" || num == "-0%")
    {
        num = "0.00%";
    }
    
    Console.WriteLine(num);
    //result is: 0.00%
}

CodePudding user response:

In WPF XAML I use this Binding:

"{Binding DoubleValue, Converter={x:Static converters:DoubleToRoundedDoubleConverter.Instance},
                       ConverterParameter=4,
                       StringFormat=P2}"

With the following Converter:

public class DoubleToRoundedDoubleConverter : IValueConverter
{
    public static readonly IValueConverter Instance = new DoubleToRoundedDoubleConverter();

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double doubleValue
            && (parameter is int digits || (parameter is string paramStr && int.TryParse(paramStr, out digits)))
            && digits >= 0)
        {
            double roundedDouble = Math.Round(doubleValue, digits);
            double bound = 5.0 * Math.Pow(0.1, digits   1.0);
            return Math.Abs(roundedDouble) < bound ? 0.0 : roundedDouble;
        }

        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
  • Related