我有一个双,一个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标志。
当且仅当f
和i
在它们各自的类型中表示相同的数学值时,INEXACT标志保持未设置且f
等于(double)i
。
这种方法要求编译器警告代码访问FPU的状态,通常使用#pragma STDC FENV_ACCESS on
但通常不支持,并且您必须使用编译标志。
OP的代码具有可以避免的依赖性。
为了成功比较, d
必须是整数并且round(d) == d
来处理。 即使是d
,因为NaN也会失败。
d
必须在[ INT64_MIN
… INT64_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_t
的double
。 (可能适用于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