Skip to content

Instantly share code, notes, and snippets.

@kwasmich
Last active May 21, 2016 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kwasmich/dc24ebad6406abb103aba4a2b42e80a9 to your computer and use it in GitHub Desktop.
Save kwasmich/dc24ebad6406abb103aba4a2b42e80a9 to your computer and use it in GitHub Desktop.
Compiler cast "nan"s to "inf"s
//
// 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
@kwasmich
Copy link
Author

All compilers I have my hands on expose this issue at least at a specific optimization level.

@mrkskwsnck
Copy link

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