首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何正确地将浮点数与int相乘,并得到仅受有效数字影响的结果?

如何正确地将浮点数与int相乘,并得到仅受有效数字影响的结果?
EN

Stack Overflow用户
提问于 2013-01-10 15:54:50
回答 3查看 13.3K关注 0票数 6

我有代码在浮点数(表示秒)和int64 (表示纳秒)之间进行转换,从浮点数中取6小数位。

代码语言:javascript
复制
int64_t nanos = f * 1000000000LL;

但是,许多存储在浮点数中的十进制值不能精确地在二进制浮点数中表示,因此,当浮点数为14199999488时,我会得到类似于14.2f的结果。目前,我通过计算基数之后的大量数字来解决这个问题。

代码语言:javascript
复制
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正确地绕过浮点数。然后,我将字符串解析为前逗号和后逗号整数,并使用整数算法对它们进行多重处理。

代码语言:javascript
复制
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);
}

这很好,但我想知道是否有更好的,也许更快的方法来做到这一点?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-01-10 16:32:39

例如,如果您想要舍入小数点后一位。

代码语言:javascript
复制
#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,.,还有六个小数位

代码语言:javascript
复制
n = ((long long)((f + 0.0000005) * 1000000)) * 1000LL;

如果要考虑有效数字(所有数字),必须先取log10(f),然后调整小数位数的四舍五入。

但是正如@MarkB已经说过的,如果您首先使用int64_t,那么根本不需要这样做。

票数 2
EN

Stack Overflow用户

发布于 2013-01-10 16:31:21

通过将该值存储在已造成损害的float中,无论它是什么,您都丢失了原始数字。您可以猜测一个值,这个值可能是预期的,然后是循环的,或者如果您只是试图为用户显示一个值,您可以将其舍入到较低的小数位数。

相反,您可以通过在整个代码库中使用定点int64_t表示来解决所有这些问题,永远不要转换到/从float,并且避免在每次转换过程中丢弃精度。

票数 3
EN

Stack Overflow用户

发布于 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中使用这样的代码,您的问题的解决方案可能是:

代码语言:javascript
复制
nanos := (floatingPointSeconds asMinimalDecimalFraction * 1e9) rounded.

但是上面的代码使用的是精确的算术(1e9是一个整数)和遮罩下的任意长度整数。

请注意,在浮点数中执行乘法将是错误的:

代码语言:javascript
复制
nanos := (aFloat * 1e9) asMinimalDecimalFraction rounded.

的确,尽管1e9 asFloat转换是精确的,但它的意义和跨度为21位,因此浮点乘法很可能会累积舍弃误差,并使检索一个短分数的问题恶化。

虽然我从技术上回答了这个问题,但出于以下原因,我个人认为上述算法从实际意义上来说是不合适的:

  1. 在没有任意精确算术库的情况下,用低级别C/C++指令完成这一操作并不是达到结果的最快途径。
  2. 它是非常有限的,因为它不适用于带有几个四舍五入错误的计算结果(在统计上需要很多位数)。
  3. 如果您可以完全避免使用浮点数并使用nanos int,那就太过分了。

尽管如此,很高兴知道它的存在..。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14261877

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档