将对象转换为长整型

3

我有如下函数:

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

我使用了double类型来允许14.4的转换!我有一个失败的测试:

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

        Assert.Equal(longValue, result);

    } 

我已经追踪代码,发现roundedValue是正数,但convertedValue是负数。那么问题出在哪里呢?


1
为什么要将解析类型设置为 double 以返回 long? - Mitch Wheat
2
也许参考TypeConverter .. 或 Convert.ToInt64(Object) .. - user166390
因为可能将13.4作为参数传递,如果我使用了缓冲区的长整型,则错过了这种可能性。 - mehdi.loa
long roundedValue = (long)Math.Round(myDoubleValue, 0); 将myDoubleValue四舍五入为整数,并将结果转换为长整型。 - Mitch Wheat
@MitchWheat 我不明白。我已经做了同样的事情。难道不是吗? - mehdi.loa
3个回答

4
问题在于你试图使用一个 double 来保存一个有19个有效数字(十进制)的整数值,而 double 只能表示 15-16个有效数字
因此,在 double 中无法精确表示该值。显然,当转换为 long 时,四舍五入导致该值溢出并变成负值。
您可以通过以下方式进行确认:
var convertedValue = checked((long)roundedValue);

如果您一定要处理这种情况,我建议使用 decimal 而不是 double,或者根据小数点(或您所在地区使用的任何符号)拆分字符串,并以此方式处理四舍五入。

1
检查您的originalValue的类型。它的编译时类型是object,但运行时的实际类型是什么?如果它是一个装箱数字类型,就没有必要调用.ToString()然后再调用TryParse。最好直接解箱到正确的类型(特别是如果它始终是相同的运行时类型),然后根据需要转换为另一种数值类型。
如果您发送一个具有许多位数的long,例如0x7FFFFFFFFFFFFFFF,它与long.MaxValue相同,通过double,请注意,longdouble具有大约10位更高的精度。从技术上讲,这是因为double使用11个指数位,但不必存储最高有效位。
long.MaxValue的值处,由于指数在通过2的幂时上升了一倍,double的精度发生了变化。long.MaxValue下方的double值具有1024的精度。这意味着只能表示1024的整数倍。毫不奇怪,long.MaxValue上方的double值具有2048的精度。当然,整数类型long的精度始终为1。

以下是最接近long.MaxValue的三个下方和三个上方的double

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

当你将 long.MaxValue,即 9223372036854775807 转换为 double 时,最接近的可表示数字是 9.223372036854775808E+18,比实际值大了 1.0。它的字符串表示形式使用 .ToString() 只会显示15个数字,而使用 .ToString("R") 则会显示17个数字(上面列表中的撇号')。将 double `9.223372036854775808E+18 转换回 long 时,会出现溢出异常,因为这个数字比实际值大了 1.0。当然,你仍然可以将该数转换为 ulong,以显示确切的值。

0

你应该首先检查提供的参数是否为long

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

    double buffer = defaultValue;

    ...
}

否则,您可能会在将long转换为double时丢失一些信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接