On 2017-07-05 19:00, Max Reitz wrote: > On 2017-07-05 18:29, Eric Blake wrote: >> On 07/05/2017 11:22 AM, Max Reitz wrote: >> >>>>>> return (double)x == x && x == y; >>>>> >>>>> Yes, that would do, too; and spares me of having to think about how well >>>>> comparing an arbitrary double to UINT64_MAX actually works. :-) >>>> >>>> On second thought, this won't do, because (double)x == x is always true >>>> if x is an integer (because this will implicitly cast the second x to a >>>> double, too). However, (uint64_t)(double)x == x should work. >>> >>> Hm, well, the nice thing with this is that (double)UINT64_MAX is >>> actually UINT64_MAX + 1, and now (uint64_t)(UINT64_MAX + 1) is >>> undefined... Urgs. >> >> (uint64_t)(UINT64_MAX + 1) is well-defined - it is 0. >> >> (Adding in unsigned integers is always well-defined - it wraps around on >> mathematical overflow modulo the integer size. You're thinking of >> overflow addition on signed integers, which is indeed undefined) > > It's not. See the standard:
Or, well, yes, it is in this case, but I meant literally UINT64_MAX + 1,
not the uint64_t value. I meant the natural number 2^64.
Because the issue is that (double)UINT64_MAX will (or may, depending on
the environment and such) give us 2.0^64 == 0x1p64.
And this is where the quotation I gave below comes in. When casting an
integer to a double we may end up with a value that is not representable
in the original integer type, so we cannot easily cast it back.
Max
> When a finite value of real floating type is converted to an integer
> type other than _Bool, the fractional part is discarded (i.e., the value
> is truncated toward zero). If the value of the integral part cannot be
> represented by the integer type, the behavior is undefined. [61]
>
> [61] The remaindering operation performed when a value of integer type
> is converted to unsigned type need not be performed when a value of real
> floating type is converted to unsigned type. Thus, the range of portable
> real floating values is (−1, Utype_MAX+1).
>
> See for yourself:
>
> printf("%i\n", (uint64_t)(double)UINT64_MAX == UINT64_MAX);
>
> This yields 1 with gcc and -O3.
>
>>>
>>> So I guess one thing that isn't very obvious but that should *always*
>>> work (and is always well-defined) is this:
>>>
>>> For uint64_t: y < 0x1p64 && (uint64_t)y == x
>>>
>>> For int64_t: y >= -0x1p63 && y < 0x1p63 && (int64_t)y == x
>>
>> That's harder to read, compared to the double-cast method which is
>> well-defined after all.
>>
>>>
>>> I hope. :-/
>>>
>>> (But finally a chance to use binary exponents! Yay!)
>>
>> Justifying the use of binary exponents is going to be harder than that,
>> and would need a really good comment in the code, compared to just using
>> a safe double-cast.
>
> It's not safe.
>
> Max
>
signature.asc
Description: OpenPGP digital signature
