iOS 时间戳精度丢失问题(float=longlong/float)

问题现象

使用long long 格式保持服务端返回的时间戳,然后本地展示该时间戳时,发现总是差一两分钟

    NSDate *currentDate = [NSDate date];
    //模拟获得服务器传回的单位是毫秒的时间戳
    long long timeInterval = [currentDate timeIntervalSince1970] * 1000;
    //毫秒转化为秒,再转化为日期类型
    NSDate *convertDate = [NSDate dateWithTimeIntervalSince1970:(timeInterval/1000.f)];
    NSLog(@"currentDate:%@,convertDate:%@",currentDate,convertDate);

输出效果: 这里注意分钟和秒已经出现了误差(实际是精度丢失)
currentDate:Wed Jun 10 11:17:34 2020,
convertDate:Wed Jun 10 11:18:24 2020

问题原因

时间戳(精确到毫秒)使用long long 格式存储,2020年目前的时间戳位数是13位,long long绝对足够存储下来,由于是整数类型,精度也肯定不会丢失。
但是当将该时间戳转化为NSDate类型时,精度缺丢失了,问题肯定出现在下面这行代码中:

NSDate *convertDate = [NSDate dateWithTimeIntervalSince1970:(timeInterval/1000.f)];

这行代码中,存在一个隐式类型转换,long long / float 类型,默认得到的是float类型,float类型存不下一个时间戳吗?答案是肯定能存下,但是精度没保障。

float的十进制精度是小数点后6~7位,能绝对保证的只有6位。
粗略得看一下这个13位的时间戳1591760542219,转化为浮点数的十进制1.1591760542219 * 10^13,即1.159176之后的小数都是无法精确保证。
实际测试,13位时间戳(毫秒)
1591760542219
移除1000.0得到的浮点数时间戳(单位是秒)
1591760512.000000
对比已经出现了精度丢失,丢失的范围在100秒以内,这也是上面问题现象中时间出现偏差的原因;

问题解法

  1. 本地使用double存储服务端给的时间戳
  2. 将代码中的隐式转换去掉,强制将long long 格式的时间戳转为double后再除以1000.0

根本原因:float的表示精度很低

这就要从浮点数的表示法讲起了IEEE754 浮点数的表示方法

浮点数表示法的简要总结

float.jpeg

浮点数在计算机中的存储格式如上图所示,由符号位,指数,尾数三部分构成,float和double的区别仅体现在指数和尾数所占位数不同。
以float为例,符号位1bit,指数占8bit,尾数23bit。为了直观简化得说明,我们假设其指数和尾数都是以原码(正数符号位为0,负数符号位为1,其中指数部分也包含一个符号位)的方式来表示,简化的示例:

0.75 = 0100 0000 1100 0000 0000 0000 0000 0000

符号位 0 表示为正;指数位1000 0001表示为-1 尾数100 …… (此处省略20个0)表示1.1(二进制是1.1,十进制就是1.5)(由于尾数使用“二进制的科学计数法”,所以首位的整数部分的1默认存在,不占实际的空间,仅存储小数点后面尾数,这也是为什么取名为尾数的原因吧) 所以

0.75 = 1.5 * 2^(-1)

通过上面的示例,我们可以知道,决定float表示范围的是指数,决定float表示精度的是尾数
再来粗略得计算一下float的表示范围
8bit 指数,可以表示的指数为 -126~+127(0和255有其它用户,这里不展开讲)则float能表示的最大值为
floatMax = +(1.11111111111111111111111) × 2 ^127 ≈ 3.402823 e +38
这里用10进制直观的展示一下340282346638528859811704183484516925440.000000,整数位有39位
对应的float能表示的最小值为
floatMin = -(1.11111111111111111111111) × 2 ^(-126) ≈ −1.175494e −38
float能表示的精度由23bit的尾数决定,其最大值为2^23-1 = 8388607,也就是说尾数数值超过这个值,float将无法精确表示,所以float最多能表示小数点后7位,但绝对能保证的为6位(这里指科学计数法的方式),用普通十进制标识一下340282346638528859811704183484516925440.000000 后面的数值范围都是无法精确表示的。
再看一眼double的表示精度:尾数域为52位,最大值2^52−1=4,503,599,627,370,495 所以双精度浮点数的十进制的精度最高为 16 位,绝对保证的为15位,

所以float能表示的数值范围很大(+-10^38),但是对于一个精确到毫秒的只有13位有效数值的时间戳却无能为力,iOS系统存储的时间戳也是默认使用的double类型,所以自己处理时间戳格式类型转换时,需要注意float的表示精度问题。

参考文献:IEEE754 浮点数的表示方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343