昨天整理code,顺手写了个UIColor和16进制RGB表示的颜色转换。由于UIColor中的RGBA范围是0..1,所以里面用到了一些乘除法,和强制类型转换:
CGFloat(Float(r)/255.0)
Int(r*255)
写完以后测试了一下貌似没什么问题,就睡觉去了。后来突然想到,1/255并不是一个有理数,而且Int()做的是取地板而不是四舍五入,会不会在这种转换的过程中因为精度的问题造成数据错误呢?于是写了一小段测试代码:
//精度测试,确认转换不会丢失信息
let a = Array.init(0...255)
let b = a.map{ CGFloat(Float($0)/255.0) }
let c = b.map{ Int($0*255) }
a.elementsEqual(c)
还好结果是True,这个转换过程并没有出错的机会。但我怎么可能就此罢休?通过修改a的初始化条件发现,乘除255的时候,在算257的时候就出错了。按理说1/255并不是一个很小的数字,如果准确的话,16位的存储结构是可以保留足够信息的,所以我尝试这打印了一下各个数据类型的精度:
sizeof(Int)*8//64
sizeof(UInt)*8//64
sizeof(UInt16)*8//16
sizeof(Int32)*8//32
sizeof(Float)*8//32
sizeof(Double)*8//64
sizeof(CGFloat)*8//64
sizeof(Character)*8//72
sizeof(String)*8//192
sizeof(CGRect)*8//256
sizeof(UIColor)*8//64
第一组相当的make sense,Int默认就是64位;第二组是实数部分,令我惊讶的是原来CGFloat也是64位的,而Float反而比它差只有32位。第三组的结果比较值得玩味:Swift里面的String搞得比较复杂,是什么都不奇怪,但我以为Character至少会是8,结果却是72;UIColor本身是由4个CGFloat构成,其size却不是4倍的CGFloat;倒是CGRect比较可以解释,因为它是CGSize和CGPoint组合而成,实质上也是4个CGFloat。