ลอย 754 สู่ Hamming


29

คุณจะได้รับเป็นอินพุตจำนวนเต็มkในช่วงตั้งแต่-4503599627370496(−2 52 ) ถึง4503599627370496(2 52 ) ในฐานะที่เป็นที่รู้จักกันดีจำนวนเต็มในช่วงนี้สามารถแทนค่าทศนิยมที่มีความแม่นยำสองเท่า

คุณควรส่งออกน้ำหนัก Hamming (จำนวนคน) ของการเข้ารหัสของkใน รูปแบบ binary64 สิ่งนี้ใช้ 1 บิตสำหรับเครื่องหมาย, 11 บิตสำหรับเลขชี้กำลัง (เข้ารหัสด้วยออฟเซ็ต) และ 52 สำหรับ mantissa; ดูลิงค์ด้านบนเพื่อดูรายละเอียด

ในฐานะที่เป็นตัวอย่างจำนวน22จะแสดงเป็น

0 10000000011 0110000000000000000000000000000000000000000000000000

เนื่องจากมีคนออกเป็น55

โปรดทราบว่า endianness จะไม่ส่งผลกระทบต่อผลลัพธ์ดังนั้นคุณสามารถใช้การแทนค่าภายในที่แท้จริงของเครื่องเพื่อคำนวณผลลัพธ์ได้อย่างปลอดภัย

กฎเพิ่มเติม

กรณีทดสอบ

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

1
คุณตั้งใจจะให้ฟังก์ชั่นสามารถรับอินพุตได้ในbinary64รูปแบบทศนิยมหรือไม่หากต้องการ? บางคน (รวมทั้งตัวเองในขั้นต้น) ได้รับการตีความคำถามเป็นฟังก์ชั่นที่จำเป็นต้องยอมรับปัจจัยการผลิตเป็นชนิดจำนวนเต็มเช่น longC ใน C sqrt((int)foo)คุณสามารถยืนยันว่าภาษาจะแปลงสำหรับคุณเช่นเดียวกับเมื่อคุณเรียก แต่มีรหัสเครื่อง x86 บางคำตอบ asm (เช่นcodegolf.stackexchange.com/a/136360/30206และของฉัน) ซึ่งทั้งคู่ตั้งสมมติฐานว่าเราต้องยอมรับอินพุตจำนวนเต็ม 64 บิต การยอมรับbinary64ค่าจะประหยัดได้ 5 ไบต์
Peter Cordes

ถ้าเป็นเช่นนั้นทุกสิ่งที่เกี่ยวกับช่วง จำกัด เป็นเพียงในกรณีที่มีคนต้องการแฮ็กแปลงเป็นรูปแบบไบนารี 64 บิตตัวเองแทนที่จะเป็นประเภท -punning? หรือภาษาที่ไม่มีการสะกดคำแบบพิมพ์? อืมความท้าทายที่น่าสนใจอาจจะเป็นการเพิ่มเลขชี้กำลังและ mantissa ของbinary64จำนวนเต็ม base2 หากคุณต้องการจัดการแยกต่างหากมันอาจคุ้มค่าที่จะทำสิ่งอื่นนอกเหนือจากการพิมพ์และวนรอบบิตทั้งหมด
Peter Cordes

2
@PeterCordes ใช่คุณสามารถป้อนในรูปแบบของจำนวนจุดลอยตัว AA ช่วงที่ จำกัด คือการทำให้แน่ใจว่าการแสดง
จุดทศนิยม

โอเคขอบคุณ. ฉันเดาว่าคุณต้องการออกจากตัวเลือกในการเขียนฟังก์ชั่นที่ใช้ a longดังนั้นคุณไม่สามารถพูดเลขฐานสองใด ๆ ได้เลยdoubleเพราะไม่ใช่ทุกคู่ที่เป็นจำนวนเต็ม แต่ทั้งหมดจำนวนเต็มมูลค่าdoubles สามารถแปลงไปlongและกลับมาถึงขีด จำกัด longของ (ในขณะที่คุณชี้ให้เห็นย้อนกลับไม่เป็นความจริงคุณจะได้เป็นตัวแทนที่ใกล้ที่สุดdoubleสมมติว่าโหมดการปัดเศษเริ่มต้น) อย่างไรก็ตามนี่เป็นวิธีที่ถูกต้องในการตั้งคำถาม ฉันไม่ได้อ่านมันอย่างถี่ถ้วน>. <
Peter Cordes

"โปรดทราบว่า endianness จะไม่ส่งผลต่อผลลัพธ์ดังนั้นคุณสามารถใช้การแทนค่าภายในที่แท้จริงของเครื่องเพื่อคำนวณผลลัพธ์ได้อย่างปลอดภัย" เว้นแต่ว่าเครื่องของคุณจะไม่ใช้รูปแบบจุดลอยตัว IEEE ...
Jerry Jeremiah

คำตอบ:


8

MATL , 5 ไบต์

3Z%Bz

ลองออนไลน์!

การทับศัพท์ที่แน่นอนของคำตอบ MATLAB ของฉัน โปรดทราบว่าอินพุตและเอาต์พุตมีความหมายโดยนัย -2 ไบต์ขอบคุณ Luis Mendo

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แล้วคำนวณน้ำหนัก hamming ของ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 สามารถแข่งขันกับภาษากอล์ฟได้อย่างถูกต้องหรือเอาชนะเยลลี่ :)
DJMcMayhem

2
ใช่ไวยากรณ์ AT&T หรือไม่ คุณสามารถใช้objdump -drwC -Mintelเพื่อแยกส่วนในไวยากรณ์ของ Intel หากคุณมีตัวชี้ในการลงทะเบียนที่คุณสามารถใช้ในการจัดเก็บ / โหลดคุณสามารถประหยัดไบต์ด้วย/movaps [rsi], xmm0 popcnt rax, [rsi](movaps เพียง 3 ไบต์สั้นกว่า movq 2) แต่นั่นไม่ช่วยอะไรเพราะ[rsp-24]ใช้ 2 ไบต์เพิ่มเติม (SIB จากการใช้ RSP เป็นฐานบวก disp8) และไบต์พิเศษเหล่านั้นจำเป็นต้องใช้ทั้งในการจัดเก็บและโหลด โอ้ฉันคิดว่าฉันเห็นการประหยัด แต่ไม่: /
ปีเตอร์กอร์เดส

ผมบันทึกไว้ 4 ไบต์มีการเรียกประชุมที่กำหนดเอง หรือยังบันทึก 2 ไบต์ด้วยแบบแผนการโทรเช่นนี้โดยใช้คำแนะนำ x87
Peter Cordes

1
@DJMcMayhem: อาจไม่ใช่ครั้งเดียว ยังไม่มีคำตอบภาษากอล์ฟในการแข่งขัน Extreme Fibonacci (พิมพ์ 1,000 หลักแรกของ Fib (1 พันล้าน) และคำตอบของรหัสเครื่อง x86ของฉัน(เร็ว 105 ไบต์หรือ 101 ไบต์ที่ทำงานใน 5 นาทีแทน 1 นาที)ไม่มากไปกว่าคำตอบอื่น ๆ และพวกเขาทั้งหมดในภาษาที่มีจำนวนเต็มความแม่นยำขยายสร้างขึ้น
Peter Cordes

2
หรือความท้าทายที่เรียบง่าย (และโดยไม่ต้องการประสิทธิภาพการทำงาน) ความเข้มของสีที่สำคัญผสมอาร์เรย์ของจำนวนเต็ม คำตอบของรหัสเครื่องของฉันคือความยาวครึ่งหนึ่งของคำตอบของ pyth
Peter Cordes

11

C (gcc) , 82 68 ไบต์

9 ไบต์ขอบคุณ Neil

การแฮ็กระดับบิตที่ลอยระดับความชั่วร้าย

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

ลองออนไลน์!


ฉันรู้ว่าคุณจะเป็นคนแรกฉันไม่ได้คาดหวังภาษา :-D
Luis Mendo

@ LuisMendo ฉันแค่คิดว่ามันจะสะดวกในภาษานั้น ... ฉันไม่รู้ภาษาอื่นที่สามารถทำได้
Leaky Nun

2
บันทึก 9 ไบต์ด้วยการขยับอย่างอื่น: ... ;long l=... ;l*=2;)s+=l<0;...
Neil

1
นี้แน่นอนต้องมีการดำเนินการกับ C long64 มันใช้งานได้กับ x86-64 Linux แต่อาจล้มเหลวบน Windows ฉันขอแนะนำให้พูดว่า "gcc กับ 64 บิตlong" เนื่องจาก gcc ทำงานบนหลาย ๆ แพลตฟอร์มส่วนใหญ่มี ABIs ที่แตกต่างกัน
Peter Cordes

1
@ ความคิดเห็นของ Peter คือเหตุผลที่ฉันเพิ่ม "LP64" ในการแก้ไข ฉันจัดเรียงข้อความอื่นในสิ่งที่ฉันคิดว่าเป็นลำดับที่สมเหตุสมผลกว่า ฉันเดาว่าคุณไม่ชอบการเปลี่ยนแปลงนั้นและย้อนกลับ แต่ LP64 เป็นคำมาตรฐานที่อธิบาย ABI โดยที่ longs และ pointers นั้นมีค่า 64- บิต (เมื่อเทียบกับ ILP64 ซึ่ง ints เป็น 64-bit หรือ LLP64 ตามที่ใช้บน Windows ที่มีความยาวและตัวชี้เป็น 64- บิตและยาวยังคงเป็น 32- บิต) บางทีฉันควรเพิ่มคำอธิบายเพิ่มเติมหรือลิงก์อินไลน์ไปยังบทความ Wikipedia ที่เกี่ยวข้อง
โคดี้เกรย์

8

Python 3 , 72 71 ไบต์

1 ไบต์ต้องขอขอบคุณ Lynn

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

ลองออนไลน์!

คำอธิบาย

รูปแบบ binary64 ประกอบด้วยสามองค์ประกอบ:

  • บิตแรกคือบิตเครื่องหมายซึ่งก็คือ1ถ้าจำนวนเป็นลบ
  • 11 บิตถัดไปจะเก็บเลขชี้กำลังด้วยการเพิ่ม 1,023
  • 52 บิตถัดไปจะเก็บซิกนิฟิแคนด์หรือแมนทิสซา

n and(…)-(n>0)ไบต์สั้นลงใช่ไหม
ลินน์

หรือ int-> ลอยหรือลอยใด ๆ เลยสำหรับเรื่องนั้น
user2357112 รองรับ Monica

8

C (gcc) , 47 ไบต์

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

นี่ไม่ใช่พกพา มันได้รับการทดสอบด้วย gcc 7.1.1 บน x86_64 ที่ใช้ Linux โดยไม่มีธงของคอมไพเลอร์

ลองออนไลน์!


1
อินพุตต้องเป็นจำนวนเต็ม หรือมันจะโอเคที่จะให้ผู้โทรจัดการโดยการแปลงlongไปยังdoubleไซต์การโทรโดยนัย?
Peter Cordes

1
นอกจากนี้ยังอาศัยความบังเอิญของพฤติกรรมของคอมไพเลอร์ที่จะเกิดขึ้นที่จะออกnในraxด้วยรหัสยกเลิกการที่ดีที่สุดคือยิ้มสวย มันจะหยุดหากคุณเปิดใช้-O3งานดังนั้นจึงไม่ใช่เพียง gcc โดยทั่วไปเป็น gcc บน x86-64 โดยมี 64 บิตlongเมื่อปิดใช้งานการเพิ่มประสิทธิภาพ หากคุณใส่ความต้องการเหล่านั้นไว้ในคำตอบของคุณ ฉันคิดว่ามีแพลตฟอร์ม gcc รองรับที่มี 64- บิตlongแต่ที่เกิดขึ้นเพื่อออกจากpopcountlผลการลงทะเบียนอื่น ๆ นอกเหนือจากการลงทะเบียนค่าตอบแทน
Peter Cordes

1
ฉันใช้จำนวนเต็มในแง่คณิตศาสตร์ ฉันได้เพิ่มรายละเอียดของสภาพแวดล้อมการทดสอบของฉันเนื่องจากฉันไม่แน่ใจว่า gcc, x86-64 และ 64- บิตยาวพอ ที่กล่าวว่าอย่างน้อยใน x86 ฟังก์ชั่น return-less ทำงานกับ gcc (และ tcc) บ่อยกว่าไม่
Dennis

ใช่ฉันเพิ่งอ่านคำถามอีกครั้งและฉันยอมรับว่าการยอมรับอาร์กิวเมนต์doubleควรจะดี มันไม่ได้พูดอะไรเกี่ยวกับการกำหนดให้ฟังก์ชันยอมรับในรูปแบบ base2 และใช่ gcc รุ่นต่าง ๆ สามารถเปล่งรหัสที่แตกต่างออกไปได้ (ความจริงสนุก: โดยไม่มี-mpopcntgcc จะไม่ใช้popcntinsn และจะปล่อยลำดับของคำสั่งเพื่อเลียนแบบมันสถาปัตยกรรมบางอันไม่มีคำสั่ง popcnt เลยดังนั้น__builtin_popcountlต้องใช้ลำดับของ insns เสมอ)
Peter Cordes

ใช่__builtin_*ฟังก์ชั่นจำนวนมาก (ส่วนใหญ่) มีรุ่นดั้งเดิมเพื่อหลีกเลี่ยงการสร้างคำแนะนำที่ผิดกฎหมาย -march=nativeใช้popcntqเฉพาะเมื่อมี
Dennis


6

C (gcc), 63 ไบต์

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

วิธีการแก้ปัญหานี้ขึ้นอยู่กับคำตอบของ @ LeakyNun แต่เนื่องจากเขาไม่ต้องการปรับปรุงคำตอบของเขาเองฉันจึงโพสต์เวอร์ชันที่ตีกอล์ฟให้มากกว่านี้

ลองออนไลน์


2
ฉันสงสัยอย่างมากว่าไม่มีใครต้องการปรับปรุงคำตอบของพวกเขา
Mr. Xcoder

1
@ Mr.Xcoder ตกลงฉันจะเก็บไว้ที่นี่จนกว่าเขาจะแก้ไขคำตอบของเขาเอง ถ้าเขาไม่ต้องการแก้ไขนี่จะอยู่ที่นี่ ฉันโพสต์การปรับปรุงนี้เป็นความคิดเห็นเกี่ยวกับคำตอบของเขาและเขาปฏิเสธ

1
ฉันคิดว่าอินพุตต้องเป็นประเภทจำนวนเต็มไม่ใช่ของจริง
ceilingcat

3
@ThePirateBay ฉันไม่เห็นความคิดเห็นของคุณเกี่ยวกับคำตอบของฉันและฉันยังไม่เห็นมันในตอนนี้
Leun Nun

9
การตัดสินใจที่จะแนะนำการปรับปรุงหรือโพสต์คำตอบของคุณเองเป็นของคุณ แต่ 6 นาทีแทบจะประมาณหนึ่งชั่วโมง
Dennis

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 Nun
บันทึก 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

@Neil ไม่ทำงาน ฉันคิดว่าคุณหมายถึงการแทนที่s+=l<0?1:0?
TheLethalCoder

ความผิดฉันเอง; lนานไหมดังนั้นมันต้องการs-=l>>63?
Neil

4

Python 2 , 69 ไบต์

-12 ไบต์ขอบคุณ @ ASCII-only

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

ลองออนไลน์!



1
เล่นกอล์ฟวิธีการของคุณ 76 ไบต์ฉันแนะนำวิธีการแบบ ASCII-only โดย
Mr. Xcoder

@ Mr.Xcoder !ไม่จำเป็นเนื่องจากคำสั่งไบต์ไม่สำคัญ
เท่านั้น


@ ASCII-only บรรจุหีบห่อที่ยังไม่ได้บรรจุ ขอบคุณ: 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

@Annauld ฉันสงสัยว่ามีนักเล่นกลบิตนักกอล์ฟขอบคุณที่หามัน!
Neil

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 ไบต์หากการแก้ไข ARG แบบแทนที่ต้องเขียน 64- บิตทั้งหมดแทนที่จะทิ้งขยะใน 32 ด้านบนฉันคิดว่ามันสมเหตุสมผลที่จะโต้แย้งว่าผู้เรียกอาจต้องการโหลด 32b ต่ำเท่านั้นและ x86 ศูนย์ - ขยายจาก 32 ถึง 64 โดยนัยกับการดำเนินการทุก 32- บิต แต่มันจะหยุดไม่ให้ผู้เรียกทำadd rbx, [rdi]หรืออะไรบางอย่าง)

คำแนะนำ x87 จะสั้นกว่า SSE2 cvtsi2sd/ ชัดเจนมากขึ้นmovq(ใช้ในคำตอบของ @ ceilingcat ) และ[reg]โหมดการกำหนดแอดเดรสนั้นมีขนาดเท่ากับ a reg: เพียงแค่ mod / rm byte

เคล็ดลับคือการหาวิธีส่งค่าในหน่วยความจำโดยไม่จำเป็นต้องมีไบต์มากเกินไปสำหรับการกำหนดโหมด (เช่นการส่งผ่านสแต็กนั้นไม่ได้ยอดเยี่ยม) โชคดีที่กฎอนุญาตให้อ่าน / เขียน args หรือแยกเอาท์พุท argsดังนั้นฉันสามารถให้ผู้โทรส่งค่าตัวชี้ไปยังหน่วยความจำที่ฉันอนุญาตให้เขียนได้

เรียกได้จาก 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- บิตได้อย่างสมเหตุสมผลพอสมควร ต่ำ 32b

หากเราต้องการโต้แย้งว่าเราสามารถนำตัวชี้ไปยังจำนวนเต็มอินพุทและทำลายมันได้ในขณะที่ส่งคืนเอาต์พุตกลับมาraxก็เพียงแค่ละเว้นmov [rdi], eaxจากpopcnt_double_outargนั้นนำมาไว้ที่ 10 ไบต์


ทางเลือกโดยไม่มีเทคนิคการเรียกประชุมแบบโง่ 14 ไบต์

ใช้สแต็กเป็นพื้นที่เริ่มต้นpushเพื่อไปที่นั่น การใช้push/ popการคัดลอกลงทะเบียนใน 2 ไบต์แทน mov rdi, rsp3 ( [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 อีกต่อไป (ยกเว้นว่าคุณใช้การเรียกแบบกำหนดเองที่doubleมีการส่งผ่าน s ในการลงทะเบียน x87 จากนั้นเก็บไว้ในพื้นที่สีแดงด้านล่างสแต็กและ popcnt จากที่นั่น)

11 ไบต์:

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

แต่เราสามารถใช้เคล็ดลับ Pass-by-Reference เช่นเดียวกับก่อนหน้านี้เพื่อสร้างเวอร์ชัน 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 (long n) {return Long bitCount (สองครั้งdoubleToLongBits (n));}

เงินบริจาคโดย Kevin Cruijssen (Java SE 5+):

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

บริจาคโดย Kevin Cruijssen (Java SE 8+, ฟังก์ชัน lambda):

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

ทำได้ดีมาก! :-)
Leun Nun

1
คำตอบที่ดี +1 จากฉัน คุณสามารถกอล์ฟสามไบต์โดยการใช้พารามิเตอร์เป็นLong nและการใช้งานแทนn.bitCount(...) Long.bitCount(...)นอกจากนี้หากคุณใช้ Java 8+ คุณสามารถตีกอล์ฟเป็นn->n.bitCount(Double.doubleToLongBits(n))( 41 ไบต์ )
Kevin Cruijssen

2

เพียงลองใช้วิธีอื่นที่ปลอดภัยกว่าTheLethalCoderฉันมากับสิ่งนี้ (มันน่าเสียดาย C # มีชื่อวิธียาว ๆ ):

C # (. NET Core) , 76 + 13 ไบต์

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

ลองออนไลน์!

นับไบต์รวมถึง 13 using System;ไบต์ ก่อนอื่นฉันต้องแปลงdoublea ไปเป็น a 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คำสั่งที่สอง
Charlie

1
เมื่อคุณเพิ่มวินาทีนั่นก็คือเมื่อnamespaceมีประโยชน์ แต่ใช่ในกรณีนี้การหลีกเลี่ยง Linq นั้นถูกกว่าเล็กน้อย เพียงต้องการแสดงความคิดเห็นด้วยวิธีการในกรณีที่คุณมีความคิดใด ๆ เกี่ยวกับวิธีการย่อให้สั้นเพื่อประหยัด bytes
TheLethalCoder

@TheLethalCoder Sum(c=>c&1)สั้นกว่า หรือSum()-768
Peter Taylor


1

dc, 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.