0

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);
    }
}
1
  • I suppose values far from zero will be rounded to float.PositiveInfinity or float.NegativeInfinity. Commented Jan 7, 2023 at 8:33

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

5 Comments

the checked/unchecked contexts was what I was thinking of, and I was hoping, that if the 'global context' was checked that, that would throw an exception.
You could always write return checked((float)value); to make sure you had checked context just here, but it will not help because there is a way to represent too huge numbers, and that is called PositiveInfinity, and that will be used. For a conversion from int to short it would have been relevant (short does not support infinities).
I didn't/don't care about overflow if the context doesn't care, which is why I was hoping the way I has written this would only throw if the context is checked and the value was out of range, though if converting an out of range double to a float results in +/-∞, then that doesn't help and I would have to throw an exception anyways as JSON does not support infinities as numeric values.
What version of .NET? You should do this: var converted = (float)value; if (!float.IsFinite(converted)) { throw ... } return converted;
What I am using is .NET Standard 2.1 (I don't have very many options available for my project), but IsFinite is an available method, and so is IsInfinity.
0

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

3 Comments

Good. It is perhaps interesting that there exist some input values, like DoubleFloatImporter(3.40282355E+38), where your method would throw an exception, but a blind cast would actually round down to float.MaxValue, not up to float.PositiveInfinity. This is not necessarily something that will make you change your approach. It depend on what is desired for such values.
That could work, though I'd just throw on infinity anyways, seeing as if a JSON Document managed to say it contained an infinity there's already a problem, same with NaN.
Or return double.IsFinite(value) && (value > float.MaxValue || value < float.MinValue) throw new OverFlowException(...) : (float) value;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.