По роду деятельности я был вынужден плотно работать с форматом IEEE-754, в процессе
чего обнаружил, что ни один из известных мне компиляторов (VC++, Intel C++, CLang,
GCC (именно MinGW)) не умеет конвертировать некоторые числа в десятичной записи (типа,
1.2345e67) в формат с плавающей точкой. Все компиляторы, что я проверял, падают на
несложных тестах, когда неочевидным для компилятора является способ округления (вверх
или вниз). Примеры таких тестов можно увидеть в этом cpp файле (код слишком длинный,
чтобы постить сюда). Мой вопрос таков: знаете ли вы компилятор, который без ошибок
пройдёт хотя бы эти простые тесты (все из этого файла)? Если таковой найдётся, я предоставлю
более сложные тесты. Просто скомпилируйте и запустите программу, она выдаст на консоль,
что пройдено, а что нет. Кто найдёт такой компилятор, прошу назвать его в ответе.
Может быть кто-то захочет проверить работу тестов на других языках, буду рад узнать
результат, только вам сначала придётся написать похожую программу на основе данных тестов.
PS. Если же кому-то будет интересна причина падения, я объяснял её в одной из своих
бесед (см. видео)
UPD: Тесты против СLang (GCC их проходит). Предложенные тесты соответствуют стандарту
языка C++ (длина строки и лексемы не превышает 65535 символов). У меня ещё есть несколько
десятков тестов против CLang, которые рушат компилятор напрочь. Все связаны с long
double (например, подайте ему на вход минимальное денормализованное число, он упадёт).
Должен, однако, признать, что для float и double CLang действительно проходит все мои
тесты, но часто выдаёт предупреждения при компиляции там, где не должен. В любом случае,
данный компилятор также не поддерживает формат IEEE-754 полностью.
UPD2: Тесты, которые валят CLang и не работают в GCC (MinGW).
UPD3: Все тесты теперь на GitHub. Позже, когда у меня наступит ясность (или, быть
может, кто-то найдёт у меня ошибки) по данному вопросу, я объединю всё в один файл
для удобства.
Ответы
Ответ 1
Base
Gcc 5.3.0, судя по всему прошёл:
$ 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 ‘ f32’ with linkage
} f32;
^
TestIEEE754Convertion.cpp:25:3: warning: anonymous type with no linkage used to declare
variable ‘ 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
Для UPD2 Gcc 5 тоже молодец:
$ 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 « f32» with linkage
} f32;
^
./TestIEEE754ConvertionHard3.cpp:25:3: предупреждение: anonymous type with no linkage
used to declare variable « 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
Судя по всему прошёл. Gcc 4.9 - аналогично. Gcc 4.8 сфейлил тесты 5, 7, 8
Strict aliasing
Кстати, я могу что-то путать, но разве такие преобразования:
#define TEST(HI, LO, F) f = F; if (*(((unsigned short int*)&f)+4) != HI || *(unsigned
long long*)&f != LO) { printf ("ERROR"); return 1; }
не нарушают правило strict-aliasing? Не, конечно -Wstrict-aliasing -fstrict-aliasing
это место пропускает и код на рабочих компиляторах работает. Но как-то, малость, не
по себе.
Ответ 2
C# 6, судя по всему, прошёл. Адаптированный тест, MSVC 2015, выдаёт:
::: 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
Адаптированный исходник:
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.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125001e-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.225073858507201383090232717332404064219215980462331830553327416887204434813918195854283159012511020564067339731035811005152434161553460108856012385377718821130777993532002330479610147442583636071921565046942503734208375250806650616658158948720491179968591639648500635908770118304874799780887753749949451580451605050915399856582470818645113537935804992115981085766051992433352114352390148795699609591288891602992641511063466313393663477586513029371762047325631781485664350872122828637642044846811407613911477062801689853244110024161447421618567166150540154285084716752901903161322778896729707373123334086988983175067838846926092773977972858659654941091369095406136467568702398678315290680984617210924625396728515625e-308,
2.2250738585072018771558785585789482407880088486837041956131300312119688603996006965297904292212628858639037013670281908017171296072711910355127227413175152199055740043138804567803233377539881639177387328959246074229270113078053813397081653361296447449529789521218979090783852583365901851789618799885150427514782636076021680436220311292700454832073964845713103912225963935608322440623896907276890186717054549275173986589324810401738228328251245795065655738191038008646911615828719989708647293221449796971546706720399791990809160347625980385995424739847678861180095072511543762389603716215171729816011544604359531284325406441938645324905389137795680915804792405099227413854274942620542640408839836919187418172987793340279242767544565229087538682506419718265533447265625e-308,
2.225073858507202371221524399825492417356801716905076560672932645536733285985283197205297699430014751163740063003020570598281825052988921962169433097257311618680370015095758583081036528065392691763555900744906711111645647364804112062758171723538798309937366264595295182248000398368305570577036006227080633922504922164288936230661591439894977428478987977026639696679140794688312373772389232659678427752122018252042155806801495766953982188063736129641369100312575820243717972293621169304087413797478551780397864281278268544917722045363748655580517781818995617950934297749406849316597964346304638590078974833882923081797242441461636291003104968899481242069589385613709015202152589845793237400783350172912858237869043043055848553508913045817507736501283943653106689453125e-308,
2.22507385850720163012305563795567615250361241457301801308322872404958664760675944619203679411688695321398552054903200090343478188441232557218436756334761702051817599892294139362996674259828589999483014897143355557856769327930601597818316214242506796246078529588519927249357768832073249247992481686923224716596493432925878395010225097395757951057160073834364573849432419299709217920738991976169431413149717326525502008499797367678374315520581880443916381057236779117517775622749741380425338708447819365553307386742083452616251302946202273010905482006765402020154711200202813970014157525912344017736224427371246815175018974555997865323425588621961151633592416795802960447706494647018477736093430045142168360701364747951396213837722826145437693412532098591327667236328125e-308,
2.22507385850720163012305563795567615250361241457301801308322872404958664760675944619203679411688695321398552054903200090343478188441232557218436756334761702051817599892294139362996674259828589999483014897143355557856769327930601597818316214242506796246078529588519927249357768832073249247992481686923224716596493432925878395010225097395757951057160073834364573849432419299709217920738991976169431413149717326525502008499797367678374315520581880443916381057236779117517775622749741380425338708447819365553307386742083452616251302946202273010905482006765402020154711200202813970014157525912344017736224427371246815175018974555997865323425588621961151633592416795802960447706494647018477736093430045142168360701364747951396213837722826145437693412532098591327667236328124999e-308,
2.22507385850720163012305563795567615250361241457301801308322872404958664760675944619203679411688695321398552054903200090343478188441232557218436756334761702051817599892294139362996674259828589999483014897143355557856769327930601597818316214242506796246078529588519927249357768832073249247992481686923224716596493432925878395010225097395757951057160073834364573849432419299709217920738991976169431413149717326525502008499797367678374315520581880443916381057236779117517775622749741380425338708447819365553307386742083452616251302946202273010905482006765402020154711200202813970014157525912344017736224427371246815175018974555997865323425588621961151633592416795802960447706494647018477736093430045142168360701364747951396213837722826145437693412532098591327667236328125001e-308,
2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984374999e-308,
2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308,
2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375001e-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);
}
}
Обновление: тесты с long double перенести на C# не получится, т. к. аналога типа
данных long double (80 бит с плавающей запятой) в .NET текущей версии нету.
Обновление: добавил тесты из https://github.com/Zealint/fp_tests/blob/master/TestIEEE754ConvertionHard3.cpp
Ответ 3
gcc 4.9.2 вроде проходит. На всех четырёх.
Везде "Right on" и "OK".