Поплавок 754 до Хеммінгу


29

Вам буде дано як вхід ціле число kв діапазоні від -4503599627370496(-2 52 ) до 4503599627370496(2 52 ). Як відомо , цілі числа в цьому діапазоні можуть бути представлені точно у вигляді подвійної точності з плаваючою комою.

Ви повинні вихід на вагу Хеммінга (кількість одиниць) в кодуванні kв форматі binary64 . Для цього використовується 1 біт для знака, 11 біт для експонента (закодований зі зміщенням) і 52 для мантіси; див. вищенаведене посилання для детальної інформації.

Як приклад , число 22представлено як

0 10000000011 0110000000000000000000000000000000000000000000000000

Оскільки такі є 5, вихід є 5.

Зауважте, що випромінювання не впливає на результат, тому ви можете сміливо використовувати фактичне внутрішнє представлення на вашому пристрої значень подвійної точності для обчислення результатів.

Додаткові правила

Тестові справи

22                ->   5
714               ->   6
0                 ->   0
1                 ->  10
4503599627370496  ->   5
4503599627370495  ->  55
1024              ->   3
-1024             ->   4
-4096             ->   5
1000000000        ->  16
-12345678         ->  16

1
Чи маєте ви намір, що функції можуть приймати свої дані вже у binary64форматі з плаваючою комою , якщо вони хочуть? Деякі люди ( в тому числі себе, спочатку) інтерпретували питання, вимагаючи , щоб функції приймають вхідні сигнали як цілого типу , як C - х long. На мові C ви можете стверджувати, що мова буде конвертувати для вас, як і коли ви телефонуєте sqrt((int)foo). Але є кілька відповідей машинного коду на x86 (як codegolf.stackexchange.com/a/136360/30206 і моя), які припускали, що нам доведеться приймати 64-бітні цілі числа. Прийняття binary64значення дозволить заощадити 5 байт.
Пітер Кордес

Якщо так, то все те, що стосується обмеженого діапазону, - це лише на випадок, якщо хтось захотів зламати перетворення на бінарний бітовий візерунок, а не замість штампування? Або для мов без типового покарання? Гм, цікавим завданням може бути додавання експонента та binary64мантіси цілих чисел base2. Якщо вам потрібно все-таки обробити їх окремо, можливо, варто зробити щось інше, ніж тип-каламбур і петлю над усіма бітами.
Пітер Кордес

2
@PeterCordes Так, ви можете ввести номер у вигляді номера з плаваючою комою. Обмежений діапазон - переконатися, що подання з плаваючою комою є точним
Луїс Мендо

Добре, дякую. Я думаю, ви хотіли залишити можливість написання функції, яка займає longсимволи a , тому ви не могли просто сказати жодного бінарного64 double, оскільки не всі парні цілі числа. Але всі цілі значення doubles можуть бути перетворені на longта назад, аж до меж long. (Як ви зазначаєте, зворотний зв'язок не відповідає дійсності. Ви отримуєте найближчий представлений double, припускаючи режим округлення за замовчуванням). У всякому разі, це було цілком справедливим способом постановки питання; Я просто не прочитав його уважно>. <
Пітер Кордес

"Зверніть увагу, що витривалість не впливає на результат, тому ви можете сміливо використовувати фактичне внутрішнє представлення на вашому пристрої значень подвійної точності для обчислення результатів." якщо ваша машина не використовує формат з плаваючою точкою IEEE ...
Джеррі Єремія

Відповіді:


8

MATL , 5 байт

3Z%Bz

Спробуйте в Інтернеті!

Точна транслітерація моєї відповіді MATLAB. Зауважте, що введення та вихід є неявними. -2 байти завдяки Луїсу Мендо.

3Z%   % Typecast: changes input (implicitly taken and converted to double) to uint64 without changing underlying bits
B     % Convert integer to array of 1s and 0s
z     % Count nonzero entries

33

x86_64 машинна мова (Linux), 16 байт

0:       f2 48 0f 2a c7          cvtsi2sd %rdi,  %xmm0
5:       66 48 0f 7e c0          movq     %xmm0, %rax
a:       f3 48 0f b8 c0          popcnt   %rax,  %rax
f:       c3                      retq

Приймає єдиний 64-бітний цілий параметр в RDI, перетворює його на значення з плаваючою комою в XMM0, зберігає ці біти назад RAX, а потім обчислює вагу оббивання RAX, залишаючи результат, RAXщоб він міг бути повернутий абоненту.

Потрібен процесор, який підтримує POPCNTінструкцію, які будуть Intel Nehalem, AMD Barcelona та пізніші мікроархітектури.

Для того, щоб спробувати його в Інтернеті! , компілюйте та запустіть наступну програму C:

#include<stdio.h>
const char g[]="\xF2\x48\x0F\x2A\xC7\x66\x48\x0F\x7E\xC0\xF3\x48\x0F\xB8\xC0\xC3";
#define f(x) ((int(*)(long))g)(x)

int main(int a){
  printf("%d\n",f(22));
  printf("%d\n",f(714));
  printf("%d\n",f(0));
  printf("%d\n",f(1));
  printf("%d\n",f(4503599627370496L));
  printf("%d\n",f(4503599627370495L));
  printf("%d\n",f(1024));
  printf("%d\n",f(-1024));
  printf("%d\n",f(-4096));
  printf("%d\n",f(1000000000));
  printf("%d\n",f(-12345678));
}

2
+1, правильний інструмент для роботи! Це може бути єдиний раз, коли x86 може законно конкурувати з гольф-мовами або перемогти Jelly. :)
DJMcMayhem

2
Ew, синтаксис AT&T? Ви можете використовувати objdump -drwC -Mintelдля розбирання в синтаксисі Intel. Якщо у вас вказівник у реєстрі, який ви могли використовувати для зберігання / перезавантаження, ви можете зберегти байти за допомогою movaps [rsi], xmm0/ popcnt rax, [rsi]. (movaps - це лише 3 байти, що на 2 коротше, ніж movq.) Але це не допомагає тут, оскільки [rsp-24]займає 2 зайвих байта (SIB від використання RSP в якості бази, плюс disp8). І ці зайві байти потрібні і в магазині, і в перезавантаженні. Ну добре, я подумав, що побачив економію, але немає: /
Пітер Кордес

Я врятував 4 байти за допомогою спеціального режиму викликів . Або все-таки збережіть 2 байти з тим самим режимом виклику, що і цей, використовуючи інструкції x87.
Пітер Кордес

1
@DJMcMayhem: Можливо, не єдиний раз. Досі немає відповідей на мові гольфу на виклик Extreme Fibach (надрукуйте перші 1000 цифр Fib (1 мільярд), і мій відповідь на машинний код x86 (105 байт швидко або 101 байт, який працює за 5 хвилин замість 1 хвилини) не набагато більший, ніж деякі інші відповіді, і всі вони є мовами із вбудованими цілими числами з розширеною точністю.
Пітер Кордес,

2
Або простіший виклик (і без вимоги до продуктивності), кольоровий ключ, що поєднує масив цілих чисел . Моя відповідь машинного коду - це половина довжини піт-відповіді.
Пітер Кордес

11

C (gcc) , 82 68 байт

9 байт завдяки Нілу.

зло злом з плаваючою точкою

s;f(long n){double d=n;n=*(long*)&d;for(s=0;n;n*=2)s+=n<0;return s;}

Спробуйте в Інтернеті!


Я знав, що ти будеш першим, я просто не очікував мови :-D
Луїс Мендо

@LuisMendo Я просто думав, що на цій мові це буде зручно ... Я не знаю інших мов, які можуть це зробити
Leaky Nun

2
Збережіть 9 байт, змістивши інший шлях: ... ;long l=... ;l*=2;)s+=l<0;...
Ніл

1
Для цього, звичайно, потрібна реалізація C із 64-бітною версією long. Він працює на Linux x86-64, але не працює в Windows. Я б запропонував сказати "gcc з 64-розрядною long", оскільки gcc працює на багатьох платформах, багато з яких мають різні ABI.
Пітер Кордес

1
@ Коментар Петра, чому я додав "LP64" у редагуванні. Я також переставив інший текст так, як вважав, що це більш логічний порядок. Я думаю, вам не сподобалася ця зміна, і повернути її назад, але LP64 - це стандартний термін, який описує ABI, де longs and pointers - це 64-бітні значення (порівняно з ILP64, де ints також 64-розрядні, або LLP64, як використовується в Windows, де тільки довгі довгі та покажчики 64-бітні, а довгі - 32-бітні). Можливо, я мав би додати більше пояснень або вкладене посилання на відповідну статтю у Вікіпедії.
Коді Грей

8

Python 3 , 72 71 байт

1 байт завдяки Лінні.

lambda n:n and(bin(1020+len(bin(abs(n))))+bin(abs(n))).count('1')-(n>0)

Спробуйте в Інтернеті!

Пояснення

Формат бінарного64 складається з трьох компонентів:

  • перший біт - це бітовий знак, тобто 1якщо число від’ємне
  • наступні 11 біт зберігають експонент із доданим 1023
  • наступні 52 біти зберігають значення або мантісу.

n and(…)-(n>0)чи байт коротший, ні?
Лінн

Або int-> float, або будь-який поплавок взагалі для цього питання.
user2357112 підтримує Monica

8

C (gcc) , 47 байт

f(double n){n=__builtin_popcountl(*(long*)&n);}

Це не портативно; він був протестований на gcc 7.1.1 на x86_64 під управлінням Linux, без прапорців компілятора.

Спробуйте в Інтернеті!


1
Вхід має бути цілим числом. Або це нормально , щоб дозволити абоненту ручки , що з допомогою неявного перетворення longдля doubleна місці виклику?
Пітер Кордес

1
Крім того , спираючись на лапі поведінки компілятора статися , щоб залишити nв raxс ООН-оптимізований код досить паскудний. Якщо ввімкнено -O3, він виходить з ладу , тому це не просто gcc взагалі, це gcc на x86-64 з 64-розрядною версією з longвідключеною оптимізацією. Якщо ви поставите всі ці вимоги у своїй відповіді, я б проголосував. Я припускаю, що існують платформи gcc, які мають 64-розрядні, longале вони залишають popcountlрезультат у реєстрі, відмінному від регістра повернутого значення.
Пітер Кордес

1
Я взяв ціле число в математичному сенсі. Я додав характеристики моїх тестових середовищ, тому що я не впевнений, що gcc, x86-64 та 64-бітні тривалості достатньо. Однак, принаймні, на x86 функції, що не мають віддачі, працюють із gcc (і tcc) частіше, ніж ні.
Денніс

Так, я просто перечитував запитання, і я погоджуюся з тим, що прийняття аргументу як необхідного doubleмає бути добре. Це нічого не говорить про необхідність функції прийняти її у форматі base2. І так, різні версії gcc можуть видавати різний код, тому це теж важливо. (Цікавий факт: без -mpopcnt, ПКУ не використовуватиме popcntinsn, і буде випромінювати послідовність команд , щоб емулювати деякі архітектури не мають інструкції POPCNT на всіх, так що . __builtin_popcountlЗавжди використовувати деяку послідовність insns)
Пітер Кордес

Так, багато (більшість?) __builtin_*Функцій мають застарілі версії, щоб уникнути створення незаконних інструкцій. -march=nativeвикористовує popcntqлише за наявності.
Денніс


6

C (gcc), 63 байти

f(double d){long s=0,n=*(long*)&d;for(;n;n*=2)s+=n<0;return s;}

Це рішення засноване на відповіді @ LeakyNun, але оскільки він не хоче вдосконалювати власну відповідь, я розміщую тут ще версію для гольфу.

Спробуйте в Інтернеті


2
Я дуже сумніваюся, що хтось не хоче вдосконалювати свою відповідь.
Містер Xcoder

1
@ Mr.Xcoder. Гаразд, я зберігатиму це тут, поки він не відредагує власну відповідь. Якщо він не хоче редагувати, це залишиться тут. Я опублікував це вдосконалення як коментар до його відповіді, і він відхилив її.

1
Я думаю, що вхід повинен бути цілим типом, а не реальним.
стельовий кіт

3
@ThePirateBay Я не побачив ваш коментар до моєї відповіді, і я все ще не бачу його зараз.
Leaky Nun

9
Рішення запропонувати вдосконалення або опублікувати власну відповідь - ваше, але 6 хвилин - навряд чи близько години .
Денніс

5

C #, 81 70 68 байт

d=>{unsafe{long l=*(long*)&d,s=0;for(;l!=0;l*=2)s-=l>>63;return s;}}

Збережіть 11 байт завдяки монахині @Leaky.
Збережено 2 байти завдяки @Neil.

Спробуйте в Інтернеті! Використовує System.BitConverter.DoubleToInt64Bitsзамість unsafeкоду, оскільки я не міг змусити TIO працювати з ним.

Повна / відформатована версія:

namespace System
{
    class P
    {
        static void Main()
        {
            Func<double, long> f = d =>
            {
                unsafe
                {
                    long l = *(long*)&d, s = 0;

                    for (; l != 0; l *= 2)
                        s -= l >> 63;
                    return s;
                }
            };

            Console.WriteLine(f(22));
            Console.WriteLine(f(714));
            Console.WriteLine(f(0));
            Console.WriteLine(f(1));
            Console.WriteLine(f(4503599627370496));
            Console.WriteLine(f(4503599627370495));
            Console.WriteLine(f(1024));
            Console.WriteLine(f(-1024));
            Console.WriteLine(f(-4096));
            Console.WriteLine(f(1000000000));
            Console.WriteLine(f(-12345678));

            Console.ReadLine();
        }
    }
}

for(;l!=0;l*=2)і вам не знадобиться тернар
Leaky Nun

@LeakyNun Дякую, що я чухав голову над цим віками.
TheLethalCoder

Ви можете використовувати s-=l>>31?
Ніл

@Neil Не працює. Я припускаю, що ви маєте на увазі замінити s+=l<0?1:0?
TheLethalCoder

Моє ліжко; lце довге, тож воно потребує s-=l>>63?
Ніл

4

Python 2 , 69 байт

-12 байт, завдяки @ ASCII

lambda n:bin(*unpack('Q',pack('d',n))).count('1')
from struct import*

Спробуйте в Інтернеті!



1
Гольф ваш підхід, 76 байт , я рекомендую лише підхід ASCII
Містер Xcoder

@ Mr.Xcoder !це не потрібно, оскільки порядок байтів тут не має значення
лише ASCII


@ ASCII Лише упакуйте розпаковане. Спасибі: D
Dead Possum

4

JavaScript (ES6), 81 80 77 байт

f=
n=>new Uint8Array(Float64Array.of(n).buffer).map(g=i=>i&&g(i^i&-i,x++),x=0)|x
<input oninput=o.textContent=f(this.value)><pre id=o>0

Редагувати: Збережено 1 байт завдяки @Arnauld. Збережено 3 байти завдяки @DocMax.


Не могли б ви зробити g(i^i&-i,x++)-1 байт?
Арнольд

@Arnauld Мені було цікаво, чи був гофрист, трохи подвійний, дякую, що знайшов його!
Ніл

1
Ще 3, якщо ви заміните new Float64Array([n])наFloat64Array.of(n)
DocMax

4

машинний код x86-64, 12 байт для int64_tвведення

6 байт для doubleвведення

Потрібно popcntрозширення ISA ( CPUID.01H:ECX.POPCNT [Bit 23] = 1).

(Або 13 байт, якщо для зміни аргументу на місці потрібно записати всі 64-бітні замість того, щоб залишати сміття у верхній частині 32. Я думаю, що розумно стверджувати, що абонент, ймовірно, хотів би завантажити хоч низький 32b, і нуль x86 -розширюється від 32 до 64 неявно під час кожної 32-бітної операції, але це не зупиняє абонента робити add rbx, [rdi] чи щось подібне.)

Інструкції x87 коротші, ніж більш очевидна SSE2 cvtsi2sd/ movq(використовується у відповіді на @ roofcat ), а [reg]режим адресації має той самий розмір, що і reg: лише байт mod / rm.

Трюк полягав у тому, щоб придумати спосіб передавати значення в пам'яті, не потребуючи занадто багато байтів для адресних режимів. (наприклад, передача стека не така вже й велика.) На щастя, правила дозволяють аргументи для читання / запису або окремі вихідні аргументи , тому я можу просто заставити абонента передати мені вказівник на пам'ять, яку мені дозволяється писати.

Зателефонувавши з C з підписом: void popc_double(int64_t *in_out); Дійсно лише низький 32b результату, що може бути дивним для C, але природним для asm. (Для виправлення цього потрібен префікс REX на остаточному сховищі ( mov [rdi], rax), тому ще один байт.) У Windows змініть rdiнаrdx , оскільки Windows не використовує x86-64 System V ABI.

Список NASM. Посилання TIO має вихідний код без розбирання.

  1  addr    machine      global popcnt_double_outarg
  2          code         popcnt_double_outarg:
  3                           ;; normal x86-64 ABI, or x32: void pcd(int64_t *in_out)
  4 00000000 DF2F             fild qword  [rdi]    ; int64_t -> st0
  5 00000002 DD1F             fstp qword  [rdi]    ; store binary64, using retval as scratch space.
  6 00000004 F3480FB807       popcnt rax, [rdi]
  7 00000009 8907             mov    [rdi], eax    ; update only the low 32b of the in/out arg
  8 0000000B C3               ret
    # ends at 0x0C = 12 bytes

Спробуйте в Інтернеті! Включає_start тестову програму, яка передає їй значення і закінчується статусом виходу = повернення popcnt. (Відкрийте вкладку "налагодження", щоб побачити її.)

Передача окремих покажчиків вводу / виводу також спрацювала (rdi та rsi в x86-64 SystemV ABI), але тоді ми не можемо розумно знищити 64-бітний вхід або як легко обгрунтувати необхідність 64-бітового вихідного буфера під час запису лише низький 32b.

Якщо ми хочемо стверджувати, що ми можемо взяти покажчик на вхідне ціле число і знищити його, повертаючи при цьому вихід rax, тоді просто опустімо mov [rdi], eaxз popcnt_double_outarg, знизивши його до 10 байт.


Альтернатива без дурних викликів-конвенцій, 14 байт

використовувати стек як місце для нуля, pushщоб дістати його туди. Використовуйте push/ popдля копіювання регістрів у 2 байти замість 3 для mov rdi, rsp. ( [rsp]завжди потрібен байт SIB, тому варто витратити 2 байти на копіювання rspперед трьома інструкціями, які ним користуються.)

Телефонуйте з C з цим підписом: int popcnt_double_push(int64_t);

 11                               global popcnt_double_push
 12                               popcnt_double_push:
 13 00000040 57                       push   rdi         ; put the input arg on the stack (still in binary integer format)
 14 00000041 54                       push   rsp         ; pushes the old value (rsp updates after the store).
 15 00000042 5A                       pop    rdx         ; mov      rdx, rsp
 16 00000043 DF2A                     fild   qword [rdx]
 17 00000045 DD1A                     fstp   qword [rdx]
 18 00000047 F3480FB802               popcnt rax,  [rdx]
 19 0000004C 5F                       pop    rdi         ; rebalance the stack
 20 0000004D C3                       ret
    next byte is 0x4E, so size = 14 bytes.

Прийом введення у doubleформаті

Питання просто говорить про те, що це ціле число в певному діапазоні, а не те, що воно повинно бути в двійковому цілому поданні base2. Прийняття doubleвводу означає, що більше немає сенсу використовувати x87. (Якщо ви не використовуєте користувацьку конвенцію про виклики, коли doubles передаються в регістри x87. Потім зберігайте в червоній зоні під стеком і виконайте popcnt.)

11 байт:

 57 00000110 66480F7EC0               movq    rax, xmm0
 58 00000115 F3480FB8C0               popcnt  rax, rax
 59 0000011A C3                       ret

Але ми можемо використовувати той самий трюк проходження посилання, як і раніше, щоб зробити 6-байтну версію: int pcd(const double&d);

 58 00000110 F3480FB807               popcnt  rax, [rdi]
 59 00000115 C3                       ret

6 байт .



3

MATLAB, 36 байт

@(n)nnz(de2bi(typecast(n,'uint64')))

Використовуючи той факт, який de2biне тільки коротший dec2bin, але й забезпечує результат в одиницях та нулях, а не ASCII 48, 49.


3

Java (64, 61, 41 байт)

Цілком просте використання стандартної бібліотеки (Java SE 5+):

int f (довгий n) {return Довгий. bitCount (Double. doubleToLongBits (n));}

Вклад Kevin Cruijssen (Java SE 5+):

int f(Long n){return n.bitCount(Double.doubleToLongBits(n));}

Вклад Kevin Cruijssen (Java SE 8+, лямбда-функція):

n->n.bitCount(Double.doubleToLongBits(n))

Чудово зроблено! :-)
Leaky Nun

1
Приємна відповідь, +1 від мене. Ви можете грати в три байти, взявши параметр як Long nі використовувати n.bitCount(...)замість Long.bitCount(...). Крім того, якщо ви користуєтеся Java 8+, ви можете n->n.bitCount(Double.doubleToLongBits(n))
переграти

2

Просто щоб спробувати інший, більш безпечний підхід TheLethalCoder , я придумав це (шкода, що C # має такі довгі назви методів):

C # (.NET Core) , 76 + 13 байт

d=>Convert.ToString(BitConverter.DoubleToInt64Bits(d),2).Split('1').Length-1

Спробуйте в Інтернеті!

Кількість байтів включає 13 байт для using System;. Спочатку мені потрібно перетворити doubleна а, longщо має однакове бінарне представлення, потім я можу перетворити його у бінарне string, а потім я рахую 1s лише шляхом розбиття рядка та підрахунку підрядків мінус 1.


Хороша альтернатива, але вам потрібно включити usingсвій рахунок до кількості байтів.
TheLethalCoder

Використання Linq для 95 байт тільки кілька: namespace System.Linq;{d=>Convert.ToString(BitConverter.DoubleToInt64Bits(d),2).Count(c=>c>48)}. Хоча я не перевіряв цього, він повинен працювати.
TheLethalCoder

@TheLethalCoder працює, але я намагався уникати Linq, тому мені не довелося додавати другу usingдирективу.
Чарлі

1
Коли ви додасте друге, це стане namespaceв нагоді. Але так у цьому випадку уникати Linq було дещо дешевше. Просто хотів прокоментувати його підхід у випадку, якщо у вас були ідеї, як скоротити його, щоб заощадити ваші байти.
TheLethalCoder

@TheLethalCoder, Sum(c=>c&1)коротше. АбоSum()-768
Пітер Тейлор


1

постійного струму, 79 байт

[pq]su[-1r]st0dsb?dd0=u0>tsa[1+]ss[la2%1=slb1+sblad2/sa1<r]dsrxlb1022+sa0lrx+1-

Вихід залишається вгорі стека.
Пізніше я додам пояснення.

Спробуйте в Інтернеті!

Зауважте, що негативним числам передує _, а не -.



1

C, 67 байт

int i;g(char*v){int j=v[i/8]&1<<i%8;return!!j+(++i<64?g(v):(i=0));}

контрольний код та результати

#define R     return
#define u32 unsigned
#define F        for
#define P     printf

int main()
{/*           5   6 0 10                5               55    3      4       16*/
 double v[]={22,714,0,1 ,4503599627370496,4503599627370495,1024, -1024, -12345678};
 int i; 

 F(i=0;i<9;++i)
     P("%f = %d\n", v[i], g(&v[i]));
 R 0;
}

>tri4
22.000000 = 5
714.000000 = 6
0.000000 = 0
1.000000 = 10
4503599627370496.000000 = 5
4503599627370495.000000 = 55
1024.000000 = 3
-1024.000000 = 4
-12345678.000000 = 16

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.