2

Is there anything wrong (potential undefined behavior) with the following code, were a float value gets assigned to a long int ?

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

1 Answer 1

5

The conversion would be done at compile time. You might get a warning from the compiler, but there's nothing actually 'wrong' with the code, for all it is very aconventional to initialize an integer with a floating point constant.


The initialization I gave was just to keep it simple. In the code it will be something like:

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

Nanoseconds into tv_nsec; interesting, but there shouldn't be any major issues. A double in practice has enough precision that you won't run into much trouble, though you might occasionally get a different value from what you expected because the fraction is truncated down when you didn't expect it. It might be worth checking that; it depends how crucial it would be to you.

I did a quick program to see whether there might be a problem. Bear in mind that this is run time calculation and not compile time calculation, but the results might be similar for you:

#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; } 

It prints out values where the fractional value does not match the integer value. A small section of some voluminous output is:

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 

While it is by no means every value that has problems, I record (with wc -l) 17,075,957 out of 1,000,000,000 values (or about 1.7% of the values) with a discrepancy. That was with GCC 4.1.2 on Mac OS X 10.7.4 (as supplied by Apple). I got the same result with GCC 4.7.0. It took about 30 seconds to generate the data.

One of my favourite quotes from 'The Elements of Programming Style' by Kernighan and Plauger is:

  • A wise programmer once said, 'Floating point numbers are like little piles of sand. Every time you move one, you lose a little sand and pick up a little dirt.'

This demonstrates the issue quite well.


Note that a trivial change reduces the error rate to 0:

long r = d * 1E9 + 0.5; 

Maybe you should use a macro:

#define NANOSECONDS(x) ((x) * 1E9 + 0.5) long r = NANOSECONDS(d); 

You can use a smaller additive constant; 0.1 also reduced the error rate to 0.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. The initialization I gave was just to keep it simple. In the code it will something like.. struct timespec t; t.tv_nsec = (some float value < 1) * 1E9.
I've cancelled my original 'you should be OK' comment and added an expanded answer with a 'well, you are going to have to decide whether it is OK for you' caveat.
Thanks. for my purpose this caveat is ok.. let me also update my question for the benefit of others.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.