获取数字小数部分的最优方案:是否存在不使用fmod的理由?
好问题!先插个小纠正:你提到的foo / static_cast<int>(foo) - 1.0其实没法正确得到小数部分(比如13.42代入会算出约0.032,明显不对),正确的“截断整数部分取小数”的写法应该是foo - static_cast<double>(static_cast<int>(foo)),或者用std::trunc(foo)来做截断。
回到你的核心问题——有没有合理的理由避开fmod?当然有!下面是几个常见的场景:
负数处理的行为不符合需求:
fmod的结果符号会和被除数保持一致,比如fmod(-13.42, 1.0)会返回-0.42。但如果你的业务逻辑需要负数的小数部分是正数(比如希望-13.42的小数部分是0.58,也就是补到下一个整数的差值),那fmod就满足不了,这时候你会用foo - std::floor(foo)来得到正的小数部分,而不是fmod。性能敏感场景下的开销问题:在嵌入式系统、高频计算这类对性能要求极高的场景里,
fmod作为标准库函数,要处理NaN、无穷大、极小值等各种边界情况,指令开销会比直接的截断减法更大。如果你的输入能保证是正常的正浮点数,那foo - static_cast<double>(static_cast<int>(foo))这种直接的运算,执行速度会更快。代码可读性的考量:如果你的代码只需要处理正浮点数的小数部分,直接写“减去整数部分”的逻辑,对于不熟悉
fmod的开发者来说,一眼就能看懂意图;而fmod(foo, 1.0)虽然功能正确,但可能需要别人反应一下“哦,对,取模1就是拿小数部分”。当然这点见仁见智,但也是很多人选择不用fmod的理由之一。
当然反过来讲,fmod也有它不可替代的优势:比如能正确处理各种特殊值(NaN、无穷大),负数处理的行为是标准化的,如果你需要兼容这些场景,fmod其实是更稳妥的选择。要不要用它,完全取决于你的业务场景和需求。
内容的提问来源于stack exchange,提问作者Jonathan Mee

