【问题标题】:conversion from double to unsigned long long failed从 double 到 unsigned long long 的转换失败
【发布时间】:2013-02-28 16:10:01
【问题描述】:

基于问题convert from float-point to custom numeric type,我想出了一种可移植的安全方法将浮点类型转换为整数数组并且代码工作正常,但是对于从double 转换为unsigned long long 时的某些值具有可以由unsigned long long 安全表示的精度 转换失败不是因为编译时错误而是无效值是signed long long 的最小可表示值或零,转换在visual c++ 2008 上失败,英特尔xe 2013 和 gcc 4.7.2。

这里是代码:(注意while循环中main函数中的第一条语句)

#ifndef CHAR_BIT
#include <limits.h>
#endif

#include <float.h>
#include <math.h>

typedef signed int          int32;
typedef signed long long    int64;
typedef unsigned int       uint32;
typedef unsigned long long uint64;

typedef float  float32;
typedef double float64;

// get size of type in bits corresponding to CHAR_BIT.
template<typename t>
struct sizeof_ex
{
    static const uint32 value = sizeof(t) * CHAR_BIT;
};

// factorial function
float64 fct(int32 i)
{
    float64 r = 1;
    do r *= i; while(--i > 1);
    return r;
}

int main()
{
    // maximum 2 to power that can be stored in uint32
    const uint32 power_2  = uint32(~0);
    // number of binary digits in power_2
    const uint32 digit_cnt = sizeof_ex<uint32>::value;
    // number of array elements that will store expanded value
    const uint32 comp_count = DBL_MAX_EXP / digit_cnt + uint32((DBL_MAX_EXP / digit_cnt) * digit_cnt < DBL_MAX_EXP);
    // array elements
    uint32 value[comp_count];

    // get factorial for 23
    float64 f = fct<float64>(23);
    // save sign for later correction
    bool sign = f < 0;
    // remove sign from float-point if exists
    if (sign) f *= -1;

    // get number of binary digits in f
    uint32 actual_digits = 0;
    frexp(f, (int32*)&actual_digits);

    // get start index in array for little-endian format
    uint32 start_index = (actual_digits / digit_cnt) + uint32((actual_digits / digit_cnt) * digit_cnt < actual_digits) - 1;

    // get all parts but the last
    while (start_index > 0)
    {
        // store current part
        // in this line the compiler fails
        value[start_index] = uint64(f / power_2);
        // exclude it from f
        f -= power_2 * float64(value[start_index]);
        // decrement index
        --start_index;
    }
    // get last part
    value[0] = uint32(f);
}

上面的转换代码会从编译器到另一个编译器给出不同的结果,这意味着当阶乘函数的参数说 20 时,所有编译器都返回有效结果,当值大于 20 时,一些编译器得到部分结果,其他编译器没有,什么时候它变得更大,例如35 变为零。

请告诉我为什么会出现这些错误?

谢谢。

【问题讨论】:

    标签: c++ floating-point type-conversion


    【解决方案1】:

    我认为您的转换逻辑没有任何意义。

    您有一个名为“power_2”的值,它实际上不是 2 的幂,尽管评论它是。

    您通过除以小于 32 位的数字来提取非常大(>64 位)数字的位。显然,结果将 > 32 位,但您将其存储为 32 位值,并截断它。然后你将它乘以原始除数并从你的浮点数中减去。然而,随着数字被截断,您减去的值比原始值少得多,这几乎肯定不是您所期望的。

    我认为还有更多的错误 - 你并不总是想要前 32 位,对于不是 32 位长的倍数的数字,你想要实际长度 mod 32。

    这是我认为你正在尝试做的代码的一个有点懒惰的hack。请注意,pow() 可以被优化出来。

    while (start_index > 0)
    {
        float64 fpow = pow(2., 32. * start_index);
        // store current part
        // in this line the compiler fails
    
        value[start_index] = f / fpow;
        // exclude it from f
    
        f -= fpow * float64(value[start_index]);
        // decrement index
        --start_index;
    }
    

    这几乎没有经过测试,但希望能说明我的意思。

    【讨论】:

    • 我已经更改了很多代码,直到我忘记更新 cmets 来表示我想要的,非常感谢您的帮助。
    猜你喜欢
    • 2014-09-30
    • 2016-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2015-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多