简体   繁体   English

将浮点数分解为其整数部分和小数部分

[英]Decompose a floating-point number into its integral and fractional part

I am implementing a fractional delay line algorithm.我正在实施分数延迟线算法。 One of the tasks involved is the decomposition of a floating-point value into its integral and fractional part.所涉及的任务之一是将浮点值分解为其整数部分和小数部分。 I know there are a lot of posts about this topic on SO and I probably read most of them.我知道在 SO 上有很多关于这个主题的帖子,我可能阅读了其中的大部分。 However I haven't found one post that deals with the specifics of this scenario.但是,我还没有找到一篇处理这种情况的具体情况的帖子。

  • The algorithm must be using 64-bit floating-point values.该算法必须使用 64 位浮点值。

  • Input floating-point values are guaranteed to always be positive.输入浮点值保证始终为正。 (delay times cannot be negative) (延迟时间不能为负)

  • The output integer part has to be represented by an integer datatype.输出整数部分必须由整数数据类型表示。

  • The integer datatype must have enough bits so that the double-to-integer conversion occurs without the risk of overflowing.整数数据类型必须有足够的位,以便双精度到整数的转换发生而没有溢出的风险。

  • Issues resulting from floating-point values lacking an exact internal representation must be avoided.必须避免因浮点值缺乏精确的内部表示而导致的问题。 (ie 9223372036854775809.0 might be internally represented as 9223372036854775808.9999998 and when cast to integer it erroneously becomes 9223372036854775808) (即 9223372036854775809.0 可能在内部表示为 9223372036854775808.9999998,当转换为整数时,它错误地变为 92233372036854775808)

  • The implementation should work regardless of rounding mode or compiler optimization settings.无论舍入模式或编译器优化设置如何,实现都应该有效。

So I wrote a function:所以我写了一个函数:

 double my_modf(double x, int64_t *intPartOut);

As you can see its signature is similar to the modf() function in the C standard library.如您所见,它的签名类似于 C 标准库中的 modf() 函数。

The first implementation I came up with is:我想出的第一个实现是:

double my_modf(double x, int64_t *intPartOut)
{
    double y;
    double fracPart = modf(x, &y);
    *intPartOut = (int64_t)y;
    return fracPart;
}

I have also been experimenting with this implementation which - at least on my machine - runs faster than the previous, however I doubt its robustness.我也一直在试验这个实现 - 至少在我的机器上 - 运行速度比以前快,但是我怀疑它的稳健性。

double my_modf(double x, int64_t *intPartOut)
{
    int64_t y = (int64_t)x;
    *intPartOut = y;
    return x - y;
}

...and this is my latest attempt: ...这是我最近的尝试:

double my_modf(double x, int64_t *intPartOut)
{
    *intPartOut = llround(x);
    return x - floor(x);
}

I can't make up my mind as to which implementation would be best to use, or if there are other implementations that I haven't considered that would better accomplish the following goals.我无法决定最好使用哪种实现,或者是否还有其他我没有考虑过的实现可以更好地实现以下目标。 I am looking for the (1) most robust and (2) most efficient implementation to decompose a floating-point number into its integral and fractional part, keeping into consideration the list of points mentioned above.我正在寻找 (1) 最健壮和 (2) 最有效的实现来将浮点数分解为其整数和小数部分,同时考虑上面提到的点列表。

Updated answer after reading your comment below.阅读下面的评论后更新了答案

If you are already sure the values are within [0, 2^63-1] then a simple cast will be faster than llround() since this function may also check for overflow (on my system, the manual page states so, however the C standard does not require it).如果您已经确定这些值在 [0, 2^63-1] 内,那么简单的llround()将比llround()更快,因为此函数还可以检查溢出(在我的系统上,手册页如此说明,但是C 标准不需要它)。

On my machine for example (x86-64 Nehalem) casting is a single instruction ( cvttsd2si ) and llround() is obviously more than one.例如,在我的机器上 (x86-64 Nehalem) 铸造是一条指令 ( cvttsd2si ),而llround()显然不止一个。

Am I guaranteed to get the right result with a simple cast (truncation) or is it safer to round?我能保证通过简单的转换(截断)得到正确的结果还是舍入更安全?

Depends on what you mean with "right".取决于你对“正确”的意思。 If the value in the double can be correctly represented by an int64_t , then sure you're going to get exactly the same value.如果double的值可以由int64_t正确表示,那么确保您将获得完全相同的值。 However, if the value cannot be precisely represented by the double then truncation is automatically performed when casting.但是,如果该值不能由double精确表示,则在强制转换时会自动执行截断。 If you want to round the value in a different way that's another story and you'll have to use one of ceil() , floor() or round() .如果您想以不同的方式舍入该值,那就是另一回事了,您必须使用ceil()floor()round()

If you also are sure that no values will be +/- Infinity or NaN (and in that case you can use -Ofast ), then your second implementation should be the fastest if you want truncation, while the third should be the fastest if you want to floor() the value.如果您还确定没有 +/- Infinity 或 NaN 值(在这种情况下您可以使用-Ofast ),那么如果您想要截断,那么您的第二个实现应该是最快的,而如果您想要截断,则第三个应该是最快的想要floor()值。

Given that the maximum value of the integer part of the floating-point input x is 2 63 −1 and that x is non-negative, then both:假设浮点输入x的整数部分的最大值是 2 63 -1 并且x是非负数,那么两者:

double my_modf(double x, int64_t *intPartOut)
{
    double y;
    double fracPart = modf(x, &y);
    *intPartOut = y;
    return fracPart;
}

and:和:

double my_modf(double x, int64_t *intPartOut)
{
    int64_t y = x;
    *intPartOut = y;
    return x - y;
}

will correctly return the integer part in intPartOut and the fractional part in the return value regardless of rounding mode.无论舍入模式如何,都将正确返回intPartOut的整数部分和返回值中的小数部分。

GCC 9.2 for x86_64 does a better job optimizing the latter version , and so does Apple Clang 11.0.0. GCC 9.2 for x86_64 在优化后一个版本方面做得更好,Apple Clang 11.0.0 也是如此。

llround will not return the integer part as desired because it rounds to the nearest integer rather than truncating. llround不会根据需要返回整数部分,因为它四舍五入到最接近的整数而不是截断。

Issues about x containing errors cannot be resolved with the information provided in the question.问题中提供的信息无法解决有关x包含错误的问题。 The routines shown above have no error;上面显示的例程没有错误; they return exactly the integer and fractional parts of their input.它们准确地返回输入的整数和小数部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM