### Challenge

Given an IPv4 address in dotted-quad notation, and an IPv4 subnet in CIDR notation, determine if the address is in the subnet. Output a distinct and consistent value if it is in the subnet, and a separate distinct and consistent value if it is not in the subnet. The output values do not necessarily need to be truthy/falsey in your language.

### CIDR subnet notation brief primer

IPv4 network addresses are 32 bits in length, split into four groups of 8 bits for ease of reading. CIDR subnet notation is a mask of the specified number of bits, starting leftmost. For example, for a /24 subnet, this means the right-most 8 bits of the address are available in that subnet. Thus two addresses that are separated by at most 255, and have the same subnet mask, are in the same subnet. Note that valid CIDR have all the host-bits (the right hand side) unset (zeros).

xxxxxxxx xxxxxxxx xxxxxxxx 00000000


For another example, a /32 subnet specifies that all of the bits are the subnet mask, essentially meaning that only one host is allowed per /32.

xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx


### Examples:

Using True for "in the subnet" and False for "not in the subnet" as output:

127.0.0.1
127.0.0.0/24
True

127.0.0.55
127.0.0.0/23
True

127.0.1.55
127.0.0.0/23
True

10.4.1.33
10.4.0.0/16
True

255.255.255.255
0.0.0.0/0
True

127.1.2.3
127.0.0.0/24
False

127.1.2.3
127.1.2.1/32
False

10.10.83.255
10.10.84.0/22
False


### Rules and Clarifications

• Since input parsing isn't the interesting point of this challenge, you're guaranteed to get valid IPv4 addresses and subnet masks.
• Input and output can be given by any convenient method.
• You can print the result to STDOUT or return it as a function result. Please state in your submission what values the output can take.
• Either a full program or a function are acceptable.
• Standard loopholes are forbidden.
• This is so all usual golfing rules apply, and the shortest code (in bytes) wins.
• Do we have to take input in the same format as your test cases? Commented May 1, 2019 at 15:47
• @EmbodimentofIgnorance You don't have to take them as one-per-line as in the examples, but you do need to take them as a dotted-quad and dotted-subnet as in the examples. (e.g., see the JavaScript answer by Arnauld) Commented May 1, 2019 at 16:01
• Is it ok to have them separated by a slash, e.g. 10.0.0.1/10.0.0.0”/16? Commented May 1, 2019 at 17:26
• @Poke I agree you are correct in that CIDR notation describes an IP address and a subnet size. As in, 1.255.1.1/8 is a valid CIDR expression, representing the host 1.255.1.1 within the network 1.0.0.0 with a subnet mask of 255.0.0.0. However the challenge asks for the network number and subnet specifically in CIDR notation, which 1.255.1.1/8 is not a valid network number and subnet combination. Commented May 2, 2019 at 18:52
• Now we also need an IPv6 version of this challenge Commented May 3, 2019 at 9:43

# Python 3 (62 bytes)

Very straightforward:

from ipaddress import*

• Nice, but does python have a built-in for recognizing goats as well? Commented May 1, 2019 at 14:43
• Of course Mathematica has a build-in for everything - even for exoplanets! Nothing can beat that... But as you could have seen, Python matches Mathematica's goat-formace Commented May 1, 2019 at 16:27
• I wonder if does a ip_adress object and a ip_network object constitute any convenient method, possibly letting Python win, unless a python-based golfing language has these as its types? Commented May 2, 2019 at 2:13
• You won't get it in the range of 20 bytes in normal Python. Only the import and lambda are already longer than the Stax answer. It's no surprise that golfing languages win from "normal" languages... :-( Commented May 2, 2019 at 6:03
• @someone: I beat Python with 53 bytes of x86-64 machine code. :) Not a traditional golfing language, and most of the code-size is parsing string->int manually. (host^net)>>(32-mask) is only 10 bytes. But it's half way in between for tasks not involving lists of lists, or mapping a function onto a list, because many scalar operations can be done with a 2 or 3 byte instruction, and loops can be constructed around things in a few bytes. Commented May 3, 2019 at 11:53

# C# (Visual C# Compiler), 250+31=281 bytes

(a,b)=>{Func<string,string>h=g=>string.Join("",g.Split('.').Select(x=>{var e=Convert.ToString(int.Parse(x),2);while(e.Length<8)e='0'+e;return e;}));a=h(a);var c=b.Split('/');b=h(c[0]);var d=int.Parse(c[1]);return a.Substring(0,d)==b.Substring(0,d);};


Bytecount includes using System;using System.Linq;

Try it online!

I wrote this in JS as soon as the challenge was posted, but Arnauld beat me to the punch with a much better answer, so here it is in C# instead.

Definitely a lot of room for golfing.

## Explanation:

The function consists of a sub-function called h:

h=g=>string.Join("",
g.Split('.').Select(x => {
var e = Convert.ToString(int.Parse(x), 2);
while (e.Length < 8) e = '0' + e;
return e;
}
);


This sub-function splits the IP Address on ., converts each number to a binary string, left-pads each string with 0 to be 8 bits long, then concatenates the strings into one 32-bit binary string.

This is immediately done in-place with a=h(a); on the given IP Address.
We then split the Subnet mask into an IP Address and a mask number with c=b.Split('/');

The IP Address component is also passed through our sub-function: b=h(c[0]); and the mask number is parsed to an integer: var d=int.Parse(c[1]);

Finally we take the first d bits of both binary strings (where d is the mask number) and compare them: return a.Substring(0,d)==b.Substring(0,d);

• Too tired to work this out so I just Golfed yours for you Commented May 1, 2019 at 16:39
• Actually forgot about PadLeft in that too Try it online! Commented May 1, 2019 at 16:43
• Lots of optimizations. I'm happy to inform you that your rPad is a built-in on strings. pastebin link to TIO link that is too long alone Commented May 2, 2019 at 3:06
• @someone Small FYI: URL shorteners like tinyurl.com are allowed in comments on this SE, unlike most. :) Commented May 2, 2019 at 9:41
• 188 - tinyurl.com/y6xfkbxt - nice url shortening tips @KevinCruijssen
– dana
Commented May 4, 2019 at 4:35

# Linux POSIX shell (with net-tools/iputils) (34 bytes non-terminating, 47 bytes terminating)

What is best suited to parse network masks and addresses than the network utilities themselves? :)

route add -net $2 reject;! ping$1


Warning: the script is potentially damaging to your Internet connectivity, please run with care.

Input: the script takes the tested IP address as first argument, and the tested subnet. as second argument.

Output: the script returns a truthy value (0) if the first argument of the script belongs to the subnet indicated in the second argument. Otherwise, it will never terminate.

Assumptions: the script must be run as a root user, in a clean environment (i.e., no other blackhole route has been set by the administrator, and if a previous instance of the script has been run, the blackhole route it created has been removed). The script also assumes a "working Internet connection" (i.e., a valid default route is present).

## Explanation:

We create a blackhole route to the specified subnet. We then test connectivity to the provided IP address by using ping. If the address doesn't belong to the subnet (and since we assume a properly set Internet connection), ping will try to send packets to that address. Note that whether this address actually responds does not matter, as ping will keep trying forever. Conversely, if the address does belong to the subnet, ping will fail with ENETUNREACH and return 2, and since we negated the command, the script will succeed.

## Example

Test whether 5.5.5.5 belongs to 8.8.8.0/24

$sudo ./a.sh 5.5.5.5 8.8.8.0/24 PING 5.5.5.5 (5.5.5.5) 56(84) bytes of data. [...runs forever...]  (Clean with sudo ip route del 8.8.8.0/24 after running the command). Test whether 5.5.5.5 belongs to 5.5.5.0/24: $ sudo ./a.sh 5.5.5.5 5.5.5.0/24
connect: Network is unreachable
$echo$?
0


(Clean with sudo ip route del 5.5.5.0/24 after running the command).

Test whether 8.8.8.8 belongs to 5.5.5.0/24:

$sudo ./a.sh 8.8.8.8 5.5.5.0/24 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=122 time=2.27 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=122 time=1.95 ms [...runs forever...]  (Clean with sudo ip route del 5.5.5.0/24 after running the command). # 47-byte version if we disallow non-terminating scripts route add -net$2 reject;ping -c1 $1;[$? = 2 ]


As per @Grimy's comment, here's the version which always terminates, and returns 0 (truthy) if the address is in the subnet, and 1 (falsy) otherwise. We make ping terminate with the -c1 flag which limits the number of sent packets to 1. If the address responded, ping will return 0, and if not, ping will return 1. Only if the address belongs to the blackholed subnet will ping return 2, which is thus what we test against in the last command.

• While clever, this doesn't meet the requirement to output a distinct and consistent value if the address is not in the subnet (running forever doesn't count as an output, see also this). Commented May 2, 2019 at 12:10
• @Grimy: But it doesn't silently run forever, so only your 2nd link applies, not the first. Also I think ping would die from SIGPIPE if it was running with stdout+stderr piped into another program, and the reader closed the pipe. And that is the most likely use-case because the exit status can be success either way (if we added a -c1 option to ping to set the count.) But sure, reading its output with var=$(/a.sh) would fail; you'd need a reader that stopped after deciding, rather than reading the whole output and then looking at it. Commented May 3, 2019 at 5:50 • @Grimy Fair point (although for the sake of the argument I could say that we have two consistent values here, since ping will terminate in less than, say, one second in case of a blackholed address). I added a terminating version for an extra 13 bytes! :) Commented May 3, 2019 at 7:10 # JavaScript (ES6), 82 bytes Takes input as (address)(subnet). Returns a Boolean value. a=>s=>!([s,v]=s.split/,+v&&(g=s=>s.split..map(k=v=>k=k<<8|v)|k>>32-v)(a)^g(s))  Try it online! # PHP, 10192 88 bytes -13 bytes from @gwaugh function($i,$r){[$r,$n]=explode('/',$r);return(ip2long($i)&~(1<<32-$n)+1)==ip2long($r);}  Try it online! • Had some fun golfing it (Ty!): function($i,$r){return!((ip2long($i)^ip2long(strtok(r,'/')))>>32-strtok(_));} Commented May 2, 2019 at 12:43 • @Christoph very nice! Never occurred to me that you could just use any token for the second call to strtok(). Yours is 4 bytes shorter than my very similar answer below. Props! Commented May 2, 2019 at 14:34 • @Christoph You should post your solution as a separated answer since it is better than mine. Commented May 2, 2019 at 17:27 # PowerPC/PPC64 C, 116 114 bytes #include<stdio.h> main(){unsigned u[4];char*p=u;for(;p<u+3;)scanf("%hhu%c",p++,u+3);return!((*u^u[1])>>32-p[-4]);}  (Tested on x86_64 Ubuntu 18.04 using powerpc64-linux-gnu-gcc -static and qemu-user.) The program takes the two lines on standard input, and as its exit code it returns 1 if the address matches and 0 if it does not. (So this does depend on the specification not requiring a truthy value for a match and a falsey value for a mismatch.) Note that if you're running interactively, you will need to signal EOF (^D) three times after entering the second line. This relies on PowerPC being big-endian, and also on that platform returning 0 for right-shifting a 32-bit unsigned value by 32. It reads the octets into unsigned values one-by-one, along with the netmask length in another byte; then it takes the xor of the two unsigned 32-bit addresses and shifts out the irrelevant bits. Finally, it applies ! to satisfy the requirement of returning only two distinct values. Note: It might be possible to shave off two bytes by replacing u+3 with p and requiring compilation with -O0. That's living more dangerously than I care to, though. Thanks to Peter Cordes for the inspiration for this solution. # More portable C, 186171 167 bytes Here I'll preserve a more portable version which runs 167 bytes. #include<stdio.h> main(){unsigned a,b,c,d,e,f,g,h,n;scanf("%u.%u.%u.%u %u.%u.%u.%u/%u",&a,&b,&c,&d,&e,&f,&g,&h,&n);return!(n&&((((a^e)<<8|b^f)<<8|c^g)<<8|d^h)>>32-n);}  This program takes the two lines on standard input, and returns exit code 1 if the address is in the subnet, and 0 if it isn't. (So this does rely on the specification not requiring a truthy value for matches and a falsey value for non matches.) A breakdown of the core expression: • a^e, b^f, c^g, d^h calculates the xor of the address and the mask byte-by-byte. • (((a^e)<<8|b^f)<<8|c^g)<<8|d^h then combines them into a single unsigned 32-bit value by a Horner-like method. • ...>>32-n then shifts off the bits of the xor difference that are not relevant to the subnet mask (keeping in mind that - has higher precedence in C than <<) • There is one gotcha, though: if n=0 then ~0U<<32 will give undefined behavior assuming unsigned is 32 bits (which it is on virtually all current platforms). On the other hand, if n=0 then any address will match, so n&&... will give the correct result (taking advantage of the short-circuiting behavior of &&). • Finally, to meet the requirement that the output can only be one of two values, we apply ! to output 0 or 1. -15 bytes due to comments by ceilingcat and AdmBorkBork -4 bytes due to comment by Peter Cordes • Using exit codes to return values is one of the default I/O methods and is thus allowed. Commented May 2, 2019 at 12:34 • @ceilingcat Of course, how silly of me to miss that. Commented May 2, 2019 at 16:19 • @AdmBorkBork OK, thanks, I've changed it to use exit code. Commented May 2, 2019 at 16:19 • idea: target a little-endian or big-endian C implementation (code-golf doesn't require portable code) and type-pun output pointers onto the bytes of an unsigned. e.g. with char*p=&a then p++,p++,p++,... or p--,... as scanf args. The format string would need to be "%hhu.%hhu..." though, so it's a significant tradeoff between that extra size vs. declaring fewer vars and being able to do (a^b)>>(32-count) Commented May 3, 2019 at 6:00 • @PeterCordes Yup, the right shift works, thanks. Commented May 3, 2019 at 16:44 # Stax, 22 bytes é.○▄╗jF⌐§╥§I╓☻lw«ç┴║╫┼  Run and debug it It takes the input parameters space-separated on standard input. Unpacked, ungolfed, and commented, it looks like this. '/:/~ split on slash and push the last group back to the input stack j{ split on space; for each group, run this code block './ split on period {emVB|E evaluate integers and decode integer as base-256 ;e|< peek from input stack and shift left Vu/ integer divide by 2^32 F end of for-each = two values left on stack are equal?  Run this one # x86-64 machine code function, 53 48 bytes changelog: • -2 jz over the shift instead of using a 64-bit shift to handle the >>(32-0) special case. • -3 return in ZF instead of AL, saving 3 bytes for a setnz al. (See also Daniel Schepler's 32-bit machine code answer based on this, which then evolved to use some other ideas we had. I'm including my latest version of that at the bottom of this answer.) Returns ZF=0 for host not in subnet, ZF=1 for in subnet, so you can branch on the result with je host_matches_subnet Callable with the x86-64 System V calling convention as bool not_in_subnet(int dummy_rdi, const char *input_rsi); if you add in setnz al. The input string contains both the host and network, separated by exactly 1 non-digit character. The memory following the end of the CIDR width must contain at least 3 non-digit bytes before the end of a page. (Shouldn't be a problem in most cases, like for a cmdline arg.) Daniel's 32-bit version doesn't have this limitation. We run the same dotted-quad parse loop 3 times, getting the two IPv4 addresses, and getting the /mask as an integer in the high byte of a dword. (This is why there has to be readable memory after the /mask, but it doesn't matter if there are ASCII digits.) We do (host ^ subnet) >> (32-mask) to shift out the host bits (the ones allowed to mismatch), leaving only the difference between the subnet and the host. To solve the /0 special case where we need to shift by 32, we jump over the shift on count=0. (neg cl sets ZF, which we can branch on and leave as the return value if we don't shift.) Note that 32-mask mod 32 = -mask, and x86 scalar shifts mask their count by & 31 or & 63.  line addr machine NASM source. (from nasm -felf64 -l/dev/stdout) num code bytes 1 %use smartalign 2 3 ;10.4.1.33 10.4.0.0/23 true 4 ;10.4.1.33 10.4.0.0/24 false 5 6 ;; https://codegolf.stackexchange.com/questions/185005/im-in-your-subnets-golfing-your-code 7 %ifidn __OUTPUT_FORMAT__, elf64 8 in_subnet: 9 10 00000000 6A03 push 3 11 00000002 5F pop rdi ; edi = 3 dotted-quads to parse, sort of. 12 .parseloop: 13 14 ;xor ebx,ebx ; doesn't need to be zeroed first; we end up shifting out the original contents 15 ;lea ecx, [rbx+4] 16 00000003 6A04 push 4 17 00000005 59 pop rcx ; rcx = 4 integers in a dotted-quad 18 .quadloop: 19 20 00000006 31D2 xor edx,edx ; standard edx=atoi(rdi) loop terminated by a non-digit char 21 00000008 EB05 jmp .digit_entry 22 .digitloop: 23 0000000A 6BD20A imul edx, 10 24 0000000D 00C2 add dl, al 25 .digit_entry: 26 0000000F AC lodsb 27 00000010 2C30 sub al, '0' 28 00000012 3C09 cmp al, 9 29 00000014 76F4 jbe .digitloop 30 ; al=non-digit character - '0' 31 ; RDI pointing to the next character. 32 ; EDX = integer 33 34 00000016 C1E308 shl ebx, 8 35 00000019 88D3 mov bl, dl ; build a quad 1 byte at a time, ending with the lowest byte 36 0000001B E2E9 loop .quadloop 37 38 0000001D 53 push rbx ; push result to be collected after parsing 3 times 39 0000001E FFCF dec edi 40 00000020 75E1 jnz .parseloop 41 42 00000022 59 pop rcx ; /mask (at the top of a dword) 43 00000023 5A pop rdx ; subnet 44 00000024 58 pop rax ; host 45 00000025 0FC9 bswap ecx ; cl=network bits (reusing the quad parse loop left it in the high byte) 49 00000027 F6D9 neg cl 50 00000029 7404 jz .all_net ; skip the count=32 special case 51 52 0000002B 31D0 xor eax, edx ; host ^ subnet 53 0000002D D3E8 shr eax, cl ; shift out the host bits, keeping only the diff of subnet bits 54 55 .all_net: 56 ; setnz al ; return ZF=1 match, ZF=0 not in subnet 57 0000002F C3 ret 58 00000030 30 .size: db - in_subnet

0x30 = 48 bytes


including a _start that calls it on argv[1] and returns an exit status.

## on my desktop
$./ipv4-subnet "10.4.1.33 10.4.0.0/24" && echo "$? : in subnet" || echo "$? : not in subnet" not in subnet$ ./ipv4-subnet "10.4.1.33 10.4.0.0/23"    && echo "$? : in subnet" || echo "$? : not in subnet"
in subnet


It works fine if you pass a command line arg containing a newline instead of a space. But it has to be instead, not as well.

# x86 32-bit machine code function, 38 bytes

Do 9 integer -> uint8_t parses and "push" them on the stack, where we pop them off as dwords or use the last one still in CL. Avoids reading past the end of the string at all.

Also, dec is only 1 byte in 32-bit mode.

    72                             in_subnet:
73 00000000 89E7                   mov   edi, esp
74 00000002 51                     push  ecx
75 00000003 51                     push  ecx                   ; sub esp,8
76                             .byteloop:
77
78 00000004 31C9                   xor   ecx,ecx               ; standard ecx=atoi(rdi) loop terminated by a non-digit char
79                                                             ; runs 9 times: 8 in two dotted-quads, 1 mask length
80 00000006 EB05                   jmp  .digit_entry
81                              .digitloop:
82 00000008 6BC90A                 imul   ecx, 10
83 0000000B 00C1                   add    cl, al
84                              .digit_entry:
85 0000000D AC                     lodsb
86 0000000E 2C30                   sub    al, '0'
87 00000010 3C09                   cmp    al, 9
88 00000012 76F4                   jbe   .digitloop
89                                 ; RDI pointing to the next character.
90                                 ; EDX = integer
91
92 00000014 4F                     dec    edi
93 00000015 880F                   mov    [edi], cl           ; /mask store goes below ESP but we don't reload it
94 00000017 39E7                   cmp    edi, esp
95 00000019 73E9                   jae   .byteloop
96
97                                 ;; CL = /mask still there from the last conversion
98                                 ;; ESP pointing at subnet and host on the stack, EDI = ESP-1
99
100 0000001B 5A                     pop    edx   ; subnet
101 0000001C 58                     pop    eax   ; host
102
103 0000001D 31D0                   xor    eax, edx             ; host ^ subnet
105 00000021 7402                   jz     .end                 ; 32-n = 32 special case
106 00000023 D3E8                   shr    eax, cl
107                             .end:
108                                 ; setz  al                  ; just return in ZF
109 00000025 C3                     ret

1
php ipsn.php 127.1.2.3 127.0.0.0/24  Try it online! -2 bytes borrowing @Christoph's little trick for strtok(). His answer is still shorter though! # x86 assembly function, 49 43 bytes This is mostly posted to satisfy Peter Cordes's request for the revised version I created. It can probably go away once/if he incorporates it into his answer. This function expects esi to point to an input string, with the address and subnet parts separated either by a space or a newline character, and the return value is in the ZF flag (which by definition has only two possible values).  1 %use smartalign 2 3 ;10.4.1.33 10.4.0.0/23 true 4 ;10.4.1.33 10.4.0.0/24 false 5 6 ;; https://codegolf.stackexchange.com/questions/185005/im-in-your-subnets-golfing-your-code 7 in_subnet: 8 9 ;xor ebx,ebx ; doesn't need to be zeroed first; we end up shifting out the original contents 10 ;lea ecx, [rbx+4] 11 00000000 6A09 push 9 12 00000002 59 pop ecx ; ecx = 9 integers (8 in two dotted-quads, 13 ; 1 mask length) 14 15 00000003 89E7 mov edi, esp 16 00000005 83EC0C sub esp, 12 17 .quadloop: 18 19 00000008 31D2 xor edx,edx ; standard edx=atoi(rdi) loop terminated by a non-digit char 20 0000000A EB05 jmp .digit_entry 21 .digitloop: 22 0000000C 6BD20A imul edx, 10 23 0000000F 00C2 add dl, al 24 .digit_entry: 25 00000011 AC lodsb 26 00000012 2C30 sub al, '0' 27 00000014 3C09 cmp al, 9 28 00000016 76F4 jbe .digitloop 29 ; al=non-digit character - '0' 30 ; RDI pointing to the next character. 31 ; EDX = integer 32 33 00000018 4F dec edi 34 00000019 8817 mov [edi], dl 35 0000001B E2EB loop .quadloop 36 37 0000001D 59 pop ecx ; /mask (at the top of a dword) 38 0000001E 5A pop edx ; subnet 39 0000001F 58 pop eax ; host 40 00000020 0FC9 bswap ecx ; cl=network bits (reusing the quad parse loop left it in the high byte) 41 42 ; xor cl, -32 ; I think there's some trick like this for 32-n or 31-n, but maybe only if we're masking to &31? Then neg or not work. 43 44 00000022 31D0 xor eax, edx ; host ^ subnet 45 ; xor edx, edx ; edx = 0 46 00000024 F6D9 neg cl 47 00000026 7402 jz .end 48 00000028 D3E8 shr eax, cl ; count=32 special case isn't special for a 64-bit shift 49 .end: 50 0000002A C3 ret 51 0000002B 2B .size: db - in_subnet


And the x86 Linux wrapper part:

53                                  global _start
54                                  _start:
55 0000002C 8B742408                    mov    esi, [esp+8]   ; argv[1]
56 00000030 E8CBFFFFFF                  call   in_subnet
57 00000035 0F95C0                      setnz  al
58 00000038 0FB6D8                      movzx  ebx, al
59 0000003B B801000000                  mov    eax, 1         ; _exit syscall
60 00000040 CD80                        int    0x80


-6 bytes due to suggestion from Peter Cordes to return the value in ZF.

• I guess I could save one byte by removing the last xor edx,edx and replacing cmovz eax,edx with jz .nonzero; xor eax,eax; .nonzero:. cmovz still wins if we have calling convention ebx=0. Commented May 3, 2019 at 23:45
• Can we just jz over the shr to the setz or the ret? We can swap the setnz to setz and return 1 for a match if that helps. Or even say that our return value is ZF. I should have done that in my answer. (But I don't think we can justify requiring the caller to create constants for us, like ebx=0. My answer on Tips for golfing in x86/x64 machine code argues that would be stretching a custom calling convention too far. Commented May 3, 2019 at 23:51
• BTW, I use cut to remove some columns from the NASM listing output because all my instructions are short: nasm -felf foo.asm -l/dev/stdout | cut -b -34,\$((34+6))-. Also, I used mov instead of movzx in my _start caller because the exit status comes from the low byte of the arg to sys_exit(). The kernel ignores the higher bytes. Commented May 3, 2019 at 23:53
• I guess that would work. That takes the count down to 43 bytes and then I insert setnz al after call in_subnet in the wrapper. Commented May 3, 2019 at 23:58
• Yup. Easy to imagine the normal use case for this function would be call/je, rather than printing or further passing along the result. Like I pointed out in the "tips", some system-call calling conventions already do this in real life (usually with CF=error). Commented May 4, 2019 at 0:03

# Java 215 211 207 202 200 199 198 190 180 bytes

Long k,c;boolean a(String i,String s){return(b(i)^b(s))>>32-k.decode(s.split("/")[1])==0;}long b(String i){for(c=k=0l;c<4;k+=k.decode(i.split("[./]")[3+(int)-c])<<8*c++);return k;}


Outputs true for truthy and false for falsy.

Note: This uses long instead of int for the potential right shift of 32.

Try it online!

Saved 1 byte thanks to ceilingcat

Saved 10 bytes thanks to Peter Cordes

• This doesn't output a "distinct and consistent value" for falsey. Commented May 3, 2019 at 12:54
• I'd argue that it's distinctly and consistently non-zero but if that's not the spirit of the challenge, I can change it.
– Poke
Commented May 3, 2019 at 14:10
• A 64-bit integer supports left-shifts by 32. Also, you can right shift host ^ net to shift out the bits you want to remove, instead of actually creating a mask. But I guess Java needs a compare in there to create a boolean from an integer. Maybe a !, because it doesn't matter which of true or false you produce for which output. (I asked the OP for clarification about whether they intended to exclude 0 / non-zero, and they said yes they were aware of the consequences of that wording: Commented May 4, 2019 at 0:00
• @PeterCordes Converting everything to long does lose me some bytes but I make up for it by being able to remove the ternary and doing the XOR as you suggest. I'm checking what else I can golf before posting
– Poke
Commented May 4, 2019 at 6:56

# Charcoal, 36 bytes

≔⪪Ｓ/θ≔Ｉ⊟θζ⊞θＳＵＭθ÷↨Ｉ⪪ι.²⁵⁶Ｘ²⁻³²ζ⁼⊟θ⊟θ


Try it online! Link is to verbose version of code. Takes the subnet as the first parameter and and outputs - only if the address lies within the subnet. Explanation:

≔⪪Ｓ/θ


Split the subnet on /.

≔Ｉ⊟θζ


Remove the mask and cast it to integer.

⊞θＳ


Push the address to the array.

ＵＭθ÷↨Ｉ⪪ι.²⁵⁶Ｘ²⁻³²ζ


Split both addresses on ., convert them to integers, interpret as base 256, and discard the masked bits.

⁼⊟θ⊟θ


Compare the two values.

# Japt, 26 bytes

Ëq'/
ËÎq. Ë°¤ù8Ã¬¯Ug1,1Ãr¶


Try it

-3 bytes thanks to @Shaggy!

Input is an array with 2 elements [address, subnet]. Transpiled JS below:

// U: implicit input array
// split elements in U on the / and
// save back to U using a map function
U = U.m(function(D, E, F) {
return D.q("/")
});
// map the result of the previous operation
// through another function
U.m(function(D, E, F) {
return D
// get the address portion of the / split
// value and split again on .
.g().q(".")
// map each octet through another function
.m(function(D, E, F) {
// convert the octet to a base 2 string
// left padded to a length of 8
return (D++).s(2).ù(8)
})
// join the base 2 octets
.q()
// take the left bits of the joined octets
// determined by subnet size
.s(0, U.g(1, 1))
})
// at this point, the intermediate result
// contains 2 masked values, reduce
// using === to check for equality
.r("===")

• 26 bytes Commented May 5, 2019 at 16:21
• Interesting - I didn't realize you could coerce a string to a number with ++.
– dana
Commented May 5, 2019 at 23:03
• Yup, just like you can in JS. It's no use, though, if you need to resuse the original value later on, though, but it is handy on occasion. Commented May 5, 2019 at 23:17
• The need for the comma in the g method is annoying me; can't figure out a way around it at all. At least not one that'll save you a byte. Commented May 5, 2019 at 23:18

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

a=>{var b=a.Select(x=>x.Split(".").SelectMany(g=>Convert.ToString(int.Parse(g.Split("/")[0]),2).PadLeft(8)).Take(int.Parse(a[1].Split("/")[1])));return b.First().SequenceEqual(b.Last());}


I can definitely golf this down more.

Try it online!

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

a=>a.Select(x=>x.Split('.','/').Take(4).Aggregate(0L,(y,z)=>y<<8|int.Parse(z))>>32-int.Parse(a[1].Split('/')[1])).Distinct().Count()<2


Try it online!

LINQ statement that takes a 2-element string array as input in [address, subnet] format.

Each dotted quad is converted into 32 bits of a long using bit manipulation. The bits are right shifted by the subnet size and elements are compared for equality.

There were a couple of C# answers at the time that this answer was posted, but none that used pure bit manipulation.

// a: input array containing address and subnet
a=>a
// iterate over input elements
.Select(x=>x
// split element on . and /
.Split('.','/')
// the subnet will have 5 elements,
// we only want the parts before the /
.Take(4)
// use an aggregate function to convert dotted quad to 32 bits
.Aggregate(0L,(y,z)=>y<<8|int.Parse(z))
// shift bits of aggregate to the right
>>
// shift amount determined by subnet size
32-int.Parse(a[1].Split('/')[1])
)
// test for equality by checking if number
// of unique values is equal to 1
.Distinct()
.Count()<2


# Ruby (48 bytes)

require 'ipaddr'