mehdi.loa
mehdi.loa

Reputation: 575

Converting object to long

I have the following function:

    public virtual long AsLong(object originalValue,long defaultValue)
    {
        double buffer = defaultValue;
        if (originalValue != null)
        {
            double result;
            var readValueIsconverted = double.TryParse(originalValue.ToString(), out result);
            if (readValueIsconverted)
                buffer = result;
        }

        var roundedValue = Math.Round(buffer, 0);
        var convertedValue = (long) roundedValue;
        return convertedValue;
    }

I have used double in order to allow conversion of 14.4! I have the following failing test:

    [Fact]
    public void CanConvertLongMaxValue()
    {
        var cellValue = new Converter();
        const long longValue = 0x7FFFFFFFFFFFFFFF;
        var result = cellValue.AsLong(longValue, 12);

        Assert.Equal(longValue, result);

    } 

I have traced the code and roundedValue is positive but the convertedValue is negative. so what is the problem?

Upvotes: 3

Views: 26359

Answers (3)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61952

Check the type of your originalValue. Its compile-time type is object, but what is the real type at run-time? If it's a boxed number type, there's no need to call .ToString() followed by TryParse. It is better to unbox directly to the correct type (especially if it is always the same run-time type) and then convert to another numerical type if necessary.

If you send a long with many digits, like 0x7FFFFFFFFFFFFFFF which is the same as long.MaxValue, through a double, note that a long has approximately 10 bits' higher precision than a double. Technically, that's because a double uses 11 bits for the exponent but doesn't have to store the most significant bit.

At the value of long.MaxValue, the precision of a double changes because the exponent goes up by one when we pass a power of two. double values just below long.MaxValue have a precision of 1024. This means that only whole multiples of 1024 can be represented. Not surprisingly, double values just above long.MaxValue have a precision of 2048. And of course the precision of the integer type long is always exactly 1.

Here are the three nearest double under and the three over long.MaxValue:

9.22337203685477'27'36 E+18
9.22337203685477'37'60 E+18
9.22337203685477'47'84 E+18
---
9.22337203685477'58'08 E+18  <-- two to the 63rd power
9.22337203685477'78'56 E+18
9.22337203685477'99'04 E+18

When you convert long.MaxValue, that is 9223372036854775807 to double, the nearest representable is 9.223372036854775808E+18 which is 1.0 too large. Its string representation with .ToString() will show only 15 digits, while .ToString("R") will show 17 digits (the apostrophes ' in my list above). When converting the double `9.223372036854775808E+18 back to long, we get an overflow exception because the number is 1.0 too big. You can still convert the number to ulong, of course, which should reveal the exact value.

Upvotes: 1

Thorarin
Thorarin

Reputation: 48486

The problem is that you're trying to hold an integer value with 19 significant digits (in decimal) in a double which has 15-16 significant digits.

Hence, it's not possible to represent the value exactly in the double. Apparently rounding causes the value to overflow when converted into a long, making it a negative value.

You can confirm this like so:

var convertedValue = checked((long)roundedValue);

If you absolutely must deal with this case, I would suggest using decimal instead of double, or splitting the string on the decimal point (or whatever is used in your locale) and dealing with rounding that way.

Upvotes: 4

MarcinJuraszek
MarcinJuraszek

Reputation: 125630

You should check, if provided parameter is long at the first place:

public virtual long AsLong(object originalValue,long defaultValue)
{
    if(originalValue.GetType() == typeof(long))
        return (long) originalValue;

    double buffer = defaultValue;

    ...
}

Otherwise you can loose some information on long to double conversion.

Upvotes: 0

Related Questions