https://habr.com/ru/post/522820/ Код (ASM): T_ Equ TByte ptr W_ Equ Word ptr ... Qdec dQ 987654321987654321 DebugMem dd 4 Dup (0) ChrQdec Db 18 Dup (0) ... fNInit ; если не было fILd [Qdec] fBstP T_[DebugMem] xOr eDi,eDi Mov eCx,9 __1: ; может можно и еще пооптимизировать MovZx eAx,B_[DebugMem + eCx - 1] RoR Ax,4 SHR Ah,4 Or Ax,3030h Mov W_[ChrQdec + eDi * 2],Ax Inc eDi LoopD __1
ioann_V, не совсем так, оптимизация бывает разной 1) по размеру кода 2) по скорости. так что это тоже своего рода оптимизация
Мой алгоритм в статье - он конечно, по кол-ву кодобайт длинный, да. Но, работает быстро, раз, во вторых - память не потребляет вообще - никаких обращений к Кешу, в этом и фишка, точнее одна из. Ну и почему-то на Arm-ах, он работает вдвое быстрее, алгоиртма от Abseil lib(google), который считается наибыстрейшим. А вот на x86_64 - можно получить прирост в 1.5 раза ну и незасоренный кеш. Так мне интересно в целом все, я и WDK использовал в своей практике и какие-то другие, низкоуровневые вещи. Может, за тем чтобы какие то пробелы восполнить я тут. Ну и что-то сделать интересное, в этом низкоуровневом направлении: мечтал всегда, честно говоря. Еще со времен wx.net
Ну, вообще, по коду много вопросов, на самом деле. 1. Почему, например, количество разрядов целого числа не устанавливаете методом половинного деления, а последовательно сравниваете с 10, 100, 1000 и т.д.? 2. Большое количество if'ов - большая нагрузка на branch predictor. 3. Большое количество команд в функции - большая нагрузка на кэш команд. 4. memcpy для копирования очень маленьких кусков памяти и не запись результата прямо по переданному адресу - хммммм.... 5. Работает только на LE-архитектуре.
1. Здесь на самом деле, можно и так, но у меня на тестах - этот вариант показал лучший результат. Я запускал разные бенчмарки, от разных людей. Тем не менее, совсем не спорно, что в зависимости от того, какаие входные данные - условные переходы можно поменять. Если скажем, известно, что нам приходит, только числа больше 6-значных. 2. Да, это верно. Но, без бренчей тут никак - даже если делать loop, это тоже бренч предиктор, который правда, работает несколько иначе, чем при условных переходах, но дает свой вклад и убивает некоторые механизмы работы процессора. Ну точнее, тот вариант что дали выше, он всегда будет бегать в цикле 9раз, а если оджнозначное число пришло? Обычно, вычисляют длину числа(чтобы сделать потом реверс массива) 3. А так ли оно велико, для процессоров Intel SB(2k11)+? 4. МемЦпу тут оптимизируется до простого регистрового копирования, но так сделано - чтобы не попасть на Undefined Behaviour от C++. Это же он самый, а не чистый asm. Первая версия использовала reinterpret_cast - но чувак из google тут меня и поправил, сказал, что memcpy даст гарантии. 5. Yes, но и переделать для BE реально(? - есть же циклические сдвиги). И шестое, это то, что если писать на чистом Ассемблере, учитывая специфику архитектуры, то можно улучшить решение, потому что ассемблер сам по себе, он ну, реально переносим слабо. --- Сообщение объединено, 9 дек 2020 --- P.s скорее всего сюда и залью статейку по переводу Плав.Запятой в строки - там интересные и сложные вещи, с математикой связанные.
как раз в этом и смысл низкоуровневой оптимизации. нужный алгоритм пишется под разные архитектуры и потом при компиляции выбирается нужный кейс. По этому рекомендую вам попробовать на асме показать класс , как раз форум тематический.
DebugMem для наглядности, а для работы - DebugMem Equ ChrQdec+8 Естественно можно и без LoopD, т.е. развернуть. Но бывает время LoopD = 0 - конвеер так умеет. Быстродействие удобнее сравнить на ваших процессорах, тестах и до 10^18 соответственно. Код (ASM): fNInit ; если не было fILd [Qdec] fBstP T_[ChrQdec + 8] ; до 5 раз нечто подобное ниже, ; можно и в "обратную сторону" с Cmp / Jxx MovZx eAx,B_[ChrQdec + 15] RoR Ax,4 SHR Ah,4 SHL eAx,16 Mov Al,B_[ChrQdec + 16] RoR Ax,4 SHR Ah,4 Or eAx,30303030h Mov D_[ChrQdec + 0 * 4],eAx
Была (а сталобыть и есть уже много-много лет подряд) же конференция на жабере, пойдемте там резвитьца!
А моя реализация в среднем чуть быстрее: Код (Text): Statistics of performance test 'dsp.number': ┌Case─────────────┬Time[s]┬───Iter┬Samp[s]┬────Est┬─Perf[i/s]┬Cost[us/i]┬Rel[%]┐ │uint32_to_string0│ 5.00│6622000│ 5.00│6621308│1324261.75│ 0.7551│100.00│ │uint32_to_string1│ 5.00│6997000│ 5.00│6996510│1399302.05│ 0.7146│105.67│ └─────────────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴──────┘ Performance test 'dsp.number' has succeeded, execution time: 10.01 s -------------------------------------------------------------------------------- Overall performance test statistics: execution time [s]: 10.01 launched: 1 ignored: 0 succeeded: 1 failed: 0 Ссылка на реализацию: https://github.com/sadko4u/lsp-dsp-lib/blob/decimal-optimizations/src/test/ptest/number.cpp Код: Код (Text): /* * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/> * (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com> * * This file is part of lsp-dsp-lib * Created on: 9 дек. 2020 г. * * lsp-dsp-lib is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * lsp-dsp-lib is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with lsp-dsp-lib. If not, see <https://www.gnu.org/licenses/>. */ #include <lsp-plug.in/test-fw/ptest.h> #include <lsp-plug.in/common/alloc.h> static const uint16_t table[] = { 0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930, 0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931, 0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932, 0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933, 0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934, 0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935, 0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936, 0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937, 0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938, 0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939 }; char* uint32_to_string0(uint32_t n, char *out_str) { if ( n < 10u ) { const uint64_t in = n + 0x30ull; memcpy( out_str, &in, 8u ); return out_str + 1u; } const uint32_t b = n / 10u; if ( n < 100u ) { const uint64_t in = 256ull * n - 2559ull * b + 0x3030ull; memcpy( out_str, &in, 8u ); return out_str + 2u; } const uint32_t c = n / 100u; if ( n < 1000u ) { const uint64_t in = 65536ull * n - 2559ull * ( 256ull * b + c ) + 0x303030ull; memcpy( out_str, &in, 8u ); return out_str + 3u; } const uint32_t d = n / 1000u; if ( n < 10000u ) { const uint64_t in = 16777216ull * n - 2559ull * ( 256ull * ( 256ull * b + c ) + d ) + 0x30303030ull; memcpy( out_str, &in, 8u ); return out_str + 4u; } const uint32_t e = n / 10000u; if ( n < 100000u ) { const uint64_t in = ( ( 16777216ull * n - 2559ull * ( 256ull * ( 256ull * b + c ) + d ) - 10 * e ) << 0x08ull ) + e + 0x3030303030ull; memcpy( out_str, &in, 8u ); return out_str + 5u; } const uint32_t f = n / 100000u; if ( n < 1000000u ) { const uint64_t in = ( ( 16777216ull * n - 2559ull * ( 256ull * ( 256ull * b + c ) + d ) - 10 * e ) << 0x10ull ) + ( ( 256ull * e - 2559ull * f ) + 0x303030303030ull ); memcpy( out_str, &in, 8u ); return out_str + 6u; } const uint32_t g = n / 1000000u; if ( n < 10000000u ) { const uint64_t in = ( ( 16777216ull * n - 2559ull * ( 256ull * ( 256ull * b + c ) + d ) - 10 * e ) << 0x18ull ) + ( ( 65536ull * e - 2559ull * ( 256ull * f + g ) + 0x30303030303030ull ) ); memcpy( out_str, &in, 8u ); return out_str + 7u; } const uint32_t h = n / 10000000u; if ( n < 100000000u ) { const uint64_t in = ( ( 16777216ull * n - 2559ull * ( 256ull * ( 256ull * b + c ) + d ) - 10 * e ) << 0x20ull ) + ( ( 16777216ull * e - 2559ull * ( 256ull * ( 256ull * f + g ) + h ) ) + 0x3030303030303030ull ); memcpy( out_str, &in, 8u ); return out_str + 8u; } const uint32_t x = n / 100000000u; const uint64_t r_0 = ( ( 16777216ull * n - 2559ull * ( 256ull * ( 256ull * b + c ) + d ) - 10 * e ) << 0x20ull ) + ( 16777216ull * e - 2559ull * ( 256ull * ( 256ull * f + g ) + h ) - 10 * x ); if ( 9u < x ) { const uint64_t in_1 = ( ( x % 10u ) << 8ull ) + x / 10u + 0x3030ull; memcpy( out_str, &in_1, 8u ); const uint64_t in_2 = ( ( r_0 + 0x3030303030303030ull ) ); memcpy( out_str + 2u, &in_2, 8u ); return out_str + 9u; } else { const uint64_t in_1 = x + 0x30u; memcpy( out_str, &in_1, 8u ); const uint64_t in_2 = r_0 + 0x3030303030303030ull; memcpy( out_str + 1u, &in_2, 8u ); return out_str + 10u; } } char* uint32_to_string1(uint32_t n, char *out_str) { uint8_t bytes; uint64_t div; uint32_t mod; // 4294967295 if (n <= 9999) { if (n <= 99) bytes = (n <= 9) ? 1 : 2; else bytes = (n <= 999) ? 3 : 4; } else if (n <= 99999999) { if (n <= 999999) bytes = (n <= 99999) ? 5 : 6; else bytes = (n <= 9999999) ? 7 : 8; } else bytes = (n <= 999999999) ? 9 : 10; char *ret = &out_str[bytes+1]; if (bytes & 1) { out_str += bytes - 1; div = n / 10; mod = n % 10; out_str[0] = 0x30 + mod; out_str[1] = '\0'; n = div; bytes &= ~1; } else { out_str += bytes; out_str[0] = '\0'; } uint16_t *dst = (uint16_t *)out_str; while (n) { --dst; div = n / 100; mod = n % 100; *dst = table[mod]; n = div; } return ret; } typedef char* (* uint32_to_string_t)(uint32_t n, char *out_str); static const uint32_t values[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 11, 22, 33, 44, 55, 66, 77, 88, 99, 45, 54, 36, 63, 27, 72, 81, 100, 212, 323, 434, 545, 656, 767, 878, 989, 191, 272, 363, 454, 999, 153, 709, 1234, 2345, 3456, 5678, 7890, 8901, 9012, 9876, 8765, 7654, 6543, 5432, 4321, 3210, 2109, 1098, 12345, 23456, 34567, 45678, 56789, 67890, 78901, 89012, 90123, 76543, 65432, 54321, 43210, 32109, 21098, 10987, 568947, 689471, 894713, 947134, 471341, 713412, 134120, 341205, 568947, 689671, 814713, 957134, 471641, 712412, 634127, 241205, 7654321, 6543210, 5432109, 4321098, 3210987, 2109876, 1098765, 9876543, 8765432, 7591274, 5912747, 9127475, 1928374, 9283741, 2837419, 8374192, 19283746, 92837461, 28374619, 83746192, 37461928, 74619283, 46192837, 61928374, 13822473, 38224731, 82247313, 22473138, 24731382, 47313822, 73138224, 31382247, 104837519, 483751910, 837519104, 375191048, 751910483, 519104837, 191048375, 910483751, 884772641, 847726418, 477264188, 772641884, 726418847, 264188477, 641884772, 418847726, 1498673324, 1437674341, 1123456789, 1987654321, 2581275057, 2302857683, 2476301756, 2762307694, 3278574027, 3495817583, 3459371495, 3048658387, 4168205863, 4258672947, 4148592495, 4027845728 }; PTEST_BEGIN("dsp", number, 5, 1000) void call(const char *label, char *out, uint32_to_string_t func) { printf("Testing %s ...\n", label); PTEST_LOOP(label, for (size_t i=0; i<sizeof(values)/sizeof(uint32_t); ++i) func(values[i], out); ); } PTEST_MAIN { char buf[0x20]; #define CALL(func) \ call(#func, buf, func) CALL(uint32_to_string0); CALL(uint32_to_string1); PTEST_SEPARATOR; } PTEST_END
У меня результаты, тоже близки, но все же, берет немного мое решение: Код (Text): BM_FastIntToBuffer_1<int32_t>/0 0.463 ns 0.463 ns 1000000000 BM_FastIntToBuffer_1<int32_t>/0 0.463 ns 0.463 ns 1000000000 BM_FastIntToBuffer_1<int32_t>/1 3.64 ns 3.64 ns 206269735 BM_FastIntToBuffer_1<int32_t>/8 4.01 ns 4.01 ns 183023062 BM_FastIntToBuffer_1<int32_t>/64 4.24 ns 4.24 ns 165640593 BM_FastIntToBuffer_1<int32_t>/512 4.27 ns 4.27 ns 163858753 BM_FastIntToBuffer_1<int32_t>/4096 4.28 ns 4.28 ns 163977891 BM_FastIntToBuffer_1<int32_t>/32768 4.25 ns 4.25 ns 164379338 BM_FastIntToBuffer_1<int64_t>/0 0.463 ns 0.463 ns 1000000000 BM_FastIntToBuffer_1<int64_t>/0 0.463 ns 0.463 ns 1000000000 BM_FastIntToBuffer_1<int64_t>/1 3.60 ns 3.60 ns 206391114 BM_FastIntToBuffer_1<int64_t>/8 3.98 ns 3.98 ns 187250811 BM_FastIntToBuffer_1<int64_t>/64 4.24 ns 4.24 ns 165850672 BM_FastIntToBuffer_1<int64_t>/512 4.27 ns 4.27 ns 163552205 BM_FastIntToBuffer_1<int64_t>/4096 4.27 ns 4.27 ns 163668873 BM_FastIntToBuffer_1<int64_t>/32768 4.29 ns 4.29 ns 162510009 BM_FastIntToBuffer_1<int64_t>/262144 4.29 ns 4.29 ns 163283259 BM_FastIntToBuffer_1<int64_t>/2097152 4.29 ns 4.29 ns 162782934 BM_FastIntToBuffer_1<int64_t>/16777216 4.32 ns 4.31 ns 162079999 BM_FastIntToBuffer_1<int64_t>/134217728 4.18 ns 4.18 ns 167288345 BM_FastIntToBuffer_1<int64_t>/1073741824 3.46 ns 3.46 ns 202346753 BM_FastIntToBuffer_2<int32_t>/0 2.08 ns 2.08 ns 335005897 BM_FastIntToBuffer_2<int32_t>/0 2.09 ns 2.08 ns 334739517 BM_FastIntToBuffer_2<int32_t>/1 4.03 ns 4.03 ns 178465825 BM_FastIntToBuffer_2<int32_t>/8 4.24 ns 4.24 ns 173153228 BM_FastIntToBuffer_2<int32_t>/64 4.34 ns 4.34 ns 162019339 BM_FastIntToBuffer_2<int32_t>/512 4.38 ns 4.38 ns 159033800 BM_FastIntToBuffer_2<int32_t>/4096 4.38 ns 4.38 ns 159785547 BM_FastIntToBuffer_2<int32_t>/32768 4.45 ns 4.45 ns 159699516 BM_FastIntToBuffer_2<int64_t>/0 2.08 ns 2.08 ns 334980342 BM_FastIntToBuffer_2<int64_t>/0 2.08 ns 2.08 ns 335701603 BM_FastIntToBuffer_2<int64_t>/1 4.02 ns 4.02 ns 178519955 BM_FastIntToBuffer_2<int64_t>/8 4.15 ns 4.15 ns 173342059 BM_FastIntToBuffer_2<int64_t>/64 4.38 ns 4.38 ns 161389677 BM_FastIntToBuffer_2<int64_t>/512 4.40 ns 4.39 ns 155380901 BM_FastIntToBuffer_2<int64_t>/4096 4.99 ns 4.99 ns 159681985 BM_FastIntToBuffer_2<int64_t>/32768 4.35 ns 4.35 ns 161239193 BM_FastIntToBuffer_2<int64_t>/262144 4.53 ns 4.53 ns 159868333 BM_FastIntToBuffer_2<int64_t>/2097152 4.35 ns 4.35 ns 161291794 BM_FastIntToBuffer_2<int64_t>/16777216 4.42 ns 4.42 ns 159047903 BM_FastIntToBuffer_2<int64_t>/134217728 4.57 ns 4.56 ns 155172682 BM_FastIntToBuffer_2<int64_t>/1073741824 4.09 ns 4.09 ns 171145714 --- Сообщение объединено, 9 дек 2020 --- Я прогонял гуглбенчмарком, компилировал на *nix, gcc-10, O3. Процессор - Ryzen 3900x. Но то, что оно может идти в ровень +- - момент не спорный, фишка в том, что у меня кеш не используется. И, не более того. --- Сообщение объединено, 9 дек 2020 --- Сама идея этого метода родилась когда Антон Полухин - контрибутор gcc искал новый метод, который, как раз не использовал бы кеш. А так, у тебя идея таже считай лежит - тоже ифы и ветвления, но то как ты к этому подошел - интересно тоже(организация ветвлений - я так не делал), интересно, мой метод таким образом переписать можно(бин. поиск)? Ну и конечно, респект тебе за то, что не только слова предоставляешь, но и результаты. З.Ы: То есть, я по другому ветвления бинарно делал, не так как ты.
Я пробовал с -O2, Ryzen 2700, gcc 7.5. Тут ещё где-то в сети проскакивал хитрый вариант, где предлагали число на полином хитрый умножать, и правильные значения сразу по байтикам расставлялись. Но найти не могу. Это какая-то библиотека по парсингу json + simd была...
ioann_V, экий ты хитрый, однако https://github.com/ov3rdr1v337/decima/blob/main/mybenchmark.cc то бишь в твоём коде няма циклов. но для больших чисел, увы да ах, такие трюки не катят.
Да, есть такое - хотя для 64бит можно тоже придумать, а вот если побольше, то будет сложнее - да. Но большая разрадность, это уже про длинную арифметику, FFT и вот это все. Идея основная то в чем - это посмотреть на старую проблему, под другим углом, которого нигде нету. Вот это мне интересно - ну и про это я заодно и пишу. Я уверен, что многие задачи можно решить неожиданно по-другому, так, чтобы ну как минимум заинтересовать, или как максимум - удивить! --- Сообщение объединено, 10 дек 2020 --- У меня есть на хабре, начало полного разбора скажем JeMalloc, или про интересную оплошность старого трюка с заменой деления на умножения в компиляторах - то есть такие вещи, которые не описаны. Хотя про Джемаллок только https://twitter.com/kyprizel оценил. И собственно недостаток заинтересованной аудитории в таких редких вещах - демотивирует писать дальше. А мне только это и интересно, нежели писать про то, что у процессора есть кеши, буффера обмена и все другое, что как бы должен знать каждый уважающий себя программист. --- Сообщение объединено, 10 дек 2020 --- Задача Инди меня тоже интересует - но мне так и не дали описания.
Это моя наработка 2013 (http://masm32.com/board/index.php?topic=1852.msg19874#msg19874) Код (ASM): ; masm windows console # include \masm32\include\masm32rt.inc .686 .mmx .xmm Eprst proto lpReal10:ptr REAL10,lpDest:DWORD .data _Real10_R REAL10 3.141592653589793238462E61; no crash .code main proc LOCAL result[32]:BYTE ; fldpi ; fstp _Real10_R invoke Eprst,addr _Real10_R,ADDR result print ADDR result,13,10 inkey " *** STOP - Eprst --- E N D ---*** " exit main endp $max equ 60; <-- This number may be increased magic1 equ 4D10h;lg(2)*65536=19728,3... magic2 equ 35269h;log2(10)*65536=3,32192809488736234780*65536=217705,8796265381788254208.. .const cntr = -$max REPEAT 2*$max+1 IF cntr NE 0 REAL10 @CatStr(<1.0e>,%-cntr) ELSE tabs REAL10 1.0 ENDIF WORD 0 cntr = cntr + 1 ENDM table0 label word n=0 rept 100 dw (n mod 10 shl 8)+(n/10)+"00" n=n+1 endm table1 dd shift0,shift1,shift2,shift3,shift4 .code Eprst proc uses edi esi ebx lpReal10:ptr REAL10,lpDest:DWORD local iExp:dword local x[3]:dword local temp1:dword local temp2:dword fninit mov ecx,lpReal10 fld tbyte ptr [ecx] mov edi,lpDest mov ebx,[ecx] mov eax,[ecx+4] mov [x],ebx movzx edx,word ptr [ecx+8] mov [x+4],eax mov [x+8],edx cmp edx,8000h mov byte ptr [edi],'-' sbb esi,esi and esi,0Dh sub [edi],esi and edx,7FFFh jnz a2 Zero_Or_Denormal: cmp edx,eax jnz Denormal cmp ebx,eax jnz Denormal mov dword ptr [edi+1],'oreZ' mov dword ptr [edi+5],0 ret a2: cmp edx,7FFFh jz Infinity_Or_SNAN_Or_QNAN_Or_Uncertainty Denormal:mov esi,10 sub edx,16383 mov word ptr [edi+21],'+E' imul edx,magic1 and edx,0FFFF0000h sar edx,14 mov iExp,edx jz @f cmp edx,4*$max jg a1 cmp edx,-4*$max jge load ;------------------------------------------------ dec edx a1:neg edx sar edx,2 mov temp1,edx imul edx,magic2 sar edx,16 mov temp2,edx fild temp2 ; get the characteristic fild temp1 ; log2(10)*expscale fldl2t ; log2(10) fmul ; log2(10)*expscale fsub st,st(1) ; get only the mantissa but keep the characteristic f2xm1 ; 2^(mantissa)-1 fld1 fadd ; add 1 and pop fscale fstp st(1) ; remove the characteristic jmp multiply load: fld tabs[edx+edx*2] multiply:fmul ; X * 10^expscaleS fld st fstp tbyte ptr x xor edx,edx @@: mov ecx,x+8 mov ebx,x mov eax,x+4 and ecx,7FFFh sub ecx,16382 cmp ecx,4 ja shift0 jmp table1[ecx*4] shift4::test eax,60000000h jz a6 fld tabs[12] fmul add iExp,4 fstp tbyte ptr x mov eax,x+4 mov ebx,x shld edx,eax,1 shld eax,ebx,1 shl ebx,1 add ebx,4 jmp shift0 a6: shld edx,eax,4 ;ecx=1 ebx+4 shld eax,ebx,4 ;ecx=2 ebx+8 shl ebx,4 ;ecx=3 ebx+9 add ebx,12 ;ecx=4 ebx+12 jmp shift0 shift3::shld edx,eax,3 shld eax,ebx,3 shl ebx,3 add ebx,9;10 jmp shift0 shift2::shld edx,eax,2 shld eax,ebx,2 shl ebx,2 add ebx,8 jmp shift0 shift1::shld edx,eax,1 shld eax,ebx,1 shl ebx,1 add ebx,4 shift0::adc eax,0 adc edx,0 mov [edi+1],dl mov byte ptr [edi+2],',' or byte ptr [edi+1],'0' k=3 REPEAT 17 mul esi mov [edi+k],dl mov ecx,eax mov eax,ebx mul esi add ecx,edx adc byte ptr [edi+k],'0' mov ebx,eax mov eax,ecx k=k+1 ENDM mul esi mov [edi+k],dl mov ecx,eax mov eax,ebx mul esi add ecx,edx adc byte ptr [edi+k],'0' ;-------------------------------------- mov eax,iExp mov edx,eax sar eax,2 sar edx,31 mov ecx,esi xor eax,edx sub eax,edx and edx,2 add byte ptr [edi+22],dl mov esi,eax mov edx,42949673;2^32/100 mul edx mov eax,dword ptr table0[edx*2] ;edx = quotient of the division by 100 mov dword ptr [edi+23],eax lea eax,[edx*4] shl edx,2 lea edx,[edx+edx*2] lea edx,[eax+edx*8] ;edx = quotient*100 sub esi,edx ;esi = remainder of the division by 100 mov eax,dword ptr table0[esi*2] mov dword ptr [edi+25],eax mov dword ptr [edi+27],0 ret Infinity_Or_SNAN_Or_QNAN_Or_Uncertainty: cmp eax,80000000h jnz SNAN_Or_QNAN_Or_Uncertainty and ebx,ebx jnz SNAN_Or_QNAN_Or_Uncertainty mov dword ptr [edi+1],'ifnI' mov dword ptr [edi+5],'ytin' mov dword ptr [edi+9],0 ret SNAN_Or_QNAN_Or_Uncertainty: test eax,eax jns error test eax,40000000h jnz QNAN mov dword ptr [edi+1],'ngiS' mov dword ptr [edi+5],'N la' mov dword ptr [edi+9],'a no' mov dword ptr [edi+13],'muN ' mov dword ptr [edi+17],'reb' ret QNAN: test eax,3FFFFFFFh jnz @f test ebx,ebx jnz @f mov dword ptr [edi+1],'ecnU' mov dword ptr [edi+5],'iatr' mov dword ptr [edi+9],'ytn' ret @@: mov dword ptr [edi+1],'eiuQ' mov dword ptr [edi+5],'oN t' mov dword ptr [edi+9],' a n' mov dword ptr [edi+13],'bmuN' mov dword ptr [edi+17],'re' ret error: mov dword ptr [edi+1],'orrE' mov dword ptr [edi+5],'r' ret Eprst endp end main
НетРегистрации, если что-то можно сделать не так, то так оно и будет «if anything that can go wrong then will go wrong» Приписывается капитану Эдварду А. Мерфи, инженеру Лаборатории реактивного движения, служившему на авиабазе Эдвардс в 1949
про «под новым углом» всё же не соглашусь, ибо идея развёртки циклов и аппроксимации по табличкам известны да применялись ещё задолго до компов (вспомним те же тригонометрические таблицы). и у меня ещё вопросик накатился == а ты свой алго протестил по всему диапазону, ошибок трансляции нет? в задачах оптимазы больше юзверей, чем разрабов.. разрабов тамо единицы из единиц ЗЫ.. да, и в сущности не понимаю в чём сыр-бор. тот же хухль вполне может перекидывать задачу конвертации чисел в строки на комп юзверя, разгружая свои моща.