Talking from the C perspective here...
Quite. Both outcomes are permitted.
No.
Your use of "64-bits" is a little confusing. Not all 64-bit systems
have 64 bit unsigned longs which is, I think, what you are talking
about.
On systems with 64-bit longs and standard 56-bit mantissa doubles, you
can not represent ULONG_MAX (the value of 'a' in the above code) exactly
in a double. C mandates that you get one of the two nearest
representable values, but it wont be exact. When the conversion goes
the other way the result can be undefined (if the floating point values
was rounded up to a value larger that ULONG_MAX), but, even if the
double has a value in the range of unsigned long, it will not longer
equal ULONG_MAX.
I said "no" to it being an error in the design of the language because
solving it would impose the kind of burden on implementations that C
rejects. C is designed to use native machine types wherever possible.
yeah.
also, even though double has more bits than, say, an integer, does not
mean it will reliably encode an integer's value (it can do so in theory,
and will most often do so, but whether or not it will actually always do
so is more "up for grabs").
it is much less reliable with float (since float has only about 23 bits
to hold an integer's value, vs the 52 bits or so in double).
hence, float can't reliably hold the entire integer range, and double
can't reliably hold the entire long-long range (the size of long is
target specific, even for the same CPU architecture and operating mode,
it may still vary between the OS and compiler in use).
the most common behavior seems to be:
int -> float or double, may produce a value slightly below the integer;
float or double to int, will generally truncate the value, yielding the
integer representation as rounded towards 0.
the result then is a tendency for an int->double->int conversion to have
a small chance to drop the integer value towards 0 (why? I don't know
exactly, but I have observed it before).
one can counteract this by fudging the value with a small epsilon prior
to converting back into an integer.
say, for example (untested, from memory):
(v>=0)?((int)(v+0.0001))
(int)(v-0.0001));
can't say it will always work, but similar seems to work fairly well IME
(at least on generic x86 based targets).
or such...