3

When converting double (or float) to decimal, overflow exceptions are possible. So I've written this small extension method that prevents this by saturating:

public static decimal ToDecimalSafe(this double input)
{
    try
    {
        return (decimal)input;
    }
    catch (OverflowException)
    {
        if (input < 0)
            return decimal.MinValue;
        else
            return decimal.MaxValue;
    }
}

The issue is, this overflow happens pretty often in my use case, breaking the "exceptions should be exceptional" guideline. This slows down the application, yes, but that's not terribly important. The real issue is it also causes lots of first-chance exceptions during debugging, which is annoying. Here's attempt number two, which seems to be working fine:

public static decimal ToDecimalSafe(this double input)
{
    if (input < (double)decimal.MinValue)
        return decimal.MinValue;
    if (input > (double)decimal.MaxValue)
        return decimal.MaxValue;

    try
    {
        return (decimal)input;
    }
    catch (OverflowException)
    {
        if (input < 0)
            return decimal.MinValue;
        else
            return decimal.MaxValue;
    }
}

I left the try-catch to make sure I catch some possible edge cases. The question here is: are there any edge cases or can I just omit the try-catch?

Can a double be >= (double)decimal.MinValue and <= (double)decimal.MaxValue and still cause an overflow when converting?

relatively_random
  • 4,505
  • 1
  • 26
  • 48
  • If this is happening pretty often, why on earth are you using `decimal` rather than `double` in the first place? It sounds like you're using `decimal` for engineering or scientific calculations rather than financial ones - which is not the right thing to do. – Matthew Watson Nov 04 '16 at 09:27
  • @MatthewWatson It's nothing high-precision or anything. It's just for interacting between some hardware that deals with doubles and settings that are user-supplied and using decimal. The hardware provides limits, which often has ridiculously large range which the user doesn't care about anyway. – relatively_random Nov 04 '16 at 09:54
  • @MatthewWatson "Fairly often" doesn't mean 1000 times per second, it means 100 times total when grabbing those setting limits. – relatively_random Nov 04 '16 at 09:55
  • Still sounds like someone somewhere is using `decimal` for scientific or engineering data, which is wrong. It's particularly bad to use `decimal` for numbers that are being sent/retrieved from hardware which is certainly not going to understand the .Net `decimal` struct - but will probably be happy with the IEEE-standard `double` type. – Matthew Watson Nov 04 '16 at 10:07
  • @MatthewWatson It's being used for machine parameters, which don't need much precision and are usually fixed to a couple fractional positions anyway. In this case, decimal is convenient for its presentation properties. NumericUpDown controls are also convenient and they don't understand doubles. And of course the parameters are converted to double before being sent to the hardware. – relatively_random Nov 04 '16 at 10:31
  • "decimal is convenient for its presentation properties." - no, it's not. You're having problems because it doesn't support the range required for the problem domain, and hence you having to ask this question in the first place. But it's your code to do what you like with. :) – Matthew Watson Nov 04 '16 at 10:47

1 Answers1

2

The exception will not happen anymore. You can modify your code in this way.

public static decimal ToDecimalSafe(this double input)
{
    if (input < (double)decimal.MinValue)
        return decimal.MinValue;
    else if (input > (double)decimal.MaxValue)
        return decimal.MaxValue;
    else
        return (decimal)input;
}

You can also use the specific convert method but it does not prevent the exception

Convert.ToDecimal

If your problem is just the debugging break that is annoying then I suggest to take a look at [DebuggerStepThrough] or [DebuggerHidden] attributes

cristallo
  • 1,951
  • 2
  • 25
  • 42
  • I had no idea about those attributes, thanks. Very useful. BTW, I just spent a couple minutes actually trying to figure this out by myself and it seems like giving (double)decimal.MinValue or (double)decimal.MaxValue as input raises the exception. Making the comparisons inclusive (<= and >=) solves the problem, but I'll have to figure out how to find a value that is slightly within the bounds to see if that crashes. – relatively_random Nov 04 '16 at 10:39
  • you need to handle double.NaN values as well – user007 Jun 11 '20 at 11:26