代码:

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的讨论

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

  

一个快速double转int的方法(利用magic number)的更多相关文章

  1. float(double)快速转换int的方法

    自己写一个软件渲染器的时候,无意中发现float转换int非常耗时,于是查阅文章,这才有了这个命题,以前不清楚还有这么个机制.网上看了很多文章,搜索到了一个数字6755399441055744,这个是 ...

  2. 实验四 (1):定义一个形状类(Shape)方法:计算周长,计算面积

    (1)定义一个形状类(Shape)方法:计算周长,计算面积子类:矩形类(Rectangle) :额外的方法:differ() 计算长宽差圆形类(Circle)三角形类(Triangle)正方形类(Sq ...

  3. .NET向WebService传值为decimal、double、int、DateTime等非string类型属性时,服务器端接收不到数据的问题

    最近在做CRM项目时,使用C#调用SAP PI发布的WebService服务时遇到的问题: 向WebService传值为decimal.double.int.DateTime等非string类型数据时 ...

  4. 浅析MyBatis(一):由一个快速案例剖析MyBatis的整体架构与运行流程

    MyBatis 是轻量级的 Java 持久层中间件,完全基于 JDBC 实现持久化的数据访问,支持以 xml 和注解的形式进行配置,能灵活.简单地进行 SQL 映射,也提供了比 JDBC 更丰富的结果 ...

  5. linux几种快速清空文件内容的方法

    linux几种快速清空文件内容的方法 几种快速清空文件内容的方法: $ : > filename #其中的 : 是一个占位符, 不产生任何输出. $ > filename $ echo & ...

  6. 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法

    [源码下载] 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法 作者:webabcd 介绍重新想象 Wi ...

  7. 自己写一个与startWith类似的判断方法

    package com.hanqi.lianxi; import java.util.Scanner; public class startWith {        //自己顶一个与startWit ...

  8. iOS控件——UIView的viewWithTag:(int)findTag方法描述

    UIView拥有一个viewWithTag:(int)findTag方法,调用方式为[MyView viewWithTag:整形数字]该方法返回tag == findTag的控件.ios控件中允许多个 ...

  9. 一个快速、高效的Levenshtein算法实现——代码实现

    在网上看到一篇博客讲解Levenshtein的计算,大部分内容都挺好的,只是在一些细节上不够好,看了很长时间才明白.我对其中的算法描述做了一个简单的修改.原文的链接是:一个快速.高效的Levensht ...

随机推荐

  1. winfrom 隐藏任务栏(win7)

    1:新建winfrom 窗体应用程序 2:拖入contextMenuStrip.notifyIcon 2个控件 3:如图 4:code: 注意:复制控件事件要注册 using System; usin ...

  2. C++代码重构——从C global到C++ template

    在学数据结构的时候,我常有这样目标--写出能够最大程度复用的代码(算法正确,封装优秀).我常想--如何能在短时间内达成"算法正确,封装优秀"这样的目标.经过一段时间的摸索,我的结论 ...

  3. Sql Server 2008服务启动失败,错误17058

    SQL Server (SQLEXPRESS) 服务因 17058 (0x42A2) 服务性错误而停止. 有关更多信息,请参阅在 http://go.microsoft.com/fwlink/even ...

  4. mongoTemplate简单用法(增删改查)

    分页时查找数量: public long countSample(String id) { Query query = new Query(); if (StringUtil.hasText(id)) ...

  5. iOS/Android 浏览器(h5)及微信中唤起本地APP

    在移动互联网,链接是比较重要的传播媒质,但很多时候我们又希望用户能够回到APP中,这就要求APP可以通过浏览器或在微信中被方便地唤起. 这是一个既直观又很好的用户体验,但在实现过程中会遇到各种问题: ...

  6. Android笔记:HTTP相关

    发送HTTP请求 HttpURLConnection.HttpClient XML解析 Pull 解析.SAX 解析.DOM 解析 解析JSON 格式数据 官方提供的JSONObject.谷歌的开源库 ...

  7. swap的应用两个数的交换

    #include <stdio.h>//这儿表示的函数的输入输出头文件void swap(int x,int y);void swap_p(int *x,int *y);//表示调用一个方 ...

  8. 利用委托与Lambada创建和调用webapi接口

    前言 现在项目中用的是webapi,其中有以下问题:    1.接口随着开发的增多逐渐增加相当庞大. 2.接口调用时不好管理. 以上是主要问题,对此就衍生了一个想法: 如果每一个接口都一个配置文件来管 ...

  9. CLR VIA C# 学习笔记

    第19章 可空类型 1)使用Nullable<T>可将int32的值类型设置为Null,CLR会在Null时默认赋值为0; 如:Nullable<T> x=null; //使用 ...

  10. 解决VS+opencv中Debug版本与Release版本lib切换的问题

    Author: Maddock Date: 2015-03-26 09:34:48 问题来源:http://bbs.csdn.net/topics/390733725 PS: 按照上述方法做的时候,在 ...