我有一个 double 和一个 int64_t。我想知道它们是否具有完全相同的值,以及将一种类型转换为另一种类型是否不会丢失任何信息。
我当前的实现如下:
int int64EqualsDouble(int64_t i, double d) {
return (d >= INT64_MIN)
&& (d < INT64_MAX)
&& (round(d) == d)
&& (i == (int64_t)d);
}
我的问题是:这个实现正确吗?如果不是,正确答案是什么?为了正确,它必须不留下假阳性,也不留下假阴性。
一些示例输入:
- int64EqualsDouble(0, 0.0) 应返回 1
- int64EqualsDouble(1, 1.0) 应返回 1
- int64EqualsDouble(0x3FFFFFFFFFFFFFFF, (double)0x3FFFFFFFFFFFFFFF) 应该返回 0,因为 2^62 - 1 可以用 int64_t 精确表示,但不能用 double 表示。
- int64EqualsDouble(0x4000000000000000, (double)0x4000000000000000) 应该返回 1,因为 2^62 可以在 int64_t 和 double 中精确表示。
- int64EqualsDouble(INT64_MAX, (double)INT64_MAX) 应返回 0,因为 INT64_MAX 无法精确表示为 double
- int64EqualsDouble(..., 1.0e100) 应返回 0,因为 1.0e100 无法精确表示为 int64_t。
是的,您的解决方案可以正常工作,因为它的设计初衷就是这样做,因为int64_t
在使用类似于二进制 IEEE 754 双精度的平台上,根据定义 (C99 7.18.1.1:1) 以二进制补码表示double
类型。它基本上是一样的this one http://gynvael.coldwind.pl/?id=535.
在这些条件下:
d < INT64_MAX
是正确的,因为它相当于d < (double) INT64_MAX
在转换为双精度时,数字INT64_MAX
,等于 0x7fffffffffffffff,向上舍入。因此你想要d
严格小于结果double
避免执行时触发UB(int64_t)d
.
另一方面,INT64_MIN
,是-0x8000000000000000,是完全可以表示的,这意味着double
等于(double)INT64_MIN
可以等于一些int64_t
并且不应该被排除在外(并且这样的double
可以转换为int64_t
不会触发未定义的行为)
不言而喻,由于我们专门使用了关于整数和二进制浮点的 2 补码的假设,因此在不同平台上的这种推理不能保证代码的正确性。采用具有二进制 64 位浮点和 64 位 1 的补码整数类型的平台T
。在那个平台上T_MIN
is -0x7fffffffffffffff
。转换为double
该数字的四舍五入,结果是-0x1.0p63
。在该平台上,使用编写的程序,使用-0x1.0p63
for d
使前三个条件为真,导致未定义的行为(T)d
, 因为从整数到浮点的转换中的溢出是未定义的行为 http://blog.frama-c.com/index.php?post/2013/10/09/Overflow-float-integer.
如果您可以访问完整的 IEEE 754 功能,则有一个更短的解决方案:
#include <fenv.h>
…
#pragma STDC FENV_ACCESS ON
feclearexcept(FE_INEXACT), f == i && !fetestexcept(FE_INEXACT)
该解决方案利用从整数到浮点的转换,当且仅当转换不精确时设置 INEXACT 标志(即,如果i
不能完全表示为double
).
INEXACT 标志保持未设置状态并且f
等于(double)i
当且仅当f
and i
在各自的类型中表示相同的数学值。
这种方法要求编译器收到代码访问 FPU 状态的警告,通常使用#pragma STDC FENV_ACCESS on
但这通常不受支持,您必须使用编译标志。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)