c/c++语言开发共享如何测试无损双/整数转换?

我有一个双,一个int64_t。 我想知道它们是否保持完全相同的值,如果将一种类型转换为另一种类型,则不会丢失任何信息。

我目前的实施如下:

int int64EqualsDouble(int64_t i, double d) { return (d >= INT64_MIN) && (d < INT64_MAX) && (round(d) == d) && (i == (int64_t)d); } 

我的问题是:这个实现是否正确? 如果没有,那么什么是正确答案? 为了正确,它必须不留假阳性,不得假阴性。

一些示例输入:

    是的,您的解决方案正常工作,因为它的设计是这样做的,因为int64_t按照定义(C99 7.18.1.1:1)以二进制表示,在使用类似二进制IEEE 754双精度的double类型的平台上。 它与此基本相同。

    在这些条件下:

    不言而喻,由于我们已经专门使用了关于整数和二进制浮点的2的补码的假设,因此在不同平台上的这种推理无法保证代码的正确性。 采用具有二进制64位浮点和64位1的补码整数类型T 。 在该平台上, T_MIN-0x7fffffffffffffff 。 转换为该数字的double向下-0x1.0p63 ,从而产生-0x1.0p63 。 在该平台上,使用您编写的程序,使用-0x1.0p63表示d使前三个条件为真,导致(T)d中的未定义行为,因为从整数到浮点的转换中的溢出是未定义的行为 。


    如果您可以访问完整的IEEE 754function,则可以使用更短的解决方案:

     #include  … #pragma STDC FENV_ACCESS ON feclearexcept(FE_INEXACT), f == i && !fetestexcept(FE_INEXACT) 

    如果转换是不精确的(即,如果i不能完全表示为double ),则此解决方案利用从整数到浮点的转换设置INEXACT标志。

    当且仅当fi在它们各自的类型中表示相同的数学值时,INEXACT标志保持未设置且f等于(double)i

    这种方法要求编译器警告代码访问FPU的状态,通常使用#pragma STDC FENV_ACCESS on但通常不支持,并且您必须使用编译标志。

    OP的代码具有可以避免的依赖性。

    为了成功比较, d必须是整数并且round(d) == d来处理。 即使是d ,因为NaN也会失败。

    d必须在[ INT64_MININT64_MAX ]范围内进行数学 INT64_MAX ,如果if条件正确确保,则最终i == (int64_t)d完成测试。

    所以问题归结为将INT64限制与double d进行比较。

    我们假设FLT_RADIX == 2 ,但不一定是IEEE 754 binary64 。

    d >= INT64_MIN不是问题,因为-INT64_MIN是2的幂并且恰好转换为相同值的double ,因此>=是精确的。

    代码想做数学d <= INT64_MAX ,但这可能不起作用,所以问题。 INT64_MAX是“2 - 1的幂”并且可能无法精确转换 - 它取决于double精度的精度是否超过63位 - 使得比较不清楚。 解决方案是将比较减半。 d/2没有精度损失, INT64_MAX/2 + 1精确转换为double功率2

     d/2 < (INT64_MAX/2 + 1) 

    [编辑]

     // or simply d < ((double)(INT64_MAX/2 + 1))*2 

    因此,如果代码不想依赖精度低于uint64_tdouble 。 (可能适用于long double东西)更便携的解决方案

     int int64EqualsDouble(int64_t i, double d) { return (d >= INT64_MIN) && (d < ((double)(INT64_MAX/2 + 1))*2) // (d/2 < (INT64_MAX/2 + 1)) && (round(d) == d) && (i == (int64_t)d); } 

    注意:没有舍入模式问题。

    [编辑]更深的限制说明

    在数学上保证, INT64_MIN <= d <= INT64_MAX ,可以重新声明为INT64_MIN <= d < (INT64_MAX + 1)因为我们正在处理整数。 由于代码中(double) (INT64_MAX + 1)的原始应用肯定是0,所以替代方法是((double)(INT64_MAX/2 + 1))*2 。 这可以扩展到具有double高功率2到((double)(INT64_MAX/FLT_RADIX + 1))*FLT_RADIX稀有机器。 比较限制是精确的2次(lo_limit >= d) && (d < hi_limit) ,无论浮点的精度如何,转换为double不会导致精度损失,并且(lo_limit >= d) && (d < hi_limit)是精确的。 注意: FLT_RADIX == 10的罕见浮点仍然是个问题。

    除了Pascal Cuoq的精心解答之外,考虑到你在评论中给出的额外背景,我还会添加一个负零的测试。 你应该保留负零,除非你有充分的理由不这样做。 您需要进行特定测试以避免将它们转换为(int64_t)0 。 根据您当前的提议,负零将通过您的测试,存储为int64_t并回读为正零。

    我不确定测试它们的最有效方法是什么,也许这个:

    需要了解更多c/c++开发分享如何测试无损双/整数转换?,也可以关注C/ C++技术分享栏目---计算机技术网(www.ctvol.com)!

     int int64EqualsDouble(int64_t i, double d) { return (d >= INT64_MIN) && (d < INT64_MAX) && (round(d) == d) && (i == (int64_t)d && (!signbit(d) || d != 0.0); } 

      以上就是c/c++开发分享如何测试无损双/整数转换?相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/978953.html

      (0)
      上一篇 2021年12月12日
      下一篇 2021年12月12日

      精彩推荐