zoukankan      html  css  js  c++  java
  • 一个快速double转int的方法(利用magic number)

    代码:

    int i = *reinterpret_cast<int*>(&(d += 6755399441055744.0));

    知识点:

    1.reinterpret_cast<type_id> expression:type_id 必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。

    只是将bit表示进行了重新解读,不改变位表示

    reinterpret_cast 只能在指针之间转换。

    更多信息,参见msdn

    2.magic number的原理:stackoverflow的页面

    6755399441055744.0 = 1<<51 + 1<<52 = 2,251,799,813,685,248 + 4,503,599,627,370,496

    根据double类型的格式可知:尾数52位

    从而double类型有一个性质:当数据大小处于2^52~2^53范围内时,可表示的数恰好为该范围内的整数(根据数据格式显然可知)

    从而把一个(相对)非常小的double类型数强行规约到2^52~2^53范围内,截取它的后32位即为相应的int表示(因为加的数实际为11000...000,只影响第52和53位,都已经被舍去)

    3.一些问题:

      1)为什么选择1<<51 + 1<<52?

      尝试1<<52作为参数,发现正数范围内舍入正常,但负数范围内出现奇怪的结果:

      [0~-0.25) --> 0

      [-0.25~-0.75) --> -1

      [-0.75~-1.25] --> -2

      (-1.25~1.75) --> -3

      行为类似于数据大小位于2^53~2^54范围的表现(只表示范围内的全部偶数,相当于上文范围*2)

      猜想:1<<51是为了让负数的表示也正常?如何实现?

      int中的负数表示利用了补码,而double中利用了符号位,显然不同

      假设某个负数为(-a),则经过这样变换后,变为(2^52+2^51-a),显然为正且在2^52与2^53之间

      同时,如果我们用补码形式看待这个结果,发现2^52+2^51只影响高位,0~50位不受影响。

      为什么要+1<<51?由于double类型的尾数前面有一个隐含的前导1,在加法时无影响,但减法时向前借位减就会漏掉这个前导1,因而需要1<<51手动将尾数第一位设为1(反正最后要舍去)

      关于double计算的更多细节,参见此处

      2)数据范围

      理论上来说double的大小不能大于2^51,但考虑到int的范围,足够用了

      (注意:此处的溢出行为与通常的转换例如static_cast<int>不同)

      3)存在的问题

      1.引用还是指针?

      更好的写法:int &i = reinterpret_cast<int &>(d += 6755399441055744.0);

      原因:

        int i = *reinterpret_cast<int*>(&(d += 6755399441055744.0));  计算->取引用->reinterpret_cast改类型->取指针值

        int &i = reinterpret_cast<int &>(d += 6755399441055744.0);   计算->改类型,较为明确

        要深入理解引用与指针的区别

      2.舍入规则:round-to-even,2.5-->2,3.5-->4

      原因:double加法运算的原理?

      3.会有strict alias的问题,C++中为Undefined Behavior

      关于strict alias:当两个指针指向同一块区域或对象时,我们称一个指针 alias 另一个指针

      Strict aliasing 是C或C++编译器的一种假设:不同类型的指针绝对不会指向同一块内存区域。

       更多关于strict alias的讨论

    螺蛳壳里做道场,一行代码有学问……

      

  • 相关阅读:
    01-Git 及其可视化工具TortoiseGit 的安装和使用
    IntelliJ IDEA中Warning:java:源值1.5已过时, 将在未来所有发行版中删除
    JVM学习笔记四_垃圾收集器与内存分配策略
    JVM学习笔记三_异常初步
    CentOs7 图形界面和命令行界面切换
    基本概念一
    JVM学习笔记二_对象的创建、布局和定位
    JVM学习笔记一_运行时数据区域
    个人阅读作业二
    软件工程随笔
  • 原文地址:https://www.cnblogs.com/giddens/p/5188393.html
Copyright © 2011-2022 走看看