正在将对象转换为长

本文关键字:转换 对象 | 更新日期: 2023-09-27 18:00:35

我有以下功能:

    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为负。那么问题出在哪里呢?

正在将对象转换为长

问题是您试图在具有15-16个有效数字的double中保存一个具有19个有效数字(十进制)的整数值。

因此,不可能在double中准确地表示该值。显然,四舍五入会导致值在转换为长值时溢出,从而使其成为负值。

你可以这样确认:

var convertedValue = checked((long)roundedValue);

如果您绝对必须处理这种情况,我建议使用decimal而不是双精度,或者将字符串拆分到小数点(或您所在区域中使用的任何字符串),并以这种方式处理舍入。

检查originalValue的类型。它的编译时类型是object,但在运行时真正的类型是什么?如果是盒装号码类型,则不需要调用.ToString()然后再调用TryParse。最好直接取消装箱为正确的类型(尤其是如果它总是相同的运行时类型),然后在必要时转换为另一个数字类型。

如果您通过double发送具有多个数字的long,如与long.MaxValue相同的0x7FFFFFFFFFFFFFFF,请注意,long的精度比double高大约10位。从技术上讲,这是因为double使用11位作为指数,但不必存储最高有效位。

在值为long.MaxValue时,double的精度会发生变化,因为当我们通过2的幂时,指数会上升1。刚好低于CCD_ 18的CCD_ 17值具有1024的精度。这意味着只能表示1024的整数倍。毫不奇怪,仅高于long.MaxValuedouble值的精度为2048。当然,整数类型CCD_ 21的精度总是恰好为1。

以下是三个最接近的double下方和三个最靠近的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

当您将long.MaxValue(即9223372036854775807)转换为double时,最接近的表示是9.223372036854775808E+18,这是1.0过大。它用.ToString()表示的字符串将只显示15位数字,而.ToString("R")将显示17位数字(我上面列表中的撇号')。当将double `9.223372036854775808E+18转换回long时,由于数字1.0太大,我们得到溢出异常。当然,您仍然可以将数字转换为ulong,这将显示确切的值。

如果提供的参数第一位是long,您应该检查:

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

否则,您可能会丢失longdouble转换的一些信息。