double to unsigned char cast

wrt https://github.com/mono/mono/commit/fb91fce5d339bb9ffe507588f5bc1d8d6f244d9b

This doesn’t seem right to me.
Both of these should have the and 0xff.

$ cat /s/1.c
unsigned char f1 (double i)
{
return (unsigned char)i;
}

unsigned char f2 (double i)
{
return (unsigned char)(int)i;
}

$ clang --version

Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

$ clang -arch arm64 -O2 -S -c /s/1.c && more 1.s

_f1:

fcvtzs w0, d0

ret

_f2:
fcvtzs w8, d0

and w0, w8, #0xff
ret

Hi Jay,

wrt
https://github.com/mono/mono/commit/fb91fce5d339bb9ffe507588f5bc1d8d6f244d9b

This doesn't seem right to me.

Nevertheless, it's what the standard says. C99 6.3.1.4: 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.

By definition any double you're allowed to convert to an unsigned char
doesn't need the "and".

Cheers.

Tim.

This seems disappointing.

Casting any value to an integral type should yield a value within the range of that integral type.

The value can be anything, but it should be within range.

i.e. it should be as if I anded it with (type)~(type)0.

i.e. int i = (signed char)x;

assert(i >= -128 && i <= 127);

int i = (unsigned char)x;

assert(i >= 0 && i <= 255);

seem very reasonable, assuming there is no “trap” before the assert.

  • Jay

This seems disappointing.

Casting any value to an integral type should yield a value within the range
of that integral type.

As Tim pointed out, that's not what the standard says though.

Trunk Clang (I'm not sure what version your Xcode Clang is based on)
has a flag to disable this optimization:
-fno-strict-float-cast-overflow

- Hans

I’d also note that -fsanitize=undefined will detect the error at runtime.

I acknowledge that the type system is full of holes (“C++ is not safe”) and the standard gives you leeway, but I’ve never before witnessed a cast to an integer type not yielding some value in range for that integer type. And even clang does provide such here, on x86 and amd64.

I have to also read our spec to see what we are supposed to return here also.

The “workaround” of casting to int first doesn’t seem any better defined than casting directly to char, given a double that doesn’t fit in int either.

  • Jay