解决 0d 和 -.1d 的字符串表示形式的双精度问题,以便更清洁 UI

Solving the double precision issue for string representations of 0d and -.1d for cleaner UI

不是 Is floating point math broken? 的副本如果您花时间阅读问题,我正在寻找用字符串表示这些浮点数的更好方法。

我有一个数字 up/down 控件,本质上是一个普通的文本框,旁边有上下按钮,将其值存储为双精度值。我一直运行遇到上下循环的问题,当值为(或应该为)0或-.1时,文本框分别显示“1.38777878078145E-16”和“-0.0999999999999999”(其中其他),这会使控件感觉混乱。

我知道 double 的浮点精度意味着值会被截断,并且比双精度精度长的小数的绝对精度会丢失。 -.1d 的实际值的精确表示对我来说不如它的干净表示重要,这意味着我希望 -.1d (-0.0999999999999999) 简单地显示为“-.1”。

不要根据我的示例假设所有值都在一位小数以内,尽管精度并不是非常重要,粒度才是。

现在我只是在我的双字符串转换器中捕获字符串并处理它,但是有没有更好的方法来做到这一点?

不,由于它的开销,我不能使用小数。

[ValueConversion(typeof(Double), typeof(String))]
public class DoubleToStringValueConverter : IValueConverter
{
    public object Convert(object value,
                          Type targetType,
                          object parameter,
                          System.Globalization.CultureInfo culture)
    {
        string res = ((double)value).ToString(culture);
        if (res == "1.38777878078145E-16") res = "0";
        if (res == "-0.0999999999999999") res = "-0.1";
        return res;
    }

    public object ConvertBack(object value,
                              Type targetType,
                              object parameter,
                              System.Globalization.CultureInfo culture)
    {
        string val = value as string;
        double res = 0d;
        double.TryParse(val, System.Globalization.NumberStyles.Float, culture, out res);
        return res;
    }

}

对于任何其他寻找正确答案的人,而不仅仅是那些试图强迫您使用小数的人,我发现这对于 UI 精确双精度值的表示很有效。我通过四舍五入到 10 位牺牲了一些分辨率,但在我的应用程序中 10 位就可以了。至于转换为十进制然后返回双精度,这避免了使用双精度可能导致的复合错误。

[ValueConversion(typeof(Double), typeof(String))]
public class DoubleToStringValueConverter : IValueConverter
{
    public object Convert(object value,
                          Type targetType,
                          object parameter,
                          System.Globalization.CultureInfo culture)
    {
        decimal display = Math.Round(System.Convert.ToDecimal((double)value), 10) / 1.000000000000000000000000000000000m;
        value = System.Convert.ToDouble(display);
        string res = display.ToString(culture);

        return res;
    }

    public object ConvertBack(object value,
                              Type targetType,
                              object parameter,
                              System.Globalization.CultureInfo culture)
    {
        string val = value as string;
        double res = 0d;
        double.TryParse(val, System.Globalization.NumberStyles.Float, culture, out res);

        return res;
    }
}