gpt4 book ai didi

c - 如何检查 float 是否可以精确表示为整数

转载 作者:太空狗 更新时间:2023-10-29 16:33:50 25 4
gpt4 key购买 nike

我正在寻找一种合理有效的方法来确定浮点值 (double) 是否可以由整数数据类型 (long, 64) 精确表示位)。

我最初的想法是检查指数以查看它是否为 0(或者更准确地说是 127)。但这行不通,因为 2.0 将是 e=1 m=1...

所以基本上,我被困住了。我觉得我可以使用位掩码来做到这一点,但我现在还没有想好如何做到这一点。

那么我如何检查 double 是否可以精确表示为 long?

谢谢

最佳答案

我想我找到了一种以符合标准的方式将 double 限制为整数的方法(这不是真正的问题所在,但它有很大帮助)。首先,我们需要了解为什么明显的代码正确。

// INCORRECT CODE
uint64_t double_to_uint64 (double x)
{
if (x < 0.0) {
return 0;
}
if (x > UINT64_MAX) {
return UINT64_MAX;
}
return x;
}

这里的问题是,在第二次比较中,UINT64_MAX 被隐式转换为 double。 C 标准并没有具体说明这种转换是如何进行的,只是规定要向上或向下舍入到一个可表示的值。这意味着第二次比较可能是错误的,即使在数学上应该是正确的(这可能发生在 UINT64_MAX 被四舍五入时,并且 'x' 在数学上介于 UINT64_MAX(双)UINT64_MAX)。因此,将 double 转换为 uint64_t 可能会导致该边缘情况下的未定义行为。

令人惊讶的是,解决方案非常简单。考虑到虽然 UINT64_MAX 可能无法在 double 中精确表示,UINT64_MAX+1 是 2 的幂(并且不会太大),当然是。因此,如果我们首先将输入舍入为整数,比较 x > UINT64_MAX 等同于 x >= UINT64_MAX+1,除了加法中可能溢出。我们可以通过使用 ldexp 而不是向 UINT64_MAX 加一来修复溢出。也就是说,下面的代码应该是正确的。

/* Input: a double 'x', which must not be NaN.
* Output: If 'x' is lesser than zero, then zero;
* otherwise, if 'x' is greater than UINT64_MAX, then UINT64_MAX;
* otherwise, 'x', rounded down to an integer.
*/
uint64_t double_to_uint64 (double x)
{
assert(!isnan(x));
double y = floor(x);
if (y < 0.0) {
return 0;
}
if (y >= ldexp(1.0, 64)) {
return UINT64_MAX;
}
return y;
}

现在,回到您的问题:x 是否可以在 uint64_t 中准确表示?只有当它既不是圆形也不是夹紧的时候。

/* Input: a double 'x', which must not be NaN.
* Output: If 'x' is exactly representable in an uint64_t,
* then 1, otherwise 0.
*/
int double_representable_in_uint64 (double x)
{
assert(!isnan(x));
return (floor(x) == x && x >= 0.0 && x < ldexp(1.0, 64));
}

相同的算法可用于不同大小的整数,也可用于稍作修改的有符号整数。下面的代码对 uint32_tuint64_t 版本进行了一些非常基本的测试(可能只捕获误报),但也适用于手动检查边缘情况.

#include <inttypes.h>
#include <math.h>
#include <limits.h>
#include <assert.h>
#include <stdio.h>

uint32_t double_to_uint32 (double x)
{
assert(!isnan(x));
double y = floor(x);
if (y < 0.0) {
return 0;
}
if (y >= ldexp(1.0, 32)) {
return UINT32_MAX;
}
return y;
}

uint64_t double_to_uint64 (double x)
{
assert(!isnan(x));
double y = floor(x);
if (y < 0.0) {
return 0;
}
if (y >= ldexp(1.0, 64)) {
return UINT64_MAX;
}
return y;
}

int double_representable_in_uint32 (double x)
{
assert(!isnan(x));
return (floor(x) == x && x >= 0.0 && x < ldexp(1.0, 32));
}

int double_representable_in_uint64 (double x)
{
assert(!isnan(x));
return (floor(x) == x && x >= 0.0 && x < ldexp(1.0, 64));
}

int main ()
{
{
printf("Testing 32-bit\n");
for (double x = 4294967295.999990; x < 4294967296.000017; x = nextafter(x, INFINITY)) {
uint32_t y = double_to_uint32(x);
int representable = double_representable_in_uint32(x);
printf("%f -> %" PRIu32 " representable=%d\n", x, y, representable);
assert(!representable || (double)(uint32_t)x == x);
}
}
{
printf("Testing 64-bit\n");
double x = ldexp(1.0, 64) - 40000.0;
for (double x = 18446744073709510656.0; x < 18446744073709629440.0; x = nextafter(x, INFINITY)) {
uint64_t y = double_to_uint64(x);
int representable = double_representable_in_uint64(x);
printf("%f -> %" PRIu64 " representable=%d\n", x, y, representable);
assert(!representable || (double)(uint64_t)x == x);
}
}
}

关于c - 如何检查 float 是否可以精确表示为整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8905246/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com