Swift 是否应该使用条件编译来处理不同体系结构上CGFloat的差异?
在回答关于在CGFloat上使用Swift 是否应该使用条件编译来处理不同体系结构上CGFloat的差异?,swift,Swift,在回答关于在CGFloat上使用ceil()为所有体系结构进行编译的问题时,我提出了以下解决方案: var x = CGFloat(0.5) var result: CGFloat #if arch(x86_64) || arch(arm64) result = ceil(x) #else result = ceilf(x) #endif // use result (那些已经困惑的人的背景信息:CGFloa
ceil()
为所有体系结构进行编译的问题时,我提出了以下解决方案:
var x = CGFloat(0.5)
var result: CGFloat
#if arch(x86_64) || arch(arm64)
result = ceil(x)
#else
result = ceilf(x)
#endif
// use result
(那些已经困惑的人的背景信息:CGFloat是32位体系结构的“float”类型,“double”是64位体系结构(即编译目标),这就是为什么只使用ceil()
或ceilf()之一的原因。)
根据目标体系结构的不同,它不会总是编译。请注意,对于条件编译,您似乎无法使用CGFLOAT\u IS\u DOUBLE
,只有体系结构标志…)
现在,在关于在编译时和运行时修复问题的评论中,这引起了一些争论,等等。我想,我的回答很快就被接受了,这可能会引起一些很好的争论
因此,我的新问题是:如果您希望您的iOS和OSX代码在32位和64位设备上运行,那么上述操作是否安全、合理?如果它是理智和明智的,还有更好的(至少同样有效,而不是“讨厌的”)解决方案吗
告诉我我遗漏了什么,但这对我来说似乎很简单。注意,对于当前版本的Swift,下面的解决方案已经在标准库中实现,并且对于Double
、Float
和CGFloat
所有数学函数都正确重载
var f1: Float = 1.0
var f2: Float = 2.0
var d1: Double = 1.0
var d2: Double = 2.0
var f = f1 + f2
var d = d1 + d2
Ceil
是一种算术运算,与任何其他算术运算一样,Double
和Float
都应该有重载版本
var f1: Float = 1.0
var f2: Float = 2.0
var d1: Double = 1.0
var d2: Double = 2.0
var f = f1 + f2
var d = d1 + d2
这是因为+
是重载的,并且对这两种类型都有效
不幸的是,通过从不支持函数重载的C库中提取数学函数,我们只剩下两个函数,而不是一个-ceil
和ceilf
我认为最好的解决方案是为Float
类型重载ceil
:
func ceil(f: CFloat) -> CFloat {
return ceilf(f)
}
让我们做到:
var f: Float = 0.5
var d: Double = 0.5
var f: Float = ceil(f)
var d: Double = ceil(d)
一旦我们为Float
和Double
定义了相同的操作,即使CGFloat
处理也会简单得多
要回答评论:
根据目标处理器体系结构,CGFloat
可以定义为Float
或Double
。这意味着我们应该根据目标体系结构使用ceil
或ceilf
var cgFloat: CGFloat = 1.5
//on 64bit it's a Double
var rounded: CGFloat = ceil(cgFloat)
//on 32bit it's a Float
var rounded: CGFloat = ceilf(cgFloat)
但是,我们必须使用丑陋的#if
另一个选择是使用智能强制转换
var cgFloat: CGFloat = 1.5
var rounded: CGFloat = CGFloat(ceil(Double(cgFloat))
(先将结果转换为Double
,然后将结果转换为CGFloat
)
然而,当我们处理数字时,我们希望数学函数是透明的
var cgFloat1: CGFloat = 1.5
var cgFloat2: CGFloat = 2.5
// this works on both 32 and 64bit architectures!
var sum: CGFloat = cgFloat1 + cgFloat 2
如上图所示,如果我们为Float
重载ceil
,我们可以
var cgFloat: CGFloat = 1.5
// this works on both 32 and 64bit architectures!
var rounded: CGFloat = ceil(cgFloat)
马特
以您的解决方案为基础,如果您在多个地方使用它,那么稍加扩展可能会使其更受欢迎:
extension CGFloat {
var ceil: CGFloat {
#if arch(x86_64) || arch(arm64)
return ceil(x)
#else
return ceilf(x)
#endif
}
}
代码的其余部分将更清晰:
var x = CGFloat(0.5)
x.ceil
真正的解决办法是提交一份bug报告。这种事情不应该是必要的。斯威夫特需要更宽容、更智能的数字运算。@matt嗯,公平地说,CGFloat实际上不是一种斯威夫特类型,它是一种核心图形类型。Swift的浮点始终为32位,Swift的双精度始终为64位。想要在一种语言中同时使用这两种语言,有很好的理由(性能、内存使用、精度)。他们知道,在这一点上,与核心图形类型的交互是很常见的。语言需要使这种互动变得容易。它应该是为与可可一起使用而设计的。@matt哦,这只是一个测试版。你提高雷达了吗?我经常看到这个问题出现,所以我相信你是对的,应该有一个更简单的方法来处理它。我问这个问题的部分原因是我想知道我是否错过了一个更简单的方法…这只是一个将这些检查封装在cgloatceil()中的问题,它比我的版本慢,可能是因为初始化了额外的变量,但是,这是否足以保证条件编译可能取决于您正在做什么以及您多久做一次。@MattGibson您是否将其作为较慢的基准测试?“我希望LLVM能对此进行优化,”罗格诺兰说,“我不记得了。请记住,不管怎样,这将是一个来自Swift编译器早期版本的评论,即使我在去年6月将基准测试到死亡,现在也不太可能是真的。:-)在出现真正的性能问题之前,我更喜欢简单而不是优化。您是否愿意扩展CGFloat处理?这就是我特别感兴趣的,它的复杂性是Float和Double所没有的,因为它既是Double又是Float。@MattGibson补充了一些信息
CGFloat
并不复杂,问题是我们缺少Swift真正的数学库。如果没有预处理器,C函数就不能正常工作。我们需要的是函数重载,而不是针对不同类型的不同函数。这似乎是一个很好的干净封装。我在想延期是有意义的。