C语言 在C中*有效地*提取double的小数部分
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5589383/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Extract fractional part of double *efficiently* in C
提问by Jeremy Salwen
I'm looking to take an IEEE double and remove any integer part of it in the most efficient manner possible.
我正在寻找一个 IEEE double 并以最有效的方式删除它的任何整数部分。
I want
我想要
1035 ->0
1045.23->0.23
253e-23=253e-23
I do not care about properly handling denormals, infinities, or NaNs. I do not mind bit twiddling, as I know I am working with IEEE doubles, so it should work across machines.
我不关心正确处理非正规数、无穷大或 NaN。我不介意有点麻烦,因为我知道我正在使用 IEEE 双打,所以它应该可以跨机器工作。
Branchless code would be much preferred.
无分支代码会更受欢迎。
My first thought is (in pseudo code)
我的第一个想法是(在伪代码中)
char exp=d.exponent;
(set the last bit of the exponent to 1)
d<<=exp*(exp>0);
(& mask the last 52 bits of d)
(shift d left until the last bit of the exponent is zero, decrementing exp each time)
d.exponent=exp;
But the problem is that I can't think of an efficient way to shift d left until the last bit of the exponent is zero, plus it seems it would need to output zero if all of the last bits weren't set. This seems to be related to the base 2 logarithm problem.
但问题是我想不出一种有效的方法将 d 左移,直到指数的最后一位为零,而且如果所有最后一位都没有设置,它似乎需要输出零。这似乎与以 2 为底的对数问题有关。
Help with this algorithm or any better ones would be much appreciated.
对这个算法或任何更好的算法的帮助将不胜感激。
I should probably note that the reason I want branchless code is because I want it to efficiently vectorize.
我可能应该注意到,我想要无分支代码的原因是因为我希望它能够有效地矢量化。
回答by Mark Elliot
How about something simple?
简单的东西怎么样?
double fraction = whole - ((long)whole);
This just subtracts the integer portion of the double from the value itself, the remainder should be the fractional component. It's possible, of course, this could have some representation issues.
这只是从值本身中减去 double 的整数部分,余数应该是小数部分。当然,这可能会有一些表示问题。
回答by Stephen Canon
The optimal implementation depends entirely on the target architecture.
最佳实现完全取决于目标架构。
On recent Intel processors, this can be achieved with two instructions: roundsdand subsd, but that can't be expressed in portableC code.
在最新的 Intel 处理器上,这可以通过两条指令实现:roundsdand subsd,但这不能用可移植的C 代码表示。
On some processors, the fastest way to do this is with integer operations on the floating point representation. Early Atom and many ARM CPUs come to mind.
在某些处理器上,最快的方法是对浮点表示进行整数运算。想到了早期的 Atom 和许多 ARM CPU。
On some other processors, the fastest thing is to cast to integer and back, then subtract, branching to protect large values.
在其他一些处理器上,最快的方法是转换为整数并返回,然后减去,分支以保护大值。
If you're going to be handling lots of values, you can set the rounding mode to round-to-zero, then add and subtract +/-2^52 to the number truncated to integer, then subtract from the original value to get the fraction. If you don't have SSE4.1, but do have an otherwise modern Intel CPU and want to vectorize, this is typically the best you can do. It only makes sense if you have many values to process, however, because changing the rounding mode is somewhat expensive.
如果您要处理大量值,可以将舍入模式设置为舍入为零,然后将 +/-2^52 加减到截断为整数的数字,然后从原始值中减去以获得分数。如果您没有 SSE4.1,但有其他现代 Intel CPU 并且想要矢量化,这通常是您能做的最好的事情。但是,只有当您有许多值要处理时才有意义,因为更改舍入模式有些昂贵。
On other architectures, other implementations are optimal. In general, it doesn't make sense to talk about "efficiency" of C programs; only the efficiency of a specific implementation on a specific architecture.
在其他架构上,其他实现是最佳的。一般来说,谈论C程序的“效率”是没有意义的;只有特定架构上特定实现的效率。
回答by user541686
#include <math.h>
double fraction = fmod(d, 1.0);
回答by Mathieu Rodic
Proposal
提议
The function remaindercomputes the remainder, but not the integer part like modfdoes:
该函数remainder计算余数,但不像整数部分那样计算modf:
#include <math.h>
double fracpart(double input)
{
return remainder(input, 1.);
}
This is the most efficient (and portable) way, as it doesn't compute unnecessary values to do the job (cf. modf, (long), fmod, etc.)
这是最有效的(和便携式)的方式,因为它没有计算不必要的值来完成这项工作(参见modf,(long),fmod,等)
Benchmark
基准
As Mattew suggested in the comments, I wrote some benchmark codeto compare this solution to all the other ones offered on this page.
正如 Mattew 在评论中建议的那样,我编写了一些基准代码来将此解决方案与此页面上提供的所有其他解决方案进行比较。
Please find below the time measurements for 65536 computations (compiled with Clang with optimizations turned off):
请在下面找到 65536 次计算的时间测量(使用 Clang 编译并关闭优化):
method 1 took 0.002389 seconds (using remainder)
method 2 took 0.000193 seconds (casting to long)
method 3 took 0.000209 seconds (using floor)
method 4 took 0.000257 seconds (using modf)
method 5 took 0.010178 seconds (using fmod)
Again with Clang, this time using the -O3flag:
再次使用 Clang,这次使用-O3标志:
method 1 took 0.002222 seconds (using remainder)
method 2 took 0.000000 seconds (casting to long)
method 3 took 0.000000 seconds (using floor)
method 4 took 0.000223 seconds (using modf)
method 5 took 0.010131 seconds (using fmod)
Turns out the simplest solution seems to give the best results on most platforms, and the specific methods to perform that task (fmod, modf, remainder) are actually super-slow!
事实证明,最简单的解决方案似乎在大多数平台上都能提供最佳结果,而执行该任务的特定方法 ( fmod, modf, remainder) 实际上非常慢!
回答by Graham Asher
Some profiling and experimentation using C++ in Microsoft Visual Studio 2015 indicates that the best method for positive numbers is:
在 Microsoft Visual Studio 2015 中使用 C++ 的一些分析和实验表明,正数的最佳方法是:
double n;
// ...
double fractional_part = n - floor(n);
It's faster than modf, and, as has already been mentioned, the remainder function rounds to the nearest integer, and therefore isn't of use.
它比 快modf,而且,正如已经提到的,余数函数四舍五入到最接近的整数,因此没有用。
回答by user1555418
Standard library function modf solves this problem quite neatly.
标准库函数 modf 非常巧妙地解决了这个问题。
#include <math.h>
/*...*/
double somenumber;
double integralPart;
double fractionalPart = modf(somenumber, &integralPart);
This should do what you have asked, is portable, and reasonably efficient.
这应该可以满足您的要求,可移植且效率合理。
An undocumented detail is whether the second argument could be NULL and then avoid the integral part temporary, which would be desirable in uses such as that you have described.
一个未记录的细节是第二个参数是否可以为 NULL,然后避免临时的组成部分,这在您描述的用途中是可取的。
Unfortunately it seams many implementations do not support NULL for the second argument, so will have to use a temporary whether or not you use this value.
不幸的是,许多实现不支持 NULL 作为第二个参数,因此无论您是否使用此值,都必须使用临时值。

