如何将无符号长整型转换为双精度浮点数

3
我有一个如下的随机数生成器。
uint64_t s[ 2 ];

uint64_t next(void) { 
    uint64_t s1 = s[ 0 ];
    const uint64_t s0 = s[ 1 ];
    s[ 0 ] = s0;
    s1 ^= s1 << 23; // a
    return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0; // b, c
}

实际上,我希望在范围(0,1)内获得双倍数。一种选择是在我的代码中执行此操作。

#define M_RAN_INVM64 5.42101086242752217003726400434970855e-20
[..]
double u1;
u1 = next()*M_RAN_INVM64;
  1. 我担心失去精度并且这与平台有关。
  2. 我不想得到值为0或1。

一种选择是将next()转换为有符号长整型,乘以M_RAN_INVM64,然后加上0.5。然后我可以加上2^(64)/2来确保我永远不会得到0。


double d = your_unsigned_longlong * 1.0 / ULONG_LONG_MAX;,然后过滤为0和1。 - The Paramagnetic Croissant
@user3477950 我正在使用 #define 来提高速度,以避免除法。我想我可以只定义一个常量为 1.0 / ULONG_LONG_MAX 并放弃 #define? - Simd
@userXXXXX 是的,有这种方法,但那是过早的优化。相反地,请打开编译器优化标志。 - The Paramagnetic Croissant
我正在失去精度。如果你有一个64位的值并将其塞入53位的尾数中,你知道会发生什么吗?其中一些位将无法适应。这取决于平台。只要你的浮点数是IEEE,你应该没问题。如果你需要一个真正强大的解决方案,最好从一个强大的PRNG开始。我不想得到0或1的值。最简单的方法是检查这些值并过滤它们。 - n. m.
@n.m. 平台依赖性来自于我选择了2^(-64),但现在我想想,uint64_t被定义为64位,所以这可能是可以的。 - Simd
@user2179021 unsigned long long 没有被定义为64位。它必须至少是64位。 - chux - Reinstate Monica
2个回答

2

回答被接受后:

double 数字的分布是对数分布的。假设使用基于2的double,0.25到0.5的double数量与0.5到1.0相同。

假设double是IEEE binary64:...

要获得[0.0到1.0)范围内double的均匀(线性)分布,只能使用2^53个值。因此,从next()的结果中舍弃11位。

uint64_t u1 = next();
u1 >>= 11;
// or
u1 &= 0x001FFFFFFFFFFFFF;
if (u1 == 0) ThrowOutZero();

// convert to (0.0 to 1.0)
double d = u1 / 9007199254740992.0;  // Divide by 2^53

1
编辑:为了将任何在范围(0,1)内的正 unsigned long long 转换为 long double,您可以使用以下步骤因子:
 long double step = ((long double) 1.0) / ULLONG_MAX

额外信息 当使用浮点数时,请考虑您系统的限制。以下内容可以帮助您了解系统中的浮点数:

有一种便携式方法可以获取编译程序所在系统中最小的浮点数。

通过使用标准库<float.h>,可以实现此操作。
您可以使用以下宏常量:

 FLT_MIN    // Smallest positive float number in your system  
 DBL_MIN    // Smallest positive double number in your system  
 LDBL_MIN   // Smallest positive long double number in your system  

此外,您还可以考虑类似于“epsilon”的常数,它会提供有关给定类型的最小正数的信息,使得1.0!= 1.0 +“epsilon”。
当您尝试获得尽可能接近1.0但仍然不同的数字时,这对您很有帮助:
FLT_EPSILON  
DBL_EPSILON  
LDBL_EPSILON  

例如,见:float.h (ANSI)

我有点困惑。我需要的是1/ULLONG_MAX,对吗? - Simd
@user2179021 是的,但也许我没有完全理解你想做什么。如果你只需要的是 1/ULLONG_MAX,那么你可以写成 ((long double) 1.0) / ULLONG_MAX,以获得 long double 精度的结果。 - pablo1977

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接