2

以下代码是否有任何问题(潜在的未定义行为),是否将浮点值分配给 long int ?

struct timespec t; 
t.tv_nsec = (some float value < 1) * 1E9
4

1 回答 1

5

转换将在编译时完成。您可能会收到编译器的警告,但代码实际上并没有什么“错误”,因为使用浮点常量初始化整数是非常传统的做法。


我给出的初始化只是为了保持简单。在代码中,它将类似于:

struct timespec t; t.tv_nsec = (some float value < 1) * 1E9;

纳秒到tv_nsec; 有趣,但不应该有任何重大问题。实际上,double 具有足够的精度,您不会遇到太多麻烦,尽管您可能偶尔会得到与预期不同的值,因为分数在您不期望的情况下被截断。可能值得检查一下;这取决于它对您的重要性。

我做了一个快速程序,看看是否有问题。请记住,这是运行时计算而不是编译时计算,但结果可能与您相似:

#include <stdio.h>

int main(void)
{
    for (long l = 0; l < 1000000000; l++)
    {
        double d = l / 1.0E9;
        long r = d * 1E9;
        if (r != l)
            printf("%.9ld: %12.9f != %ld\n", l, d, r);
    }
    return 0;
}

它打印出小数值与整数值不匹配的值。一些大量输出的一小部分是:

031890838:  0.031890838 != 31890837
031890839:  0.031890839 != 31890838
031890840:  0.031890840 != 31890839
031890851:  0.031890851 != 31890850
031890852:  0.031890852 != 31890851
031890853:  0.031890853 != 31890852
031890864:  0.031890864 != 31890863
031890865:  0.031890865 != 31890864
031890866:  0.031890866 != 31890865
031890877:  0.031890877 != 31890876
031890878:  0.031890878 != 31890877
031890879:  0.031890879 != 31890878
031890890:  0.031890890 != 31890889

虽然绝不是每个值都有问题,但我wc -l在 1,000,000,000 个值(或大约 1.7% 的值)中记录了(用 )17,075,957 个存在差异。那是在 Mac OS X 10.7.4(由 Apple 提供)上使用 GCC 4.1.2。我使用 GCC 4.7.0 得到了相同的结果。生成数据大约需要 30 秒。

Kernighan 和 Plauger 的“编程风格的要素”中我最喜欢的一句话是:

  • 一位聪明的程序员曾经说过,'浮点数就像一小堆沙子。每次你移动一个,你会失去一点沙子并捡起一点泥土。

这很好地说明了这个问题。


请注意,一个微不足道的更改会将错误率降低到 0:

long r = d * 1E9 + 0.5;

也许你应该使用宏:

#define NANOSECONDS(x) ((x) * 1E9 + 0.5)

long r = NANOSECONDS(d);

您可以使用较小的附加常数;0.1 也将错误率降低到 0。

于 2012-05-24T02:51:27.377 回答