C - 32位机器上的无符号长整数加倍

时间:2016-08-05 20:00:58

标签: c double unsigned primitive

您好我有两个问题:

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

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

  3. 由于定义了双倍,直接投射显然不起作用。

    很抱歉有任何困惑,我在谈论32位机器上的64位双C语言。

    举个例子:

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

2 个答案:

答案 0 :(得分:5)

  

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

支持的

uint64_t具有64个值位,没有填充位,没有符号位。它可以表示0到2之间的所有整数 64 - 1,包括在内。

基本上所有现代C实现都代表IEEE-754 64位二进制格式的double,但C不要求也不支持该格式。然而,很常见的是,假设格式是相当安全的,并且可能只是对定义FP特性的宏进行一些编译时检查。我将假设这个答案的平衡,C实现确实使用了该表示。

IEEE-754二进制双精度提供53位尾数,因此它可以表示0到2之间的所有整数 53 - 1.它是浮点格式但是,使用11位二进制指数。它可以表示的最大数是(2 53 -1)* 2 1023 ,或接近2 1077 。从这个意义上说,double的范围比uint64_t大得多,但是0和它的最大值之间的绝大多数整数不能完全表示为double s,包括几乎所有的整数可以完全由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机制。

答案 1 :(得分:1)

double可以容纳比uint64_t更大的数字,因为8字节IEEE 754的值范围是4.94065645841246544e-324d到1.79769313486231570e + 308d(正面或负面)[taken from here] { {3}}。但是,如果在该范围内添加小值,则会出现意外情况,因为在某些时候精度将无法表示例如添加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实现,例如[more detailed explanation]。 Bigints被设计用于对非常大的整数进行操作,并且即使对于非常大的值,添加一个操作也会真正增加数量。