-2

Not a duplicate of Is floating point math broken? If you take the time to read the question, I am looking for better ways of representing these floating point number in strings.

I have a numeric up/down control, essentially a normal textbox with up down buttons next to it that stores its value as a double. I have been running into an issue when cycling up and down, when the value is (or should be) 0 or -.1, the text box displays "1.38777878078145E-16" and "-0.0999999999999999" respectively (among others) and this makes the control feel messy.

I get that the floating point precision of double means that values get truncated and absolute precision for decimals longer than the doubles precision are lost. Precise representation of the actual value of -.1d is not as important to me as it's clean representation, meaning I would like -.1d (-0.0999999999999999) to simply display as "-.1".

Do not assume by my examples that all values are within one decimal, though precision is not terribly important granularity is.

Right now I am just catching the string in my double to string converter and handling it, but is there a better way to do this?

No I cannot use decimal due to its overhead.

[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;
    }

}
Community
  • 1
  • 1
Wobbles
  • 3,033
  • 1
  • 25
  • 51
  • 2
    0.1 in decimal is 0.00011001100110011... (recurring) in binary. Also, `if (res == "-0.0999999999999999") res = "-1"` - did you mean "-0.1"? – Andrew Morton Mar 31 '16 at 15:06
  • 1
    You mention ".1 - .1", but you didn't show any code relating to that. – Andrew Morton Mar 31 '16 at 15:09
  • ahh, didn't even think about its binary representation. And yes, good catch! – Wobbles Mar 31 '16 at 15:11
  • its a numeric up down control, the "code" resulting in a .1 -.1 operation is pseudo and assumed. – Wobbles Mar 31 '16 at 15:12
  • 1
    Could you switch from using `double` to using `decimal` instead? – juharr Mar 31 '16 at 15:14
  • 1
    last line of my question: `No I cannot use decimal due to its overhead.` – Wobbles Mar 31 '16 at 15:15
  • 2
    Exactly what overhead are you talking about? Heck if you are dealing with discrete values (to only one decimal place) you could potentially use `int` and just format it to have a decimal in the UI. Really depends on what your range of values are. – juharr Mar 31 '16 at 15:16
  • Its being used in hardware so there are thousands of numbers updating thousands of times a second. The overhead of the decimal type is ridiculous just to solve a floating point formatting issue. – Wobbles Mar 31 '16 at 15:20
  • Can you restrict the format of the textbox you are using to display the number? For example should there always be only 1 decimal place? Is there a range of anticipated values? – newb Mar 31 '16 at 15:28
  • .1 is just my default interval value, the box could potentially (and needs to) handle decimals in length equal to the maximum allowed by the double type. Though my application needs speed over precision, a respectable amount of granularity is needed. – Wobbles Mar 31 '16 at 15:32
  • 1
    @PeterDuniho I'm not doing that operation thousands of times though. The control is just to set the property value. I'm not switching to decimal, there is simply way too much overhead for that to be feasible, that's why we chose double, and that's why I implicitly said using decimal is not an option.... You seem to be completely overlooking the question and arguing semantics. – Wobbles Mar 31 '16 at 18:50
  • @PeterDuniho You still haven't even bothered to actually read the question. – Wobbles Apr 01 '16 at 11:29
  • 1
    The question says this problem occurs when cycling the value up or down in something akin to a textbox control with up/down buttons yet you cite overhead problems with thousands of times a second. There is a mismatch here since no user is able to click that fast. Any overhead you perceive here is negligible. Either way you can handle this problem by a simple epsilon check: `if (Math.Abs(value) < 1e-5) value = 0;`. – Lasse V. Karlsen Apr 01 '16 at 11:50
  • @LasseV.Karlsen I cite that using a decimal type is not possible because the property is used thousands of types a second, but I also state that my concern is with a user control and am concerned with the text representation of the used property, which has nothing to do with its usage in the application, further supported by the fact that the code I supplied is a `IValueConverter`. Just because the operation is run thousands of times a second does not mean that the user set props are modified thousands of times a second. There are reasons why I use double and I wish people would focus on the ? – Wobbles Apr 01 '16 at 12:02
  • @PeterDuniho What other code could you possibly need to see, everybody is aware of how doubles act with certain variables due to its floating point and limited bit storage, what code could you need to see to further support that fact that -.1 as a double does not truly equal -.1 and as a string is "ugly". It's all semantics though as I have already found a workable solution as I posted below. Just wish people here would have helped me get there without losing sight of the question and trying to argue changes that I explicitly say are not possible/feasible. – Wobbles Apr 01 '16 at 15:42
  • @PeterDuniho `Solving the double precision issue for string representations of 0d and -.1d` `Right now I am just catching the string in my double to string converter and handling it, but is there a better way to do this?` `No I cannot use decimal due to its overhead.` ` I am looking for better ways of representing these floating point number in strings.` – Wobbles Apr 01 '16 at 15:58

1 Answers1

0

For anybody else looking for a proper answer and not just people trying to force you to use decimal, I found this to work well for UI representations of in-precise double values. I sacrifice some resolution by rounding to 10 places, but in my application 10 places is just fine. As for converting to decimal then back to double, this avoids compounding errors that working with double can cause.

[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;
    }
}
Wobbles
  • 3,033
  • 1
  • 25
  • 51
  • 1
    Dividing by `1.000000000000000000000000000000000m` is an old trick to remove trailing zeroes. And the going back and forth between types is how compounding errors are eliminated. Dont call it awful just because you dont understand it. Try it, it works. – Wobbles Apr 01 '16 at 15:55
  • Have you tried it? I didn't put the code in there "just because". The resulting string without that division is "1.0000000000000000", with it it is just "1". http://stackoverflow.com/a/7983330/3797778 – Wobbles Apr 01 '16 at 16:02
  • It is a full and complete `IValueConverter`. If you dont know what that is I will update to show its usage, but its pretty basic. – Wobbles Apr 01 '16 at 16:08
  • @PeterDuniho Code to call it? IValueConverters are called when bound properties send a before propertychanged event. – Wobbles Apr 01 '16 at 16:15
  • @PeterDuniho there you have it, the code in its entirety, but there is nothing there of any help and just a waste of space. – Wobbles Apr 01 '16 at 16:17
  • @PeterDuniho actually you are since you think a bunch of unrelated code is how you achieve Minimal. None of the rest of the code is remotely needed to know any of how doubles react when converted to a string. – Wobbles Apr 01 '16 at 16:21