16
\$\begingroup\$

Problem:

Find the number of leading zeroes in a 64-bit signed integer

Rules:

  • The input cannot be treated as string; it can be anything where math and bitwise operations drive the algorithm
  • The output should be validated against the 64-bit signed integer representation of the number, regardless of language
  • Default code golf rules apply
  • Shortest code in bytes wins

Test cases:

These tests assume two's complement signed integers. If your language/solution lacks or uses a different representation of signed integers, please call that out and provide additional test cases that may be relevant. I've included some test cases that address double precision, but please feel free to suggest any others that should be listed.

input                output   64-bit binary representation of input (2's complement)
-1                   0        1111111111111111111111111111111111111111111111111111111111111111
-9223372036854775808 0        1000000000000000000000000000000000000000000000000000000000000000
9223372036854775807  1        0111111111111111111111111111111111111111111111111111111111111111
4611686018427387903  2        0011111111111111111111111111111111111111111111111111111111111111
1224979098644774911  3        0001000011111111111111111111111111111111111111111111111111111111
9007199254740992     10       0000000000100000000000000000000000000000000000000000000000000000
4503599627370496     11       0000000000010000000000000000000000000000000000000000000000000000
4503599627370495     12       0000000000001111111111111111111111111111111111111111111111111111
2147483648           32       0000000000000000000000000000000010000000000000000000000000000000
2147483647           33       0000000000000000000000000000000001111111111111111111111111111111
2                    62       0000000000000000000000000000000000000000000000000000000000000010
1                    63       0000000000000000000000000000000000000000000000000000000000000001
0                    64       0000000000000000000000000000000000000000000000000000000000000000
\$\endgroup\$
  • 12
    \$\begingroup\$ Welcome to PPCG! What's the reason behind "the input cannot be treated as string"? This disqualifies all languages that can't handle 64-bit integers and is unlikely to lead to more answers that take an integer, because this is the straightforward way when available anyway. \$\endgroup\$ – Arnauld Dec 9 '18 at 23:26
  • 1
    \$\begingroup\$ Can we return False instead of 0? \$\endgroup\$ – Jo King Dec 9 '18 at 23:33
  • 4
    \$\begingroup\$ @Arnauld There are already similar questions here and on other sites that specifically call for string-based solutions, but nothing that opens the question to math and logical operations. My hope is to see a bunch of different approaches to this problem that are not already answered elsewhere. Should this be opened to string solutions as well to be all-inclusive? \$\endgroup\$ – Dave Dec 9 '18 at 23:40
  • 4
    \$\begingroup\$ Several CPUs including x86 and ARM have specific instructions for this (x86 actually have several). I've always wondered why programming languages don't expose this feature since in most programming languages today you can't invoke assembly instructions. \$\endgroup\$ – slebetman Dec 10 '18 at 5:43
  • 1
    \$\begingroup\$ @user202729 I think I worded this poorly: 'The output should be validated against the 64-bit signed integer representation of the number, regardless of language' What I mean by that is that this question defines the number of zeros as the number of zeros in a 64-bit signed integer. I guess I made that constraint to eliminate signed vs unsigned integers. \$\endgroup\$ – Dave Dec 11 '18 at 0:26

29 Answers 29

36
\$\begingroup\$

x86_64 machine language on Linux, 6 bytes

0:       f3 48 0f bd c7          lzcnt  %rdi,%rax
5:       c3                      ret

Requires Haswell or K10 or higher processor with lzcnt instruction. Try it online!

\$\endgroup\$
  • 18
    \$\begingroup\$ Builtins strike again /s \$\endgroup\$ – Logern Dec 10 '18 at 2:06
  • 1
    \$\begingroup\$ I recommend specifying the calling convention used (though you did say on Linux) \$\endgroup\$ – qwr Dec 12 '18 at 8:02
  • \$\begingroup\$ @qwr It looks like SysV calling convention because the parameter is passed in %rdi and it is returned in %rax. \$\endgroup\$ – Logern Dec 15 '18 at 4:21
21
\$\begingroup\$

Hexagony, 78 70 bytes

2"1"\.}/{}A=<\?>(<$\*}[_(A\".{}."&.'\&=/.."!=\2'%<..(@.>._.\=\{}:"<><$

Try it online!

Isn't this challenge too trivial for a practical language? ;)

side length 6. I can't fit it in a side length 5 hexagon.

Explanation

\$\endgroup\$
  • 3
    \$\begingroup\$ I laughed really hard at the "explanation". :D \$\endgroup\$ – Eric Duminil Dec 10 '18 at 18:45
  • 1
    \$\begingroup\$ I think you may have overcomplicated handling negative numbers/zero. I managed to fit a similar program into side length 5 by not doing that hefty 2^64 calculation. It clearly isn't well golfed yet, though! \$\endgroup\$ – FryAmTheEggman Dec 10 '18 at 22:56
  • \$\begingroup\$ @fry Ah right, negative numbers always have 0 leading zeroes... which definitely leads to shorter program because generates 2^64 is long. \$\endgroup\$ – user202729 Dec 11 '18 at 5:39
12
\$\begingroup\$

Python, 31 bytes

lambda n:67-len(bin(-n))&~n>>64

Try it online!

The expresson is the bitwise & of two parts:

67-len(bin(-n)) & ~n>>64

The 67-len(bin(-n)) gives the correct answer for non-negative inputs. It takes the bit length, and subtracts from 67, which is 3 more than 64 to compensate for the -0b prefix. The negation is a trick to adjust for n==0 using that negating it doesn't produce a - sign in front.

The & ~n>>64 makes the answer instead be 0 for negative n. When n<0, ~n>>64 equals 0 (on 64-bit integers), so and-ing with it gives 0. When n>=0, the ~n>>64 evaluates to -1, and doing &-1 has no effect.


Python 2, 36 bytes

f=lambda n:n>0and~-f(n/2)or(n==0)*64

Try it online!

Arithmetical alternative.

\$\endgroup\$
9
\$\begingroup\$

C (gcc), 14 bytes

__builtin_clzl

Works fine on tio

C (gcc), 35 29 bytes

f(long n){n=n<0?0:f(n-~n)+1;}

Try it online!

Than Dennis for 6 bytes

C (gcc) compiler flags, 29 bytes by David Foerster

-Df(n)=n?__builtin_clzl(n):64

Try it online!

\$\endgroup\$
  • 3
    \$\begingroup\$ Worth noting that it's only for 64-bit machines (or any others with LP64/ILP64/etc. ABI) \$\endgroup\$ – Ruslan Dec 10 '18 at 6:43
  • 1
    \$\begingroup\$ Geez, that’s even shorter than any use of the GCC built-in __builtin_clzl with which I can come up. \$\endgroup\$ – David Foerster Dec 10 '18 at 11:49
  • \$\begingroup\$ @Ruslan: good point, on systems where long is 32 bits (including Windows x64), you need __builtin_clzll (unsigned long long). godbolt.org/z/MACCKf. (Unlike Intel intrinsics, GNU C builtins are supported regardless of the operation being doable with one machine instruction. On 32-bit x86, clzll compiles to a branch or cmov to do lzcnt(low half)+32 or lzcnt(high half). Or bsr if lzcnt isn't available. \$\endgroup\$ – Peter Cordes Dec 12 '18 at 1:00
  • \$\begingroup\$ The test cases include "0" but __builtin_clz(l)(l) is undefined behavior for zero: "If x is 0, the result is undefined." \$\endgroup\$ – MCCCS Dec 12 '18 at 13:36
  • 1
    \$\begingroup\$ @MCCCS If it works, it counts. That's also why I keep the last answer \$\endgroup\$ – l4m2 Dec 12 '18 at 13:43
8
\$\begingroup\$

Java 8, 32 26 bytes.

Long::numberOfLeadingZeros

Builtins FTW.

-6 bytes thanks to Kevin Cruijssen

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Ah, completely forgot about numberOfLeadingZeros.. You can golf it to 28 bytes btw: n->n.numberOfLeadingZeros(n) \$\endgroup\$ – Kevin Cruijssen Dec 10 '18 at 10:43
  • 1
    \$\begingroup\$ Actually, Long::numberOfLeadingZeros is even shorter (26 bytes). \$\endgroup\$ – Kevin Cruijssen Dec 10 '18 at 10:46
  • 5
    \$\begingroup\$ Wow, it doesn't happen very often that Java beats Python. Congrats! \$\endgroup\$ – Eric Duminil Dec 10 '18 at 18:46
6
\$\begingroup\$

Perl 6, 35 28 26 bytes

-2 bytes thanks to nwellnhof

{to .fmt("%064b")~~/^0*/:}

Try it online!

Anonymous code block that takes a number and returns a number. This converts the number to a binary string and counts the leading zeroes. It works for negative numbers because the first character is a - e.g. -00000101, so there are no leading zeroes.

Explanation:

{                        }  # Anonymous code block
    .fmt("%064b")           # Format as a binary string with 64 digits
                 ~~         # Smartmatch against
                   /^0*/    # A regex counting leading zeroes
 to                     :   # Return the index of the end of the match
\$\endgroup\$
5
\$\begingroup\$

Python 3, 34 bytes

f=lambda n:-1<n<2**63and-~f(2*n|1)

Try it online!

\$\endgroup\$
5
\$\begingroup\$

J, 18 bytes

0{[:I.1,~(64$2)#:]

Try it online!

J, 19 bytes

1#.[:*/\0=(64$2)#:]

Try it online!

Explanation:

                #:  - convert 
                  ] - the input to
          (64$2)    - 64 binary digits
         =          - check if each digit equals 
        0           - zero
   [:*/\            - find the running product
1#.                 - sum
\$\endgroup\$
  • 1
    \$\begingroup\$ 1#.[:*/\1-_64{.#: (17) is close but doesn't work for negative numbers :( \$\endgroup\$ – Conor O'Brien Dec 10 '18 at 22:12
  • \$\begingroup\$ @Conor O'Brien Nice approach too! \$\endgroup\$ – Galen Ivanov Dec 11 '18 at 4:41
5
\$\begingroup\$

Perl 6, 18 bytes

-2 bytes thanks to Jo King

64-(*%2**64*2).msb

Try it online!

\$\endgroup\$
5
\$\begingroup\$

JavaScript (Node.js), 25 bytes

Takes input as a BigInt literal.

f=x=>x<0?0:x?f(x/2n)-1:64

Try it online!

Or 24 bytes by returning false instead of \$0\$.

\$\endgroup\$
  • \$\begingroup\$ Wouldn't n=>n<1?0:n.toString(2)-64 perform the same? \$\endgroup\$ – Ismael Miguel Dec 10 '18 at 12:59
  • \$\begingroup\$ @IsmaelMiguel I suppose you meant n=>n<1?0:n.toString(2).length-64, but that would not work anyway. This would, I think. \$\endgroup\$ – Arnauld Dec 10 '18 at 13:28
  • 1
    \$\begingroup\$ @IsmaelMiguel No worries. :) It's indeed possible to have the .toString() approach working, but we still need a BigInt literal as input. Otherwise, we only have 52 bits of mantissa, leading to invalid results when precision is lost. \$\endgroup\$ – Arnauld Dec 10 '18 at 13:48
  • 1
    \$\begingroup\$ The fact that the BigInt suffix is the same character as your parameter is very confusing... \$\endgroup\$ – Neil Dec 11 '18 at 9:25
  • 1
    \$\begingroup\$ @Neil Unreadable code on PPCG?? This can't be! Fixed! :p \$\endgroup\$ – Arnauld Dec 11 '18 at 12:04
3
\$\begingroup\$

Powershell, 51 bytes

param([long]$n)for(;$n-shl$i++-gt0){}($i,65)[!$n]-1

Test script:

$f = {

param([long]$n)for(;$n-shl$i++-gt0){}($i,65)[!$n]-1

}

@(
    ,(-1                   ,0 )
    ,(-9223372036854775808 ,0 )
    ,(9223372036854775807  ,1 )
    ,(4611686018427387903  ,2 )
    ,(1224979098644774911  ,3 )
    ,(9007199254740992     ,10)
    ,(4503599627370496     ,11)
    ,(4503599627370495     ,12)
    ,(2147483648           ,32)
    ,(2147483647           ,33)
    ,(2                    ,62)
    ,(1                    ,63)
    ,(0                    ,64)
) | % {
    $n,$expected = $_
    $result = &$f $n
    "$($result-eq$expected): $result"
}

Output:

True: 0
True: 0
True: 1
True: 2
True: 3
True: 10
True: 11
True: 12
True: 32
True: 33
True: 62
True: 63
True: 64
\$\endgroup\$
3
\$\begingroup\$

05AB1E, 10 9 bytes

·bg65αsd*

I/O are both integers

Try it online or verify all test cases.

Explanation:

·         # Double the (implicit) input
          #  i.e. -1 → -2
          #  i.e. 4503599627370496 → 9007199254740992
 b        # Convert it to binary
          #  i.e. -2 → "ÿ0"
          #  i.e. 9007199254740992 → 100000000000000000000000000000000000000000000000000000
  g       # Take its length
          #  i.e. "ÿ0" → 2
          #  i.e. 100000000000000000000000000000000000000000000000000000 → 54
   65α    # Take the absolute different with 65
          #  i.e. 65 and 2 → 63
          #  i.e. 65 and 54 → 11
      s   # Swap to take the (implicit) input again
       d  # Check if it's non-negative (>= 0): 0 if negative; 1 if 0 or positive
          #  i.e. -1 → 0
          #  i.e. 4503599627370496 → 1
        * # Multiply them (and output implicitly)
          #  i.e. 63 and 0 → 0
          #  i.e. 11 and 1 → 11
\$\endgroup\$
3
\$\begingroup\$

APL+WIN, 34 bytes

+/×\0=(0>n),(63⍴2)⊤((2*63)××n)+n←⎕

Explanation:

n←⎕ Prompts for input of number as integer

((2*63)××n) If n is negative add 2 to power 63

(63⍴2)⊤ Convert to 63 bit binary

(0>n), Concatinate 1 to front of binary vector if n negative, 0 if positive

+/×\0= Identify zeros, isolate first contiguous group and sum if first element is zero
\$\endgroup\$
3
\$\begingroup\$

Haskell, 56 bytes

Thanks xnor for spotting a mistake!

f n|n<0=0|1>0=sum.fst.span(>0)$mapM(pure[1,0])[1..64]!!n

Might allocate quite a lot of memory, try it online!

Maybe you want to test it with a smaller constant: Try 8-bit!

Explanation

Instead of using mapM(pure[0,1])[1..64] to convert the input to binary, we'll use mapM(pure[1,0])[1..64] which essentially generates the inverted strings \$\\{0,1\\}^{64}\$ in lexicographic order. So we can just sum the \$1\$s-prefix by using sum.fst.span(>0).

\$\endgroup\$
3
\$\begingroup\$

Ruby, 22 bytes

->n{/[^0]/=~"%064b"%n}

Try it online!

\$\endgroup\$
3
\$\begingroup\$

C# (Visual C# Interactive Compiler), 42 bytes

x=>x!=0?64-Convert.ToString(x,2).Length:64

Try it online!

C# (Visual C# Interactive Compiler), 31 bytes

int c(long x)=>x<0?0:c(x-~x)+1;

Even shorter, based off of @l4m2's C (gcc) answer. Never knew that you could declare functions like that, thanks @Dana!

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Jelly,  10  9 bytes

-1 thanks to a neat trick by Erik the Outgolfer (is-non-negative is now simply )

ḤBL65_×AƑ

A monadic Link accepting an integer (within range) which yields an integer.

Try it online! Or see the test-suite.


The 10 was ḤBL65_ɓ>-×

Here is another 10 byte solution, which I like since it says it is "BOSS"...

BoṠS»-~%65

Test-suite here

...BoṠS63r0¤i, BoṠS63ŻṚ¤i, or BoṠS64ḶṚ¤i would also work.


Another 10 byter (from Dennis) is æ»64ḶṚ¤Äċ0 (again æ»63r0¤Äċ0 and æ»63ŻṚ¤Äċ0 will also work)

\$\endgroup\$
  • \$\begingroup\$ 9 bytes \$\endgroup\$ – Erik the Outgolfer Dec 11 '18 at 18:18
  • \$\begingroup\$ @EriktheOutgolfer I thought to myself "there must be a golfier way to multiply by isNonNegative" and didn't think of the Ƒ quick at all, very nice work! \$\endgroup\$ – Jonathan Allan Dec 11 '18 at 19:11
  • 1
    \$\begingroup\$ Actually, I've been theorizing about for quite some while now. Be warned that it doesn't vectorize! ;-) It's actually "flatten and then check if all elements are nonnegative". \$\endgroup\$ – Erik the Outgolfer Dec 11 '18 at 19:14
2
\$\begingroup\$

Java 8, 38 bytes

int f(long n){return n<0?0:f(n-~n)+1;}

Input as long (64-bit integer), output as int (32-bit integer).

Port of @l4m2's C (gcc) answer.

Try it online.

Explanation:

 int f(long n){       // Recursive method with long parameter and integer return-type
   return n<0?        //  If the input is negative:
           0          //   Return 0
          :           //  Else:
           f(n-~n)    //   Do a recursive call with n+n+1
                  +1  //   And add 1

EDIT: Can be 26 bytes by using the builtin Long::numberOfLeadingZeros as displayed in @lukeg's Java 8 answer.

\$\endgroup\$
2
\$\begingroup\$

Perl 5, 37 bytes

sub{sprintf("%064b",@_)=~/^0*/;$+[0]}

Try it online!

Or this 46 bytes if the "stringification" is not allowed: sub z

sub{my$i=0;$_[0]>>64-$_?last:$i++for 1..64;$i}
\$\endgroup\$
  • \$\begingroup\$ s/length$&/$+[0]/ (-3 bytes) ;) \$\endgroup\$ – Dada Dec 10 '18 at 12:59
  • \$\begingroup\$ IMO, you're not allowed to remove the sub keyword from answers containing Perl 5 functions. \$\endgroup\$ – nwellnhof Dec 10 '18 at 14:36
  • \$\begingroup\$ I've seen whats similar to removing sub in answers for other languages, perl6, powershell and more. \$\endgroup\$ – Kjetil S. Dec 10 '18 at 14:58
  • \$\begingroup\$ In Perl6, I think you don't need sub{} to make a (anonymous?) sub, which explain why it's omitted from Perl6 answers. I agree with @nwellnhof that you shouldn't be allowed to remove sub. (when I was still active, like a year ago or so, that was the rule) \$\endgroup\$ – Dada Dec 10 '18 at 15:03
  • \$\begingroup\$ changed now. And included $+[0]. \$\endgroup\$ – Kjetil S. Dec 10 '18 at 15:10
2
\$\begingroup\$

Swift (on a 64-bit platform), 41 bytes

Declares a closure called f which accepts and returns an Int. This solution only works correctly 64-bit platforms, where Int is typealiased to Int64. (On a 32-bit platform, Int64 can be used explicitly for the closure’s parameter type, adding 2 bytes.)

let f:(Int)->Int={$0.leadingZeroBitCount}

In Swift, even the fundamental integer type is an ordinary object declared in the standard library. This means Int can have methods and properties, such as leadingZeroBitCount (which is required on all types conforming to the standard library’s FixedWidthInteger protocol).

\$\endgroup\$
1
\$\begingroup\$

Clean, 103 bytes

Uses the same "builtin" as ceilingcat's answer.

f::!Int->Int
f _=code {
instruction 243
instruction 72
instruction 15
instruction 189
instruction 192
}

Try it online!

Clean, 58 bytes

import StdEnv
$0=64
$i=until(\e=(2^63>>e)bitand i<>0)inc 0

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Stax, 10 bytes

în»╧3(∞┼⌠g

Run and debug it

It's a port of Kevin's 05AB1E solution.

\$\endgroup\$
1
\$\begingroup\$

Perl 5 -p, 42 bytes

1while$_>0&&2**++$a-1<$_;$_=0|$_>=0&&64-$a

Try it online!

Longer than a bitstring based solution, but a decent math based solution.

\$\endgroup\$
  • \$\begingroup\$ Doesn't really work if I'm not mistaken \$\endgroup\$ – Dada Dec 11 '18 at 12:23
  • \$\begingroup\$ @Dada I see that there are a few cases where the floating point division doesn't quite work out properly. I put in an int call that should solve the issue. \$\endgroup\$ – Xcali Dec 11 '18 at 15:55
  • \$\begingroup\$ Sorry, I failed my copy-past it would seem. This is what I wanted to send ;) \$\endgroup\$ – Dada Dec 11 '18 at 17:36
1
\$\begingroup\$

APL(NARS), 15 chars, 30 bytes

{¯1+1⍳⍨⍵⊤⍨64⍴2}

test for few numbers for see how to use:

  f←{¯1+1⍳⍨⍵⊤⍨64⍴2}
  f ¯9223372036854775808
0
  f 9223372036854775807
1
\$\endgroup\$
1
\$\begingroup\$

K (ngn/k), 6 bytes

64-#2\

Try it online!

2\ encode the argument in binary

# length

64- subtract from 64

\$\endgroup\$
  • \$\begingroup\$ # = length ... looks string based \$\endgroup\$ – Titus Dec 13 '18 at 14:37
  • 2
    \$\begingroup\$ @Titus 2\ gives a list of integers and # finds its length. no strings are involved here. \$\endgroup\$ – ngn Dec 13 '18 at 15:38
1
\$\begingroup\$

PHP, 50 46 bytes

for(;0<$n=&$argn;$n>>=1)$i++;echo$n<0?0:64-$i;

Run as pipe with -R or try it online,

<?=$argn<0?0:0|64-log($argn+1,2); has rounding issues; so I took the long way.

\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 41 bytes

The formula for positive numbers is just 63-Floor@Log2@#&. Replacement rules are used for the special cases of zero and negative input.

The input need not be a 64-bit signed integer. This will effectively take the floor of the input to turn it into an integer. If you input a number outside of the normal bounds for a 64-bit integer, it will tell return a negative number indicating how many more bits would be needed to store this integer.

63-Floor@Log2[#/.{_?(#<0&):>2^63,0:>.5}]&

Try it online!

@LegionMammal978's solution is quite a bit shorter at 28 bytes. The input must be an integer. Per the documentation: "BitLength[n] is effectively an efficient version of Floor[Log[2,n]]+1. " It automatically handles the case of zero correctly reporting 0 rather than -∞.

Wolfram Language (Mathematica), 28 bytes

Boole[#>=0](64-BitLength@#)&

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ Boole[#>=0](64-BitLength@#)& is a good bit shorter at 28 bytes. It uses the same basic concept as yours, but applies BitLength and Boole. \$\endgroup\$ – LegionMammal978 Dec 12 '18 at 2:13
  • \$\begingroup\$ I totally forgot about BitLength! \$\endgroup\$ – Kelly Lowder Dec 14 '18 at 2:29
0
\$\begingroup\$

Charcoal, 15 bytes

I⁻⁶⁴L↨﹪NX²¦⁶⁴¦²

Try it online! Link is to verbose version of code. Explanation:

    L           Length of
       N        Input as a number
      ﹪         Modulo
         ²      Literal 2
        X       To the power
           ⁶⁴   Literal 64
     ↨          Converted to base
              ² Literal 2
 ⁻              Subtracted from
  ⁶⁴            Literal 64
I               Cast to string
                Implicitly print

The ¦s serve to separate adjacent integer literals. Conveniently, Charcoal's arbitrary numeric base conversion converts 0 into an empty list, however for negative numbers it simply inverts the sign of each digit, so the number is converted to the equivalent unsigned 64-bit integer first.

\$\endgroup\$
0
\$\begingroup\$

Rust, 18 bytes

i64::leading_zeros

Try it online!

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.