Is there a way to detect the overflow-checking context in code?

102 Views Asked by At

I'm working on an importer for LitJson, to import float values from ints, and doubles, and if overflow-checking is enabled, I want to wrap a potential overflow exception in a JsonException with a bit more information about the failure.

Right now my code looks like this, and I don't know if I need to/can check if the context is checked or not:

private static float DoubleFloatImporter(double value) {
    try
    {
        return (float)value;
    }
    catch (Exception ex)
    {
        throw new JsonException("Value is not a valid Single", ex);
    }
}
2

There are 2 best solutions below

5
Jeppe Stig Nielsen On

You may be thinking of checked and unchecked contexts, but these are not relevant for your example, the explicit conversion (cast) from double to float (so from double-precision binary floating point to single-precision).

A finite double value may round to an infinite float value (either float.PositiveInfinity or float.NegativeInfinity).

For example DoubleFloatImporter(1.23e123). As a double, the input 1.23e123 will be represented as a finite value, but when cast to float, the nearest representable value will be interpreted as +∞.

Edit: As I say in comments, something like:

private static float DoubleFloatImporter(double value) {
    var converted = (float)value;
    if (!float.IsFinite(converted))
        throw new JsonException("Converted value would become infinite or not a number");
    return converted;
}

may suit your need.

3
tmaj On

What about something like this:

static float DoubleFloatImporter(double value)
{
    if (double.IsPositiveInfinity(value))
    {
        return float.PositiveInfinity;
    }
    if (double.IsNegativeInfinity(value))
    {
        return float.NegativeInfinity;
    }
    if (value > Single.MaxValue || value < Single.MinValue)
    {
        throw new OverflowException($"'{value}' doesn't fit");
    }

    return (float)value; //Single.CreateChecked(value);
}

Some examples:

using System.Runtime.CompilerServices;

Convert(1.0);
Convert(double.MaxValue);
Convert(double.PositiveInfinity);
Convert(double.NegativeInfinity);
Convert((double)float.MaxValue + 100);
Convert((double)float.MaxValue * 2);
Convert(double.NaN);

static void Convert(double v, [CallerArgumentExpression("v")] string arg1Exp = "?")
{
    try
    {
        var f = DoubleFloatImporter(v);
        Console.WriteLine($"{arg1Exp}  ->  {f}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"{arg1Exp}  ->  {ex.Message}");
    }
}

The output:

1.0  ->  1
double.MaxValue  ->  '1.7976931348623157E+308' doesn't fit
double.PositiveInfinity  ->  ∞
double.NegativeInfinity  ->  -∞
(double)float.MaxValue + 100  ->  3.4028235E+38
(double)float.MaxValue * 2  ->  '6.805646932770577E+38' doesn't fit
double.NaN  ->  NaN