By the nature of my activity, I was forced to work closely with the IEEE-754 format, during which I found that none of the compilers known to me (VC ++, Intel C ++, CLang, GCC (MinGW)) can convert some numbers into decimal notation (such as , 1.2345e67) in floating point format. All the compilers that I checked fall on simple tests when the rounding method (up or down) is not obvious to the compiler. Examples of such tests can be seen in this cpp file (the code is too long to post here). My question is: do you know the compiler, which without errors will pass at least these simple tests ( all from this file)? If there is one, I will provide more complex tests. Just compile and run the program, it will display on the console what is passed and what is not. Who will find such a compiler, please name it in the answer.

Maybe someone wants to check the work of tests in other languages, I will be glad to know the result, only you first have to write a similar program based on these tests.

Ps. If someone is interested in the reason for the fall, I explained it in one of my conversations (see video )

UPD : Tests against CLang (GCC passes them). The proposed tests correspond to the C ++ standard (the length of the string and the lexeme does not exceed 65535 characters). I still have dozens of tests against CLang, which destroy the compiler completely. All are associated with a long double (for example, give him the input the minimum denormalized number, it will fall). I must admit, however, that for float and double, CLang does pass all my tests, but often gives warnings when compiling where it shouldn't. In any case, this compiler also does not fully support the IEEE-754 format.

UPD2 : Tests that felled CLang and do not work in GCC (MinGW).

UPD3 : All tests are now on GitHub . Later, when I get clarity (or, perhaps, someone will find my mistakes) on this issue, I will merge everything into one file for convenience.

  • Comments are not intended for extended discussion; conversation moved to chat . - Nicolas Chabanovsky
  • UPD2: gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010 OK. (Starting with -O2 (-Os?) -Wall and without -fno-strict-aliasing, a bunch of warnings like dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] ) What does it mean to fall for simple tests ? Directly - segmentation fault or zerodivide with core dump ??? - avp
  • @avp, yes, we have already figured out that gcc older versions learned to solve this problem. Falling - it means giving the wrong answer (mb. Wrong turn of speech, I agree). And CLang falls in the literal sense, but gives some kind of diagnostic message. - Zealint
  • I think you found out far from all. Errors in the programs were, are and always will be (including in the compilers). That's right, it is not when the program is formally correct , but when it actually (on the given equipment) solves the problem . - avp
  • No, you do not understand. The fact that the compilers (ALL) are full of errors, I already know, and I have several hundred tests confirming this. Here I was interested only in the matter of converting decimal numbers to IEEE-754. It turned out that GCC most likely knows how to do it right (and the one that I have from MinGW doesn't know how). The rest of the compilers cannot even do this. - Zealint

3 answers 3

Base

Gcc 5.3.0, apparently passed:

 $ gcc TestIEEE754Convertion.cpp TestIEEE754Convertion.cpp:80:3: warning: floating constant truncated to zero [-Woverflow] -2.470328229206232720882843964341106861825e-324, ^ TestIEEE754Convertion.cpp:19:3: warning: anonymous type with no linkage used to declare variable '<anonymous union> f32' with linkage } f32; ^ TestIEEE754Convertion.cpp:25:3: warning: anonymous type with no linkage used to declare variable '<anonymous union> f64' with linkage } f64; ^ $ ./a.out ::: Single precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Right on test 9 Right on test 10 Right on test 11 Right on test 12 Right on test 13 Right on test 14 Right on test 15 Right on test 16 Right on test 17 Right on test 18 Right on test 19 Right on test 20 Right on test 21 Right on test 22 Right on test 23 

Gcc 4.8.5:

 $ ./a.out ::: Single precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Right on test 9 Right on test 10 Error on test 11: 0x7fefffffffffffff != 0x7feffffffffffffe Error on test 12: 0x7fefffffffffffff != 0x7feffffffffffffe Right on test 13 Right on test 14 Right on test 15 Right on test 16 Right on test 17 Right on test 18 Right on test 19 Error on test 20: 0x8000000000000000 != 0x8000000000000001 Right on test 21 Right on test 22 Right on test 23 

Gcc 4.9.3:

 $ ./a.out ::: Single precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Right on test 9 Right on test 10 Right on test 11 Right on test 12 Right on test 13 Right on test 14 Right on test 15 Right on test 16 Right on test 17 Right on test 18 Right on test 19 Right on test 20 Right on test 21 Right on test 22 Right on test 23 

Hard 2

For UPD2 Gcc 5 is also great:

 $ gcc TestIEEE754ConvertionHard2.cpp $ ./a.out OK 

Gcc 4.8.5 - ERROR, Gcc 4.9.3 - OK

Hard 3

 $ g++-5 -O0 ./TestIEEE754ConvertionHard3.cpp ./TestIEEE754ConvertionHard3.cpp:19:3: предупреждение: anonymous type with no linkage used to declare variable «<anonymous union> f32» with linkage } f32; ^ ./TestIEEE754ConvertionHard3.cpp:25:3: предупреждение: anonymous type with no linkage used to declare variable «<anonymous union> f64» with linkage } f64; ^ $ ./a.out ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 

Apparently passed. Gcc 4.9 is similar. Gcc 4.8 sfilil tests 5, 7, 8

Strict aliasing

By the way, I may confuse something, but perhaps such transformations:

 #define TEST(HI, LO, F) f = F; if (*(((unsigned short int*)&f)+4) != HI || *(unsigned long long*)&f != LO) { printf ("ERROR"); return 1; } 

don't violate strict-aliasing? Not, of course -Wstrict-aliasing -fstrict-aliasing skips this place and the code on the working compilers works. But somehow, a little, not by itself.

  • Yes, thank you. Please check the UPD2 in the original question. If it gives OK, then GCC is great, if it gives ERROR, then some kind of test fails. - Zealint
  • @Zealint, updated - Monah Tuk
  • thank. Did you try the Hard3 test suite? There, the situation is a little tougher. If they pass, then, apparently, GCC5 will just be the only compiler that can work with the IEEE-754 format. As for strict-aliasing, this is not the case when you need to be afraid of this violation. Everything is transparent here: I simply refer to individual words of the 80-bit number and do not do anything terrible to them, just comparing. True, the code is little-endian-dependent. - Zealint
  • @Zealint: Well, since the violation of strict aliasing is formally UB, the compiler has the right to even throw out the entire function. (Do you think the kernel is just compiled with -O2 ?) - VladD
  • @VladD, well, the kernel is not compiled with a positive compiler :) There is a simpler thing, at least it’s agreed on the union more formally. Well, exactly strict-aliasing on -O2 already included. And so I had a sad experience building Emacs with -O3 - it stupidly immediately fell. - Monah Tuk

C # 6, apparently, passed. The adapted test, MSVC 2015, issues:

::: Single precision :::

Right on test 0
Right on test 1
Right on test 2
Right on test 3
Right on test 4
Right on test 5
Right on test 6
Right on test 7

::: Double precision :::

Right on test 0
Right on test 1
Right on test 2
Right on test 3
Right on test 4
Right on test 5
Right on test 6
Right on test 7
Right on test 8
Right on test 9
Right on test 10
Right on test 11
Right on test 12
Right on test 13
Right on test 14
Right on test 15
Right on test 16
Right on test 17
Right on test 18
Right on test 19
Right on test 20
Right on test 21
Right on test 22
Right on test 23

::: Double precision (TestIEEE754ConvertionHard3.cpp) :::

Right on test 0
Right on test 1
Right on test 2
Right on test 3
Right on test 4
Right on test 5
Right on test 6
Right on test 7
Right on test 8

Adapted source:

 class Program { // Тестирование числа одинарной точности void test32(float a, uint right, int testNumber) { var floatBytes = BitConverter.GetBytes(a); var uintValue = BitConverter.ToUInt32(floatBytes, 0); if (uintValue != right) Console.WriteLine($"Error on test {testNumber}: {uintValue:x8} != {right:x8}"); else Console.WriteLine($"Right on test {testNumber}"); } // Тестирование числа двойной точности void test64(double a, ulong right, int testNumber) { var ulongValue = (ulong)BitConverter.DoubleToInt64Bits(a); if (ulongValue != right) Console.WriteLine($"Error on test {testNumber}: {ulongValue:x16} != {right:x16}"); else Console.WriteLine($"Right on test {testNumber}"); } // Список чисел одинарной точности и правильных двоичных представлений этих чисел const int N32 = 8; float[] Test32 = new float[ N32 ] { 21267649200209254194690314461188718593.0f, 1.175494280757364291727882991035766513322e-38f, 1.175494280757364291727882991035766513323e-38f, 10749727.50000000000000000000000000000001f, 10749728.50000000000000000000000000000001f, -1.175494420887210724209590083408724842314e-38f, -1.175494420887210724209590083408724842315e-38f, -340282336497324057985868971510891282432.1f }; uint[] Right32 = new uint[ N32 ] { 0x7d800001, 0x007fffff, 0x00800000, 0x4b240720, 0x4b240721, 0x80800000, 0x80800001, 0xff7fffff }; // Список чисел двойной точности и правильных двоичных представлений этих чисел const int N64 = 24; double[] Test64 = new double[ N64 ] { -2.470328229206232720882843964341106861825e-324, -2.470328229206232720882843964341106861826e-324, 2.225073858507200641991763955462587799366e-308, 2.225073858507200641991763955462587799367e-308, 6755399441055826.499999999999999999999999, 6755399441055826.500000000000000000000001, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944.1, 11417981541647677146990387413251858846007164927.9, 11417981541647677146990387413251858846007164928.0, 11417981541647677146990387413251858846007164928.1, 11417981541647677780815687527366559594358767616.0, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218943.9, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944.0, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944.1, 9214843084008499.0, 0.500000000000000166533453693773481063544750213623046875, 30078505129381147446200.0, 3.518437208883201171875e13, 8.10109172351e-10, 9007199254740991.4999999999999999999999999999999995, -2.-324, 6755399441055827.499999999999999999999999, 6755399441055827.5, 6755399441055827.500000000000000000000001 }; ulong[] Right64 = new ulong[ N64 ] { 0x8000000000000000, 0x8000000000000001, 0x000ffffffffffffe, 0x000fffffffffffff, 0x4338000000000052, 0x4338000000000053, 0x7fefffffffffffff, 0x497ffffffffffffe, 0x497ffffffffffffe, 0x497fffffffffffff, 0x497fffffffffffff, 0x7feffffffffffffe, 0x7feffffffffffffe, 0x7fefffffffffffff, 0x43405e6cec57761a, 0x3fe0000000000002, 0x44997a3c7271b021, 0x42c0000000000002, 0x3e0bd5cbaef0fd0c, 0x433fffffffffffff, 0x8000000000000001, 0x4338000000000053, 0x4338000000000054, 0x4338000000000054 }; // Более сложный тест из https://github.com/Zealint/fp_tests/blob/master/TestIEEE754ConvertionHard3.cpp const int N64Hard = 9; double[] Test64Hard = new double[ N64Hard ] { 2.-308, 2.-308, 2.-308, 2.-308, 2.-308, 2.-308, 2.-308, 2.-308, 2.-308 }; ulong[] Right64Hard = new ulong[ N64Hard ] { 0x0010000000000000, 0x0010000000000001, 0x0010000000000002, 0x0010000000000000, 0x0010000000000000, 0x0010000000000001, 0x0010000000000001, 0x0010000000000002, 0x0010000000000002 }; static void Main(string[] args) { new Program().Run(); } void Run() { // Проверка всех чисел одинарной точности в цикле Console.WriteLine("\n\n::: Single precision :::\n"); for (int i = 0; i < N32; i++) test32(Test32[i], Right32[i], i); // Проверка всех чисел двойной точности в цикле Console.WriteLine("\n::: Double precision :::\n"); for (int i = 0; i < N64; i++) test64(Test64[i], Right64[i], i); // Более сложная проверка (TestIEEE754ConvertionHard3.cpp) Console.WriteLine("\n::: Double precision (TestIEEE754ConvertionHard3.cpp) :::\n"); for (int i = 0; i < N64Hard; i++) test64(Test64Hard[i], Right64Hard[i], i); } } 

Update: tests with long double cannot be transferred to C #, since there is no analogue to the long double data type (80 bits floating point) in .NET of the current version.


Update: added tests from https://github.com/Zealint/fp_tests/blob/master/TestIEEE754ConvertionHard3.cpp

  • Fine! Thank you, @VladD. UPD has more complex tests for the questions, as I promised, but I'm not sure that the C # standard is not broken by them, there are quite long numbers (the C ++ standard is not exactly broken). - Zealint
  • @Zealint: Unfortunately, long double not supported in C #, so I cannot run more complex tests. - VladD
  • Very sorry. I can try to think that it might overwhelm C #. I'll think about double now. Are there any restrictions on the length of a number in the standard language? - Zealint 2:55 pm
  • @Zealint: I don’t remember such restrictions, but now I’ll read the standard. - VladD 2:58 pm
  • @Zealint: According to the documentation ( C # Language Specification , §2.4.4.3), there seems to be no restrictions, except two: (1) the compile-time error occurs. Float or double is determined by using the IEEE “round to nearest” mode. (2) in a real literal, decimal digits are always required after the decimal point. For example, 1.3F is a real literal but 1.F is not - VladD

gcc 4.9.2 seems to pass. On all four.
Everywhere "Right on" and "OK".

  • one
    Thank. I think that GCC of this version and above can be considered the only candidate for the ability to work with floating point at the moment. Strange, but what did other C / C ++ compilers do? - Zealint