C - Unsigned long long 在 32 位机器上加倍

C - Unsigned long long to double on 32-bit machine

嗨,我有两个问题:

  1. uint64_t vs double,后者覆盖正数的范围限制更高?

  2. 如果只需要double的整数部分,如何将double转换成uint64_t。

由于 double 的定义方式,直接转换显然不起作用。

抱歉造成任何混淆,我说的是 32 位机器上 C 中的 64 位双精度数。

举个例子:

//operation for convertion I used:
double sampleRate = (
                      (union { double i; uint64_t sampleRate; })
                      { .i = r23u.outputSampleRate}
                    ).sampleRate;

//the following are printouts on command line
//         double                               uint64_t
//printed by   %.16llx                           %.16llx
outputSampleRate  0x41886a0000000000      0x41886a0000000000 sampleRate

//printed by   %f                                    %llu
outputSampleRate  51200000.000000        4722140757530509312 sampleRate

所以这两个数字保持相同的位模式,但是当打印为小数时,uint64_t 是完全错误的。 谢谢。

double 可以容纳比 uint64_t 大得多的数字,因为 8 字节 IEEE 754 的取值范围是 4.94065645841246544e-324d 到 1.79769313486231570e+308d(正或负)[taken from here][more detailed explanation].但是,如果您在该范围内添加小值,您会感到惊讶,因为在某些时候精度将无法表示,例如添加 1 并将向下舍入到较低的值,实质上使循环稳定地递增 1 不终止。

例如此代码:

#include <stdio.h>
2 int main()
3 {
4     for (double i = 100000000000000000000000000000000.0; i < 1000000000000000000000000000000000000000000000000.0; ++i)
5         printf("%lf\n", i);
6     return 0;
7 }

给我恒定输出 100000000000000005366162204393472.000000。这也是我们在 math.h 中使用 nextafter 和 nexttoward 函数的原因。您还可以在那里找到 ceil 和 floor 函数,理论上,它们可以让您解决第二个问题:删除小数部分。

但是,如果您确实需要保存大量数字,则应该改用 bigint 实现,例如GMP。 Bigints 被设计为对非常大的整数进行操作,并且即使对于非常大的值,像加一这样的操作也会真正增加数字。

uint64_t vs double, which has a higher range limit for covering positive numbers?

uint64_t,在支持的情况下,有 64 个值位,没有填充位,也没有符号位。它可以表示 0 到 264 - 1 之间的所有整数。

基本上所有现代 C 实现都以 IEEE-754 64 位二进制格式表示 double,但 C 不需要甚至不认可该格式。然而,它是如此常见,以至于假设该格式是相当安全的,并且可能只是对定义 FP 特征的宏进行一些编译时检查。为了平衡这个答案,我假设 C 实现确实使用了该表示。

IEEE-754二进制双精度提供53位尾数,因此可以表示0到2之间的所有整数53 - 1.它是一个浮点数-point 格式,但是,具有 11 位二进制指数。它能表示的最大数是(253 - 1) * 21023,也就是将近2 1077。从这个意义上说,double的范围要比uint64_t大得多,但是0到它的最大值之间的绝大多数整数都不能精确表示为double,包括几乎所有的可以 精确表示为 uint64_t.

的数字

How to convert double into uint64_t if only the whole number part of double is needed

你可以简单地赋值(转换是隐式的),或者如果你想清楚地表明转换发生了,你可以显式转换:

double my_double = 1.2345678e48;
uint64_t my_uint;
uint64_t my_other_uint;

my_uint = my_double;
my_other_uint = (uint64_t) my_double;

double 值的任何小数部分都将被截断。如果整数部分可以表示为 uint64_t,则整数部分将被准确保留;否则,行为未定义。

您提供的代码使用联合来覆盖 doubleuint64_t 的存储。这本质上并没有错,但它不是一种在两种类型之间进行转换的有用技术。转换是 C 的所有非隐式值转换机制。