【问题标题】:C - Unsigned long long to double on 32-bit machineC - Unsigned long long 在 32 位机器上翻倍
【发布时间】:2016-08-05 20:00:58
【问题描述】:

您好,我有两个问题:

  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 是完全错误的。 谢谢。

【问题讨论】:

  • 您说的是 C 中指定的 double,还是 ieee754 binary64 浮点中的 double
  • 对于#2,您是否尝试仅分配给 double 变量?
  • “直接投射显然不起作用”是什么意思?它怎么不起作用?举例说明这个数字,当你将它转换为 double 时得到了什么,以及你如何检查它的值。
  • double 有一个更大的“最大值”。但是“覆盖”?它与值的对数成比例地失去精度。所以在某些时候,即使是“整体”部分也会受到不精确的影响。
  • “在 32 位机器上用 C 语言加倍”——这说明什么!

标签: c double unsigned primitive


【解决方案1】:

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

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

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

IEEE-754 二进制双精度提供了 53 位尾数,因此它可以表示 0 到 2 之间的所有整数53 - 1。它是一个 floating-point 格式,但是,使用 11 位二进制指数。它可以表示的最大数是(253 - 1) * 21023,或接近21077。从这个意义上说,double 的范围比uint64_t 大得多,但是绝大多数介于 0 和最大值之间的整数不能完全表示为doubles,包括几乎所有可以表示的数字 完全由uint64_t 表示。

如果只需要double的整数部分,如何将double转换为uint64_t

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

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 机制。

【讨论】:

  • C11 标准草案 n1570 6.3.1.4 实数浮点数和整数 1 当实数浮点类型的有限值转换为 _Bool 以外的整数类型时,小数部分被丢弃(即值被截断为零)。如果整数部分的值不能用整数类型表示,则行为未定义。
  • @EOF,感谢您的更正。我已经更新了我的答案。
【解决方案2】:

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。 Bigint 旨在对非常大的整数进行运算,即使对于非常大的值,加一之类的运算也会真正增加数字。

【讨论】:

    猜你喜欢
    • 2013-08-01
    • 2011-03-05
    • 2013-12-05
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多