754号至汉明


29

您将得到一个介于(−2 52)至(2 52)之间的整数作为输入。如所公知的,在此范围内的整数可以准确地为双精度浮点值来表示。k-45035996273704964503599627370496

你应该输出汉明权重的编码的(那些数)kbinary64格式。这将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机器代码的asm答案(例如codegolf.stackexchange.com/a/136360/30206和mine)都假设我们必须接受64位整数输入。接受一个binary64值将节省5个字节。
彼得·科德斯

如果是这样,那么关于有限范围的所有内容只是为了以防万一,有人想自己将转换转换为binary64位模式而不是进行类型转换?还是没有类型双关的语言?嗯,一个有趣的挑战可能是将a的指数和尾数添加binary64为base2整数。如果仍然需要分别处理它们,则可能值得做一些操作,而不是键入双关和遍历所有位。
彼得·科德斯

2
@PeterCordes是的,您可以输入a浮点数的形式。有限的范围是为了确保浮点表示的准确性
Luis Mendo

好,谢谢。我想您想留下编写一个带有a的函数的选择long,所以您不能只说任何binary64 double,因为不是所有的double都是整数。但是,所有整数值doubles都可以转换为long正反,最多可以转换为long。(如您所指出的,相反情况并非如此。double假设使用默认的舍入模式,您将获得最接近的可表示形式)。无论如何,这是提出问题的一种完全有效的方法。我只是没有仔细阅读而已>。<
Peter Cordes

“请注意字节序不会影响结果,因此您可以安全地使用机器的双精度值的实际内部表示来计算输出。” 除非您的机器不使用IEEE浮点格式...
Jerry Jeremiah

Answers:


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,然后计算的汉明权重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
w,AT&T语法?您可以使用objdump -drwC -MintelIntel语法反汇编。如果你在一个寄存器,你可以用它来存储/重装了一个指针,你可以保存字节movaps [rsi], xmm0/ popcnt rax, [rsi]。(movaps只有3个字节,比movq短2个字节。)但这在这里无济于事,因为[rsp-24]需要额外的2个字节(使用RSP作为基础的SIB,加上disp8)。这些额外的字节在存储和重新加载中都需要。噢,我以为我看到了一笔存款,但是没有:/
彼得·科德斯

使用自定义调用约定保存了4个字节。或仍然使用x87指令以相同的调用约定保存2个字节。
彼得·科德斯

1
@DJMcMayhem:也许不是唯一的时间。极限斐波那契挑战赛上仍然没有高尔夫球语言答案(打印Fib的前1000位数字(十亿),我的x86机器码答案(105字节快速,或101字节,在5分钟而不是1分钟内运行)是不是比一些其他的答案大得多,而且他们都在扩展精度整数语言内置的。
彼得·科德斯

2
或更简单的挑战(且没有性能要求)是将色度键混合到整数数组中。我的机器码答案是pyth答案长度的一半。
彼得·科德斯

11

C(gcc)82 68字节

感谢Neil提供9个字节。

邪恶的浮点位级别黑客

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
当然,这需要使用64位的C实现long。它可以在x86-64 Linux上运行,但在Windows上将失败。我建议说“带有64位的longgcc”,因为gcc在许多平台上运行,其中许多平台具有不同的ABI。
彼得·科德斯

1
@Peter的评论就是为什么我在编辑中添加“ LP64”。我还以我认为更合逻辑的顺序重新排列了其他文本。我猜您不喜欢这种更改并将其回滚,但是LP64是一个标准术语,用于描述ABI,其中long和指针是64位值(与ILP64相比,int和int也是64位,即LLP64,如在Windows上使用的那样,其中只有long long和指针是64位而long仍然是3​​2位)。也许我应该添加更多的解释,或者是指向相关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位存储添加了1023的指数
  • 接下来的52位存储有效数或尾数。

n and(…)-(n>0)短一个字节,不是吗?
林恩

或者是int-> float,或者根本没有任何浮点。
user2357112支持Monica17年

8

C(gcc),47个字节

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

这不是便携式的。已在运行Linux的x86_64上使用gcc 7.1.1进行了测试,没有编译器标志。

在线尝试!


1
输入必须是整数。还是可以让呼叫者在呼叫站点通过将隐式转换为long来处理double呢?
彼得·科德斯

1
此外,依靠编译器行为侥幸发生留下nrax与未优化的代码很俗气。如果启用-O3,它会中断,因此它不仅是一般的gcc,而且是在long禁用了优化功能的64位x86-64上的gcc 。如果您将所有这些要求都放入答案中,我会投票赞成。我假设有gcc支持的平台具有64位,long但是碰巧会将popcountl结果留在除返回值寄存器之外的其他寄存器中。
彼得·科德斯

1
我从数学意义上讲整数。我已经添加了测试环境的规范,因为我不确定gcc,x86-64和64位long是否足够。就是说,至少在x86上,无返回功能与gcc(和tcc)的配合更多。
丹尼斯

是的,我只是重新阅读了问题,我同意接受arg为a double应该很好。它并没有说明要求该函数以base2格式接受它。是的,不同版本的gcc可能会发出不同的代码,因此这也很重要。(有趣的事实:没有-mpopcnt,gcc不会使用popcntinsn,并且会发出一系列指令来对其进行仿真。某些体系结构根本没有popcnt指令,因此__builtin_popcountl始终必须使用某些insns序列)
Peter Cordes

是的,许多(大多数?)__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先生17年

1
@ Mr.Xcoder。好的,我将其保留在这里,直到他编辑自己的答案。如果他不想编辑,它将保留在此处。我将此改进作为对他的回答的评论,但他拒绝了。

1
我认为输入必须是整数类型而不是实数。
ceilingcat '17

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;}}

@Leaky Nun节省11个字节。
@Neil节省了2个字节。

在线尝试!使用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

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

编辑:由于@Arnauld,节省了1个字节。@DocMax节省了3个字节。


可以g(i^i&-i,x++)为-1个字节做吗?
Arnauld

@Arnauld我确实想知道是否有高尔夫球手在扭动,感谢您找到它!
尼尔

1
如果您替换new Float64Array([n])Float64Array.of(n)
--3,

4

x86-64机器代码,int64_t输入12个字节

6字节double输入

需要popcntISA扩展名CPUID.01H:ECX.POPCNT [Bit 23] = 1)。

(或者13个字节,如果就地修改arg需要写入所有64位,而不是在前32位中留下垃圾。我认为有理由认为调用方可能只想加载低位的32b,而x86为零是合理的-每个32位操作都会将其从32隐式扩展到64。尽管如此,它确实会阻止调用者执行操作add rbx, [rdi]或执行其他操作。)

x87指令比更明显的SSE2 cvtsi2sd/ movq(用于@ceilingcat的答案中)短,[reg]寻址方式的大小与a相同reg:只是一个mod / rm字节。

诀窍是想出一种将值传递到内存中的方法,而无需为寻址模式使用太多字节。(例如,在栈上传递不是那么好。)幸运的是,规则允许读/写args或单独的输出args,因此我可以让调用者向我传递指向允许写的内存的指针。

可从C调用并带有签名: void popc_double(int64_t *in_out); 仅结果的低32b有效,这对于C可能很奇怪,但对于asm很自然。(解决此问题需要在最终存储区(mov [rdi], rax)上添加REX前缀,因此需要再增加一个字节。)在Windows上,请更改rdirdx,因为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返回值退出。(打开“调试”标签以查看它。)

传递单独的输入/输出指针也可以使用(x86-64 SystemV ABI中的rdi和rsi),但是我们不能合理地销毁64位输入,也不能轻易地证明需要64位输出缓冲区,而只编写低32b。

如果确实要争辩说我们可以使用指向输入整数的指针并销毁它,同时返回in的输出rax,则只需省略mov [rdi], eaxfrom popcnt_double_outarg,将其减少到10个字节。


替代方法,没有愚蠢的电话会议技巧,14字节

将堆栈用作暂存空间,并将其push放到那里。使用push/ pop复制2个字节的寄存器,而不是3个字节mov rdi, rsp。([rsp]始终需要一个SIB字节,因此值得花两个字节来复制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个字节


4

Perl 5中33 32 + 1(-p)= 34 33个字节

由于霍布斯节省了1个字节

$_=(unpack"B*",pack d,$_)=~y/1//

在线尝试!


您可以通过节省1个字节d一个裸字(pack d,$_代替pack"d",$_
霍布斯

3

MATLAB,36字节

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

使用de2bi不仅比短的事实dec2bin,而且还提供以1和0而不是ASCII 48、49的结果。


3

Java(64、61、41字节)

使用标准库(Java SE 5+)完全简单:

int f(long n){返回Long。比特计数(双人间。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))

做得很好!:-)
Leaky 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;。首先,我需要转换doublelong具有相同的二进制表示,那么我可以将它转换为二进制string,然后我算1通过拆分只是s字符串和计数的子串减去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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.