我有代码在浮点数(表示秒)和int64 (表示纳秒)之间进行转换,从浮点数中取6小数位。
int64_t nanos = f * 1000000000LL;但是,许多存储在浮点数中的十进制值不能精确地在二进制浮点数中表示,因此,当浮点数为14199999488时,我会得到类似于14.2f的结果。目前,我通过计算基数之后的大量数字来解决这个问题。
const float logOfSecs = std::log10(f);
int precommaPlaces = 0;
if(logOfSecs > 0) {
precommaPlaces = std::ceil(logOfSecs);
}
int postcommaPlaces = 7 - precommaPlaces;
if(postcommaPlaces < 0) {
postcommaPlaces = 0;
}然后将浮点数打印成字符串,以便让Qt正确地绕过浮点数。然后,我将字符串解析为前逗号和后逗号整数,并使用整数算法对它们进行多重处理。
const QString valueStr = QString::number(f, 'f', postcommaPlaces);
qint64 nanos = 0;
nanos += valueStr.section(".", 0, 0).toLongLong() * 1000000000LL;
if(postcommaPlaces) {
nanos += valueStr.section(".", 1).toLongLong() *
std::pow(10.0, 9 - postcommaPlaces);
}这很好,但我想知道是否有更好的,也许更快的方法来做到这一点?
发布于 2013-01-10 16:32:39
例如,如果您想要舍入小数点后一位。
#include <iostream>
int main()
{
float f = 14.2f;
long long n = f * 1000000000LL;
std::cout << "float: " << n << '\n';
n = (f + 0.05) * 10;
n *= 100000000LL;
std::cout << "rounded: " << n << '\n';
return 0;
}对于两个小数位,它是(f + 0.005) * 100,.,还有六个小数位
n = ((long long)((f + 0.0000005) * 1000000)) * 1000LL;如果要考虑有效数字(所有数字),必须先取log10(f),然后调整小数位数的四舍五入。
但是正如@MarkB已经说过的,如果您首先使用int64_t,那么根本不需要这样做。
发布于 2013-01-10 16:31:21
通过将该值存储在已造成损害的float中,无论它是什么,您都丢失了原始数字。您可以猜测一个值,这个值可能是预期的,然后是循环的,或者如果您只是试图为用户显示一个值,您可以将其舍入到较低的小数位数。
相反,您可以通过在整个代码库中使用定点int64_t表示来解决所有这些问题,永远不要转换到/从float,并且避免在每次转换过程中丢弃精度。
发布于 2013-01-10 21:12:09
正如在其他答案中所指出的,舍入到任意数目的十进制数字与打印浮点数密切相关。由于正确循环的算法相当复杂,正确执行的最简单方法是使用printf本身。
请注意,您不必提供任意数目的数字,另一种方法是使用在基2中被转换回不变的最短小数点。这些算法用于打印Scheme、Java、Python、Squeak/Pharo等.不幸的是,libm和任何标准C库都不兼容。
方案甚至更好,因为它打印*当您强制设置一个固定的数字数时,数字并不重要(*意味着任何数字在转换回基2时都会产生相同的浮点数)。
在这个问题中,http://code.google.com/p/pharo/issues/detail?id=4957有一个名为Float-asMinimalDecimalFraction.st的附件,它包含Smalltalk中类似的打印算法的实现,而不是输出一个分数(两个任意长度整数的比率),而不是一个ASCII字符串。
因此,例如,尽管14.2f在内部精确地表示为14.19999980926513671875 --这还不算太晚--您可以检索到(142/10)是最短的十进制小数。
在Smalltalk中使用这样的代码,您的问题的解决方案可能是:
nanos := (floatingPointSeconds asMinimalDecimalFraction * 1e9) rounded.但是上面的代码使用的是精确的算术(1e9是一个整数)和遮罩下的任意长度整数。
请注意,在浮点数中执行乘法将是错误的:
nanos := (aFloat * 1e9) asMinimalDecimalFraction rounded.的确,尽管1e9 asFloat转换是精确的,但它的意义和跨度为21位,因此浮点乘法很可能会累积舍弃误差,并使检索一个短分数的问题恶化。
虽然我从技术上回答了这个问题,但出于以下原因,我个人认为上述算法从实际意义上来说是不合适的:
尽管如此,很高兴知道它的存在..。
https://stackoverflow.com/questions/14261877
复制相似问题