Last active
May 21, 2016 13:58
-
-
Save kwasmich/dc24ebad6406abb103aba4a2b42e80a9 to your computer and use it in GitHub Desktop.
Compiler cast "nan"s to "inf"s
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// main.c | |
// IEEE754 | |
// | |
// Created by Michael Kwasnicki on 20.05.16. | |
// Copyright © 2016 kwasi-ich.de. All rights reserved. | |
// | |
#include <stdint.h> | |
#include <stdio.h> | |
typedef union { | |
struct { | |
uint64_t m : 52; | |
uint64_t e : 11; | |
uint64_t s : 1; | |
}; | |
uint64_t b; | |
double d; | |
} binary64; | |
typedef union { | |
struct { | |
uint32_t m : 23; | |
uint32_t e : 8; | |
uint32_t s : 1; | |
}; | |
uint32_t b; | |
float d; | |
} binary32; | |
float fp64_to_fp32(const double in_F) { | |
binary64 fp64 = {.d = in_F}; | |
binary32 fp32; | |
fp32.s = fp64.s; | |
int16_t e = fp64.e - 1023; | |
if (fp64.e == 0x000) { | |
// denorm | |
fp32.e = 0x00; | |
fp32.m = 0x00; | |
} else if (fp64.e == 0x7FF) { | |
fp32.e = 0xFF; | |
if (fp64.m == 0x0ULL) { | |
fp32.m = 0x00; | |
} else { | |
fp32.m = (fp64.m >> 29); | |
// clang | |
// different behavior when compiling with -O0 and -O3 when converting NaN | |
if (fp32.m == 0) { | |
fp32.m |= 0x400000; // set the highest mantissa bit to signal NaN | |
} | |
} | |
} else if (e < -150) { | |
fp32.e = 0x00; | |
fp32.m = 0x00; | |
} else if (e < -149) { | |
fp32.e = 0x00; | |
fp32.m = 0x00000001; | |
} else if (e < -126) { | |
// denorm | |
fp32.e = 0x00; | |
fp32.m = 0x00; | |
int shift = -126 - e; | |
if (shift == 1) { | |
uint32_t m = ((fp64.m >> 30) | 0x400000); | |
fp32.m = m; | |
} | |
if (shift >= 2) { | |
uint32_t m = ((fp64.m >> 30) | 0x400000) >> (shift - 2); | |
fp32.m = m >> 1; | |
if (m & 1) { | |
fp32.m++; | |
} | |
} | |
} else if (e > 127) { | |
fp32.e = 0xFF; | |
fp32.m = 0x00; | |
} else { | |
fp32.e = e + 127; | |
fp32.m = fp64.m >> 29; | |
// mantissa rounding | |
uint32_t m0 = fp32.m; | |
if ((fp64.m & 0x1FFFFFFF) > 0x10000000) { | |
fp32.m++; | |
} else if (((fp64.m & 0x1FFFFFFF) == 0x10000000) && (fp32.m & 1)) { | |
fp32.m++; | |
} | |
// in case of a mantissa overflow -> increase exponent | |
if (m0 > fp32.m) { | |
fp32.e++; | |
} | |
} | |
return fp32.d; | |
} | |
static inline void test2() { | |
binary64 fp64 = {.b = 0x7FF0000011111111ULL}; | |
binary32 fp32; | |
binary32 fp32_b; | |
fp32.d = (float)fp64.d; // cast to float | |
fp32_b.d = fp64_to_fp32(fp64.d); // hand cast to float | |
// print binary representation of the double | |
printf(" s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm\n"); | |
printf(" ------------------------------------------------------------------\n"); | |
printf("double: "); | |
for (int i = 0; i < 64; i++) { | |
if (i == 1 || i == 12) { | |
putchar(' '); | |
} | |
printf("%llu", (fp64.b >> (63 - i)) & 1); | |
} | |
printf(" %f\n", fp64.d); | |
printf("cast float: "); | |
// print binary representation of the float | |
for (int i = 0; i < 32; i++) { | |
if (i == 1 || i == 9) { | |
putchar(' '); | |
} | |
printf("%u", (fp32.b >> (31 - i)) & 1); | |
} | |
printf(" %f\n", fp32.d); | |
printf("my float: "); | |
// print binary representation of the hand transformed float | |
for (int i = 0; i < 32; i++) { | |
if (i == 1 || i == 9) { | |
putchar(' '); | |
} | |
printf("%u", (fp32_b.b >> (31 - i)) & 1); | |
} | |
printf(" %f\n\n\n", fp32_b.d); | |
} | |
static inline void test(const uint64_t in_FP64) { | |
binary64 fp64 = {.b = in_FP64}; // this is a NaN | |
binary32 fp32; | |
binary32 fp32_b; | |
fp32.d = (float)fp64.d; // cast to float | |
fp32_b.d = fp64_to_fp32(fp64.d); // hand cast to float | |
// print binary representation of the double | |
printf(" s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm\n"); | |
printf(" ------------------------------------------------------------------\n"); | |
printf("double: "); | |
for (int i = 0; i < 64; i++) { | |
if (i == 1 || i == 12) { | |
putchar(' '); | |
} | |
printf("%llu", (fp64.b >> (63 - i)) & 1); | |
} | |
printf(" %f\n", fp64.d); | |
printf("cast float: "); | |
// print binary representation of the float | |
for (int i = 0; i < 32; i++) { | |
if (i == 1 || i == 9) { | |
putchar(' '); | |
} | |
printf("%u", (fp32.b >> (31 - i)) & 1); | |
} | |
printf(" %f\n", fp32.d); | |
printf("my float: "); | |
// print binary representation of the hand transformed float | |
for (int i = 0; i < 32; i++) { | |
if (i == 1 || i == 9) { | |
putchar(' '); | |
} | |
printf("%u", (fp32_b.b >> (31 - i)) & 1); | |
} | |
printf(" %f\n\n\n", fp32_b.d); | |
} | |
int main(int argc, const char * argv[]) { | |
test(0x7FF8000000000000ULL); | |
test(0x7FF1111111111111ULL); | |
test(0x7FF0000011111111ULL); | |
test(0x7FF0000000000001ULL); | |
test2(); | |
} | |
// clang --version | |
// | |
// Apple LLVM version 7.3.0 (clang-703.0.31) | |
// Target: x86_64-apple-darwin15.4.0 | |
// Thread model: posix | |
// InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin | |
// clang -O0 main.c && ./a.out | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 1000000000000000000000000000000000000000000000000000 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0001000100010001000100010001000100010001000100010001 nan | |
// cast float: 0 11111111 10010001000100010001000 nan | |
// my float: 0 11111111 00010001000100010001000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 10000000000000000000000 nan <-- by value: OK | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000000000000000000000000000000001 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 10000000000000000000000 nan <-- constant: OK | |
// my float: 0 11111111 10000000000000000000000 nan | |
// clang -O3 main.c && ./a.out | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 1000000000000000000000000000000000000000000000000000 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0001000100010001000100010001000100010001000100010001 nan | |
// cast float: 0 11111111 10010001000100010001000 nan | |
// my float: 0 11111111 00010001000100010001000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 10000000000000000000000 nan <-- by value: OK | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000000000000000000000000000000001 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- constant: ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// gcc --version | |
// | |
// gcc (Raspbian 4.9.2-10) 4.9.2 | |
// Copyright (C) 2014 Free Software Foundation, Inc. | |
// This is free software; see the source for copying conditions. There is NO | |
// warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
// gcc -std=c11 -O0 main.c && ./a.out | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 1000000000000000000000000000000000000000000000000000 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0001000100010001000100010001000100010001000100010001 nan | |
// cast float: 0 11111111 00010001000100010001000 nan | |
// my float: 0 11111111 00010001000100010001000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000000000000000000000000000000001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// gcc -std=c11 -O3 main.c && ./a.out | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 1000000000000000000000000000000000000000000000000000 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0001000100010001000100010001000100010001000100010001 nan | |
// cast float: 0 11111111 00010001000100010001000 nan | |
// my float: 0 11111111 00010001000100010001000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000000000000000000000000000000001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 01000000000000000000000 nan <-- constant: somehow OK | |
// my float: 0 11111111 10000000000000000000000 nan | |
// clang --version | |
// | |
// Raspbian clang version 3.5.0-10+rpi1 (tags/RELEASE_350/final) (based on LLVM 3.5.0) | |
// Target: arm-unknown-linux-gnueabihf | |
// Thread model: posix | |
// clang -O0 main.c && ./a.out | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 1000000000000000000000000000000000000000000000000000 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0001000100010001000100010001000100010001000100010001 nan | |
// cast float: 0 11111111 00010001000100010001000 nan | |
// my float: 0 11111111 00010001000100010001000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000000000000000000000000000000001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// clang -O3 main.c && ./a.out | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 1000000000000000000000000000000000000000000000000000 nan | |
// cast float: 0 11111111 10000000000000000000000 nan | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0001000100010001000100010001000100010001000100010001 nan | |
// cast float: 0 11111111 00010001000100010001000 nan | |
// my float: 0 11111111 00010001000100010001000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000000000000000000000000000000001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan | |
// | |
// | |
// s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm | |
// ------------------------------------------------------------------ | |
// double: 0 11111111111 0000000000000000000000010001000100010001000100010001 nan | |
// cast float: 0 11111111 00000000000000000000000 inf <-- ERROR | |
// my float: 0 11111111 10000000000000000000000 nan |
I tested it on my Ubuntu Mate 16.04. GCC 5.3.1 does not expose this bug, but clang 3.8.0 is still affected when compiling a 32bit version on my 64bit linux.
gcc --version
gcc (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
clang --version
clang version 3.8.0-2ubuntu3 (tags/RELEASE_380/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
All compilers I have my hands on expose this issue at least at a specific optimization level.