On 2/13/2012 6:16 AM, Ben Bacarisse wrote:
> Alex Vinokur<> writes:
>
> Talking from the C perspective here...
>
>> unsigned long a = -1;
>> double b = a;
>> unsigned long c = b;
>>
>> Model 32-bits: a == c
>> Model 64-bits: a != c
>>
>> It is not a compiler bug for 64-bits.
>
> Quite. Both outcomes are permitted.
>
>> Is it a design bug of C/C++ languge for 64-bits?
>
> 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...