1. Java / Говнокод #25332

    +3

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    // https://github.com/TigerVNC/tigervnc/blob/8c6c584377feba0e3b99eecb3ef33b28cee318cb/java/com/jcraft/jsch/Buffer.java#L65-L85
    
      public void putInt(int val) {
        tmp[0]=(byte)(val >>> 24);
        tmp[1]=(byte)(val >>> 16);
        tmp[2]=(byte)(val >>> 8);
        tmp[3]=(byte)(val);
        System.arraycopy(tmp, 0, buffer, index, 4);
        index+=4;
      }
      public void putLong(long val) {
        tmp[0]=(byte)(val >>> 56);
        tmp[1]=(byte)(val >>> 48);
        tmp[2]=(byte)(val >>> 40);
        tmp[3]=(byte)(val >>> 32);
        System.arraycopy(tmp, 0, buffer, index, 4);
        tmp[0]=(byte)(val >>> 24);
        tmp[1]=(byte)(val >>> 16);
        tmp[2]=(byte)(val >>> 8);
        tmp[3]=(byte)(val);
        System.arraycopy(tmp, 0, buffer, index+4, 4);
        index+=8;
      }

    Жабовское байтоебство (судя по всему это такой ntohl) из реализации VNC.
    Вот интересно, в жабке-то unsigned типов нет нихера, но почему-то сделали unsigned двоичный сдвиг (>>>), который работает для этих встроенных signed типов как если б это были unsigned. А как насчет unsigned умножения и деления (сложение и вычитание - то один хер, без разницы, если у нас two's complement)?

    Запостил: j123123, 28 Января 2019

    Комментарии (58) RSS

    • https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

      byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive). The byte data type can be useful for saving memory in large arrays, where the memory savings actually matters. They can also be used in place of int where their limits help to clarify your code; the fact that a variable's range is limited can serve as a form of documentation.

      short: The short data type is a 16-bit signed two's complement integer. It has a minimum value of -32,768 and a maximum value of 32,767 (inclusive). As with byte, the same guidelines apply: you can use a short to save memory in large arrays, in situations where the memory savings actually matters.

      int: By default, the int data type is a 32-bit signed two's complement integer, which has a minimum value of -2^31 and a maximum value of 2^31-1. In Java SE 8 and later, you can use the int data type to represent an unsigned 32-bit integer, which has a minimum value of 0 and a maximum value of 2^32-1. Use the Integer class to use int data type as an unsigned integer. See the section The Number Classes for more information. Static methods like compareUnsigned, divideUnsigned etc have been added to the Integer class to support the arithmetic operations for unsigned integers.

      long: The long data type is a 64-bit two's complement integer. The signed long has a minimum value of -2^63 and a maximum value of 2^63-1. In Java SE 8 and later, you can use the long data type to represent an unsigned 64-bit long, which has a minimum value of 0 and a maximum value of 2^64-1. Use this data type when you need a range of values wider than those provided by int. The Long class also contains methods like compareUnsigned, divideUnsigned etc to support arithmetic operations for unsigned long.
      Ответить
      • Почему-то добавили только в Java SE 8 и то не для всех базовых целочисленных типов (byte и short они решили зачем-то не трогать). И добавили они это через какие-то классы-хуяссы.
        Ответить
        • А с byte и short там же как и в сишке - они нужны только для хранения. Вычисления всё равно идут минимум с интами.
          Ответить
          • > Вычисления всё равно идут минимум с интами.

            __m128i _mm_add_epi8 (__m128i a, __m128i b)
            Ответить
            • Вычисления идут с интами, но результат потом подрезается. Но, поскольку signed переполнение это UB, а для unsigned всё выглядит как-будто складывали байты, то ты не заметишь подвоха.

              Интересно, зачем тогда вообще это правило про каст мелочи в int перед операциями? Оно же как чайник Рассела. Ни доказать ни опровергнуть.
              Ответить
              • Видимо просто чтобы удобней было с мелочью работать если тебе нужен результат в инте. Иначе пришлось бы постоянно касты писать.
                Ответить
            • Кстати, а бывает ли SSE, которые делали что-то вроде:

              uint8_t in[8];
              uint16_t out[4];
              out[0] = in[0] * in[1];
              out[1] = in[2] * in[3];
              out[2] = in[4] * in[5];
              out[3] = in[6] * in[7];
              Ответить
              • Да, но они немного странные. Одна возвращает 4 старших половины, вторая - 4 младших.
                Ответить
        • > классы
          Эмуляция через signed чтобы не трогать JVM?
          Ответить
    • Кстати, а есть ли какие-то простые формулы для запиливания знакового умножения из беззнакового и наоборот?
      Ответить
      • Допустим, нужно перемножить два signed, а у нас только беззнаковое умножение:
        Пусть x_u, y_u –— временные беззнаковые переменные (типа uint16_t), хранящие битовые копии x, y (типа int16_t).
        Тогда x_u = x + pow(2,16)*h(x<0), где h –— функция Хевисайда;
        y_u = y + pow(2,16)*h(y<0);
        x_u*y_u = x*y + pow(2,16)*(y*h(x<0) + x*h(y<0)) + pow(2,32)*h(x<0)*h(y<0).
        Выразим x и y через x_u и y_u соответственно и найдём значение x*y через беззнаковые переменные:
        x*y = x_u*y_u - pow(2,16)*(y_u*h(x<0) + x_u*h(y<0)) +
        + pow(2,32)*h(x<0)*h(y<0)*(-1 + 1 + 1).
        От последнего слагаемого толку нет, ибо его значение за пределом разрядности.

        Итого:
        typedef union {uint16_t s; int16_t u;} container16;
        typedef union {uint32_t s; int32_t u;} container32;
        
        int32_t imul(int16_t x, int16_t y) {
            container16 x1, y1;
            container32 z1;
            x1.s = x;
            y1.s = y;
            z1.u = x1.u * y1.u + (1uL<<16)*(y1.u*(x1.s<0)+x1.u*(y1.s<0));
            return z1.s;
        }


        https://ideone.com/DDqPGb
        Ответить
        • Я наглючил. Вот как надо:
          typedef union {uint16_t u; int16_t s;} container16;
          typedef union {uint32_t u; int32_t s;} container32;
          
          int32_t imul(int16_t x, int16_t y) {
              container16 x1, y1;
              container32 z1;
              x1.s = x;
              y1.s = y;
              z1.u = x1.u * y1.u - (1uL<<16)*(y1.u*(x1.s<0)+x1.u*(y1.s<0));
              return z1.s;
          }


          Теперь всё правильно:
          https://ideone.com/7TE4No
          Ответить
      • А теперь если наоборот, у нас только знаковое умножение.
        Пусть x_s, y_s –— временные знаковые переменные (типа int16_t), хранящие копии x,y (типа uint16_t).
        Тогда x_s = x - pow(2,16)*h(x>T-1);
        y_s = y - pow(2,16)*h(y>T-1);
        где T = pow(2,15) –— постоянная Тараса для данного типа данных.
        x_s*y_s = x*y - pow(2,16)*(y*h(x>T-1) + x*h(y>T-1)) + pow(2,32)*h(x>T-1)*h(y>T-1).
        Аналогично выразим x и y через x_s и y_s и получим:
        x*y = x_s*y_s + pow(2,16)*(y_s*h(x>T-1) + x_s*h(y>T-1)) + pow(2,32)*h(x>T-1)*h(y>T-1)*(-1+1+1).
        Ответить
        • Итого:
          typedef union {uint16_t u; int16_t s;} container16;
          typedef union {uint32_t u; int32_t s;} container32;
          
          int32_t mul(uint16_t x, uint16_t y) {
              container16 x1, y1;
              container32 z1;
              x1.u = x;
              y1.u = y;
              z1.s = x1.s * y1.s + (1uL<<16)*(y1.s*(x1.s<0)+x1.s*(y1.s<0));
              return z1.u;
          }


          https://ideone.com/hinhoN
          Ответить
          • А если перенести коррекцию до умножения? Может быть тогда не придётся прибавлять сдвинутые числа?
            Ответить
            • Можно и до умножения:
              z1.s = (x1.s + (1uL<<16)*(x1.s<0)) * (y1.s + (1uL<<16)*(y1.s<0));


              Но тогда придётся x1.s и y1.s предварительно кастануть в числа вдвое большей разрядности.
              Ответить
              • Причём кастовать придётся без расширения знака, иначе получим неправильный результат.
                Ответить
              • Ну всмысле до умножения что-то простое (знаковый бит выбить?) и после умножения что-то настолько же простое. Чтобы x и y участвовали только по одному разу.
                Ответить
          • Аппаратные умножители циклоняшки оба режима умеют без проблем. Т.е. там должна быть совсем простая формулка, даже без сложений.
            Ответить
      • Мой тупой вореант:
        int32_t imul(int16_t x, int16_t y)
        {
            #define u(x) ((uint32_t)(x))
            #define s(x) ((int32_t)(x))
            return ((u(abs(x)) * u(abs(y))) ^ ((x ^ y) >> 15)) + (u(x ^ y) >> 31);
        }
        Перемножить модули и поставить соответствующий знак.

        ЩЫ. это не лисп, я прост пероритеты забуа
        Ответить
        • Для нахождения модуля же тоже есть батоёбской воренант?
          Ответить
        • Да, вот это уже ближе! А для числа тараса работает?
          Ответить
          • Какого-ток-токого числа тарсаса?
            Ответить
            • 0x80000000 для 32-х бит.
              Ответить
              • Я правильно понял: https://ideone.com/0qsVBS ?

                Вроде раьотает.
                Ответить
              • Оно самое. Его фундаментальное свойство:
                T + T = 0 (по модулю 2 в степени n).
                Или так:
                T = -T (по модулю 2 в степени n).
                Ответить
          • А в ваших "крестах" (или где-нибудь ещо) есть нечто для объявления типа такого: функция принимает параметр какого-нибудь челочисленного типа, а результат целочисленный с разрядностью в 2 раза больше чем у параметра? (ну кароч тип результата зависит от типа пораметра)
            Ответить
            • Можно наебашить double_type<T>::type через специализацию, которая для T=uint32_t возвращает тип uint64_t и т.п.
              Ответить
            • Джвадцать лет жду какого-нибудь «template std::variadic_int<size_t Bits>». Не дождусь, скорее всего.
              Ответить
            • Так? https://godbolt.org/z/UTZrWh
              template<typename T> struct double_type {};
              template<> struct double_type<uint8_t> { typedef uint16_t type; };
              template<> struct double_type<uint16_t> { typedef uint32_t type; };
              template<> struct double_type<uint32_t> { typedef uint64_t type; };
              
              template<typename T>
              auto mul(T a, T b) {
                  using DT = typename double_type<T>::type;
                  return DT(a) * DT(b);
              }
              Ответить
              • Гадость какая... То ли дело "Си":
                // https://tio.run/##KyhKLSjKT/7/n0s5JTUtMy9VwdkxJF6jQqdSU6FCWbkSWRgiCpfngsu5@Ic6@bjGZ@aVWMSXKAApQ7P4EiyyIGGQtLERVmmQMEjazARTOiQywFUjBGy7BlRIRyEE2Q2ubvG@oT4gNTFcWDTlluaAdWiEKCQCaYUkTYVqoEoFIChKLSktylPQ0EDVppmoqaClgC6YZA3UVcvFBbMP6m5Nrv//AQ
                
                #define CAT_(x,y) x##y
                #define CAT(x,y) CAT_(x,y)
                
                #define DOUBLE_int8_t int16_t
                #define DOUBLE_int16_t int32_t
                #define DOUBLE_int32_t int64_t
                #define DOUBLE_TYPE(T) CAT(DOUBLE_, T)
                
                #define DEF_MUL(T) \
                DOUBLE_TYPE(T) CAT(mul_, T)(T a, T b) { \
                    return ((DOUBLE_TYPE(T))a) * (DOUBLE_TYPE(T))b; \
                }
                
                DEF_MUL(int32_t)
                Ответить
        • Странно, что за монго лет так никто и не придумад лакальные дефайны.
          Ответить
      • Если у нас есть знаковые int32_t, и надо перемножить их как беззнаковые, и при этом нам доступно умножение через более широкие знаковые, например int64_t, то всё вообще очень просто.

        int32_t mul(int32_t a, int32_t b)
        {
          int64_t a_64 = 0;
          int64_t b_64 = 0;
          memcpy(&a_64, &a, sizeof(a));
          memcpy(&b_64, &b, sizeof(b));
          int64_t c_64 = a_64 * b_64;
          int c;
          memcpy(&c, &c_64, sizeof(c));
          return c;
        }


        хотя тут могут быть ньюансы с endian
        Ответить
        • Ну это не то. Всё равно для какого-нибудь int128 не найдется int256.
          Ответить
          • Зато найдется какой-нибудь biginteger. Или можно самому скостылить
            Ответить
            • > скостылить
              На темплейтах, чтобы конпелятор мог оптимизнуть под конкретную длину?
              Ответить
              • Говно эти ваши темплейты
                Ответить
                • Кстати, а можно как-то протаскивать carry flag между блоками ассемблера? А то можно было бы написать рекурсивный темплейт, который раскрывается в цепочку adc нужной длины...
                  Ответить
                  • Блин, на выход флаг можно описать, а на вход - нет.
                    Ответить
                • Ты злой. Это от отсутствия темплейтов. Я пишу только на темплейтах и чувствую себя замечательно! Попробуй и ты вычислить траекторию полёта ракеты на Марс в компайл-тайме!
                  Ответить
      • показать все, что скрытоvanished
        Ответить
        • Ответить
        • Может дело в "Си"? Попробуй "assembler", я помню у меня были там ошиппке когда я путал "mul" и "imul".
          Ответить
          • Да не, всё норм. Младшие 32 бита совпадают. А старшие, в которых есть разница, сишка всё равно отбросит. Т.е. разница есть только в асме.
            Ответить
          • показать все, что скрытоvanished
            Ответить
            • osyol ebx
              Чот не компилица. Это вообще x86?
              Ответить
              • Это наверное синтаксис «AT&T». Суффикс «l» означает, что аргумент размером в двойное слово. Регистр ebx как раз подходит, только в «AT&T» он пишется с префиксом «%»:
                osyol %ebx

                А если будешь использовать встроенный ассемблер в «gcc» или в «clang», то знак «%» ещё нужно удвоить, чтобы по ошибке не подставился мокрос.
                Ответить
                • OS Yield Operation. Для кооперативной многозадачности видимо.
                  Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • А что такого? В видюхах например стоят аппаратные шедулеры, которые тысячи тредов могут обслуживать. Но там с переключением совсем просто - у каждого треда свои регистры.
                      Ответить
                      • у x86 тоже, на резервационных станциях стоят
                        Ответить
                        • Ну они всего пару гипертредов переключают. И регистров там не десятки тысяч
                          Ответить
                  • А для вытесняющей какая инструкция?
                    Ответить
                    • для вытесняющей иснтуркция по программированю таймера, кажется в наше нелегкое время он прямо в APIC встроен
                      Ответить
                    • kozyol %ebx. Kernel Overhead-Zero Yield Operation. Переключение контекста с нулевым оверхедом для ядра.
                      Ответить
                      • LONGBOOL KoZYOL(HANDLE hWND, LPVOID lpBuffer, LPSIZE psz, LPVOID reserved, LPVOID reserved2, LPVOID unused, LPVOID unused2, GUID unusedGuid); // Deprecated. Use KoZYOL2.
                        Ответить
    • > почему-то сделали
      Ну иначе байтоёбить было бы совсем уныло.
      Ответить

    Добавить комментарий