【问题标题】:casting doubles to integers in order to gain speed将双精度数转换为整数以提高速度
【发布时间】:2010-05-12 16:58:47
【问题描述】:

在 Redis (http://code.google.com/p/redis) 中有与元素相关的分数,以便对这些元素进行排序。这个分数是双倍的,即使许多用户实际上是按整数排序(例如 unix 时间)。

保存数据库后,我们需要写入这个双倍好的磁盘。这是目前使用的:

  snprintf((char*)buf+1,sizeof(buf)-1,"%.17g",val);

另外检查无穷大和非数字条件,以便在最终的数据库文件中也表示这一点。

不幸的是,将 double 转换为字符串表示非常慢。虽然我们在 Redis 中有一个函数,可以以更快的方式将整数转换为字符串表示形式。所以我的想法是检查是否可以将双精度转换为整数而不会丢失数据,然后使用该函数将整数转换为字符串,如果这是真的。

为了提供良好的加速,整数“等价”的测试当然必须很快。所以我使用了一个可能是未定义行为的技巧,但在实践中效果很好。类似的东西:

double x = ... some value ...
if (x == (double)((long long)x))
    use_the_fast_integer_function((long long)x);
else
    use_the_slow_snprintf(x);

在我的推理中,上面的双重转换将双精度转换为长整数,然后再转换回整数。如果范围合适,并且没有小数部分,则该数字将在转换后继续存在,并且与初始数字完全相同。

因为我想确保这不会破坏某些系统中的东西,所以我在 freenode 上加入了#c,我受到了很多侮辱;)所以我现在在这里尝试。

是否有一种标准方法可以在不超出 ANSI C 的情况下完成我想做的事情?否则,上面的代码是否应该在当前 Redis 目标的所有 Posix 系统中工作?也就是说,现在运行 Linux / Mac OS X / *BSD / Solaris 的拱门?

为了使代码更清晰,我可以添加的是在尝试强制转换之前明确检查双精度的范围。

感谢您的帮助。

【问题讨论】:

  • 讨厌侮辱,伙计。我不知道答案,但我希望你能找到答案。
  • 如果有帮助,stackoverflow.com/questions/638376/… 是在 C# 中检查这一点的一种方式。我还没有找到 C 版本。
  • 或者我可以使用 modff() 检查小数部分是否为零?然后检查整数部分的范围是否在long long的范围内,如果是则强制转换。
  • 如果您只担心将它们存储为稍后检索的文本并实现了快速整数转换,那么您还应该能够实现快速浮点作为“基数到指数”转换只比转换 2 个整数稍微复杂一点。

标签: c casting integer double printf


【解决方案1】:

也许一些老式的定点数学可以帮助你。如果您将双精度值转换为定点值,您仍然可以获得小数精度,并且转换为字符串就像使用整数一样简单,只需添加一个移位函数。

另一个想法是推出自己的 snprintf() 函数。许多 FPU 单元本身支持从 double 到 int 的转换,因此应该快如闪电。将其转换为字符串也很简单。

只是给你一些随机的想法。

【讨论】:

  • 谢谢 Michael,哇,FPU 支持这种转换吗?这确实是一个好消息。分离零件并独立打印它们的技巧也很酷。谢谢,这很有帮助。
【解决方案2】:

这样做的问题是比较不会按照您的预期进行。仅仅因为一个浮点值小于另一个并不意味着它作为整数的表示将小于另一个。另外,我看到您比较(以前的)双精度值之一是否相等。由于低位的舍入和表示错误,您几乎从不想要这样做。

如果您只是在寻找某种密钥来执行诸如散列之类的操作,那么它可能会很好。如果您真的关心哪些值真正具有更大或更小的价值,那是个坏主意。

【讨论】:

  • 是的,我也注意到相等的双重比较。它可能是令人讨厌的难以发现问题的根源。它会在 100 次中工作 99 次。
  • 您好 Ted,如果您检查代码,我实际上总是在比较双打,但是在两步转换后获得了一个双打。所以这个想法是,如果双重仍然匹配,那么它能够通过那个“过滤器”而不会丢失信息。所以可以打印它的长表示而不是它本身。所以这样做的原因只是为了在双->字符串转换阶段获得速度。
  • 阅读有关比较双打的信息,即使数字在表示方面完全相同,这也不起作用?我知道如果两个数字是从字符串表示或其他数学处理生成的,那么比较可能会失败,而数字仍然是相同的,但在我的具体情况下,我可以毫无问题地得到一个假阴性,因为我'将求助于使用 snprintf() 的健全代码。如果我正确理解这个问题,比较双打的问题是误报,而不是误报。
【解决方案3】:

只要 x 在 long long 的范围内,我认为转换没有问题。也许您应该查看 modf() 函数,该函数将双精度数分为整数部分和小数部分。然后,您可以添加对 (double)LLONG_MIN 和 (double)LLONG_MAX 的检查以确保整体部分。虽然双精度可能存在困难。

但是在做任何事情之前,您是否通过测量其性能确定它实际上是一个瓶颈?整数值的百分比是否足够高以至于它真的会有所作为?

【讨论】:

  • 非常感谢,这已经实现了,并且在保存具有许多双打的 DB 时速度提高了 2 倍。在 snprintf() 函数显示非常慢的分析会话之后开始优化...
【解决方案4】:

您的测试非常好(假设此时您已经分别处理了无穷大和 NAN) - 这可能是您真正确实想要比较浮点数是否相等的极少数情况之一。它不会调用未定义的行为 - 即使x 超出了long long 的范围,您也只会得到一个“实现定义的结果”,这在这里是可以的。

唯一美中不足的是,负零最终会变成正零(因为负零比较等于正零)。

【讨论】:

  • 感谢 caf,我最近几个小时研究了一点浮点数表示。我也认为这是安全的。是的,代码中已经对 Nan 和 Infinity 进行了明确的检查,所以应该是安全的。只是为了让事情看起来更安全一些,我添加了#if 来检查 mantis 和 long long 精度是否匹配,因此只有在 double 具有至少 52 位精度时才会编译代码,然后有一个显式测试来检查如果 double 在 long long 不会溢出的范围内(并且此测试是在 52 位范围内完成的,因此我们保证它会起作用)。谢谢回复。
  • 范围测试是不必要的——如果你愿意,你甚至可以用char 来代替。当double 超出范围时,您得到的实现定义的结果在转换回double 时根本不会比较相等(C 中的“溢出”只发生在计算上,而不是转换上)。
猜你喜欢
  • 2014-04-18
  • 1970-01-01
  • 2016-07-23
  • 2015-09-13
  • 2014-11-10
  • 2011-07-07
  • 2016-01-01
  • 2013-05-13
  • 2016-03-13
相关资源
最近更新 更多