Floating Point XOR

16

Your task is pretty simple. Given two floats, bitwise xor the binary representation of them, and output that as a float.

For example,

Normal: 16.7472 ^ 123.61 = 7.13402e-37
Binary: 01000001100001011111101001000100 ^ 01000010111101110011100001010010 = 00000011011100101100001000010110

Normal: 2.2 ^ 4.4 = 1.17549e-38
Binary: 01000000000011001100110011001101 ^ 01000000100011001100110011001101 = 00000000100000000000000000000000

Normal: 7.898 ^ 3.4444 = 1.47705e-38
Binary: 01000000111111001011110001101010 ^ 01000000010111000110101001111111 = 00000000101000001101011000010101

Restrictions/clarifications:

virchau13

Posted 2019-09-15T13:33:20.810

Reputation: 309

Welcome to Code Golf! Could you provide some more test cases? – Stephen – 2019-09-15T13:45:57.923

2Does Boolean list count as a convenient method? – Adám – 2019-09-15T14:11:35.713

Why the lenient input format but stringent output format? How about Boolean list as output? – Adám – 2019-09-15T14:12:18.630

I would say no. I'll edit the question to clarify on what a "convenient method" is. – virchau13 – 2019-09-15T14:12:49.813

What if the resulting bit pattern doesn't represent a valid float? – Adám – 2019-09-15T14:13:16.287

I don't think that's possible...? I just wrote a program to bruteforce 4-byte floats from 0 to UINT_MAX, and it didn't die anywhere. NaN is a valid output, if that's what you're asking. – virchau13 – 2019-09-15T14:24:57.910

@virchau With the exactly right input, it should be possible to produce the representation of a sNaN, causing an error. – Adám – 2019-09-15T14:32:14.573

I found the inputs that work with 8-byte floats (doubles) to produce an sNaN: 1.54234e+260 1.63233e-260 It doesn't throw an FPE on my system, though. Can you try it and see? – virchau13 – 2019-09-15T14:51:42.020

That gives me 1.675075421658194e-309. – Adám – 2019-09-15T17:47:59.067

23"binary representation" of a float is extremely ambiguous. You'll need to define which representation you're using. There are an infinite number of representations, including the finite number already used by life on this planet, some being more popular than others, such as IEEE 754 – Reversed Engineer – 2019-09-16T10:07:44.310

1Do we have to handle NaN input? – Grimmy – 2019-09-16T12:42:36.893

7This question would be more interesting as "xor the values" rather than "xor the representations". The latter is of course identical to "xor two 32-bit integers" in any language that lacks a type system or admits type punning, and thus is pretty boring... – R.. GitHub STOP HELPING ICE – 2019-09-16T14:33:44.803

5Do we have to handle infinity, subnormals, or negative 0, as either input or output? – Grimmy – 2019-09-16T16:24:11.100

@R.., you're assuming 32-bit IEEE-734. XORing a pair of Microsoft Basic 40-bit floats is a bit more exciting. – Mark – 2019-09-17T00:13:21.653

@R..: Indeed -- and then of course input and output should be given as exact fractions. (It may be assumed that the input fractions are in lowest terms and the denominators are not powers of two, so the binary representations are unique). – hmakholm left over Monica – 2019-09-17T01:38:46.007

3@Mark: No, as written the question is just about xor'ing their representations, whatever those representations are. The result is dependent on the floating point format but the algorithm is always a single xor instruction on the representation, which is pretty boring. – R.. GitHub STOP HELPING ICE – 2019-09-17T01:43:53.290

@HenningMakholm: Huh? These are floating point (diadic rationals) not arbitrary rationals. I don't even know what xor would mean on the latter. – R.. GitHub STOP HELPING ICE – 2019-09-17T01:44:36.573

1@R..: Take their representations as (infinite, repeating) binary fractions, XOR them bit by bit; find out what the result represents? – hmakholm left over Monica – 2019-09-17T01:49:47.913

@HenningMakholm: That's not mathematically meaningful, unlike xor of float values which is addition of polynomials over Z/2Z. – R.. GitHub STOP HELPING ICE – 2019-09-17T01:51:30.373

@ReversedEngineer: For languages that support floating point, I think it's implied that you should use the language's native object-representation, whatever that is. (e.g. something equivalent to type-punning to integer and back). If the language doesn't support type-punning or XOR of FP data, then you have to implement it yourself using whatever FP representation your chosen implementation uses. – Peter Cordes – 2019-09-17T07:48:40.817

Answers

53

x86-64 machine code, 4 bytes

0f 57 c1 c3

In assembly:

xorps xmm0, xmm1
ret

This is a callable function that takes two floats or doubles as arguments (in xmm0 and xmm1) and returns a float or double (in xmm0).

That matches the calling conventions of both Windows x64 and the x86-64 SysV ABI, and works for floats as well as doubles. (They're passed / returned in the low 4 or 8 bytes of XMM registers).

harold

Posted 2019-09-15T13:33:20.810

Reputation: 1 199

12

C++ (gcc), 74 32 bytes

#define f(x,y)*(int*)x^=*(int*)y

Try it online!

I haven’t previously golfed in C++ so am grateful to all those who helped halve the size of the code! A macro which takes pointers to two floats as its arguments and modified the first to return the result.

Thanks to @12Me1 for saving 2 bytes and @Arnauld for saving 4! Thanks to @Nishioka for saving another 14, @Neil a further 6 and @AZTECCO and @Nishioka another 11! Thanks to @PeterCordes for saving 5 bytes!

Nick Kennedy

Posted 2019-09-15T13:33:20.810

Reputation: 11 829

1You can remove the linebreaks to save 2 chars, and this also works in C – 12Me21 – 2019-09-15T16:40:54.870

1You can save 4 more bytes with z=*(int*)x^*(int*)y;. – Arnauld – 2019-09-15T16:46:03.680

-2 bytes: using F=float;F f(F*x,F*y){int z=*(int*)x^*(int*)y;return*(F*)&z;} – Nishioka – 2019-09-15T17:07:19.257

Function-like macro, 63 bytes: #define f(x,y)[&]{int z=*(int*)x^*(int*)y;return*(float*)&z;}() – Nishioka – 2019-09-15T18:33:27.107

1With gcc extensions, 54 bytes: #define f(x,y)({int z=*(int*)x^*(int*)y;*(float*)&z;}) – Nishioka – 2019-09-15T18:34:17.983

2Since you're using pointers, is it legal to use one of the inputs as the output? If so, you could write (*(int*)x^=*(int*)y). – Neil – 2019-09-15T18:47:26.950

62 – AZTECCO – 2019-09-15T18:54:33.737

1Taking into consideration @Neil's suggestion it would get to 48 bytes: #define f(x,y)({*(int*)x^=*(int*)y;*(float*)x;}) – Nishioka – 2019-09-15T19:00:59.723

1Actually I've missed the obvious, so another -8 bytes: #define f(x,y)({*(int*)x^=*(int*)y;*x;}) – Nishioka – 2019-09-15T19:11:30.767

1Saves 2 more – AZTECCO – 2019-09-15T19:23:51.863

137 bytes after merging with @AZTECCO's solution. – Nishioka – 2019-09-15T19:33:28.023

Does this work on expressions like f(3.0+1.2,0)? – Purple P – 2019-09-16T18:22:53.690

@PurpleP no because it takes pointers to floats as its arguments. – Nick Kennedy – 2019-09-16T18:36:22.097

1Note that this is only "safe" in debug mode, or if you use gcc -fno-strict-aliasing. Maybe you should have specified C++ (MSVC) if you wanted to use type punning via pointer casting. Although it does work on G++ with optimization disabled. BTW, this also requires a C++ implementation where sizeof(int) == sizeof(float), so it won't work on G++ for AVR or MSP430 for example. (TIO runs on x86, which uses 32-bit int and float like all "normal" modern CPUs). – Peter Cordes – 2019-09-17T07:26:13.197

1You don't need a comma or GNU C statement-expression here; the golf rules allow returning a value by modifying an input operand by reference. Also, since this is golf, this version doesn't strictly need the () safety parens. The "caller" just has to make sure not to use it as part of a larger expression. It already has lots of other weirdness (like taking args by reference and destroying one of them). – Peter Cordes – 2019-09-17T07:43:12.197

I have a better solution (but it's C-only). – S.S. Anne – 2019-09-17T21:11:13.477

@PeterCordes I think I’ve modified my answer in accordance with this - thanks! – Nick Kennedy – 2019-09-17T23:31:18.470

9

ARM Thumb Machine Code, 6 4 Bytes

48 40 70 47

In assembly:

EORS R0, R1 ; Exclusive Or of the first two params, store result in the return register
BX LR       ; Branch to the value stored in the Link Register (Return address)

Under the standard Arm calling convention, the first two parameters are passed in the registers R0 and R1, results are returned in R0, and LR holds the return address. Assuming you're using the soft float ABI with 32 bit floats, this will perform the desired operation in 4 bytes.

-2 bytes thanks to Cody Gray

Sir_Lagsalot

Posted 2019-09-15T13:33:20.810

Reputation: 4 898

2Would it be possible to use EORS r0, r1 instead, in order to save 2 bytes? That's only a 2-byte instruction (48 40), compared to your 4-byte EOR. You're already targeting Thumb, so this should work fine, as far as I can see. The only difference is it updates status flags, but you don't care about that side effect in this case. – Cody Gray – 2019-09-17T00:31:43.250

2You should specify that this is using the soft-float ABI which passes FP args in integer registers, not VFP / NEON s0 and s1. – Peter Cordes – 2019-09-17T07:30:32.843

6

Python 3 + numpy, 75 59 bytes

lambda x,y:(x.view("i")^y.view("i")).view("f")
import numpy

Try it online!

Defines a lambda which takes two numpy float32 arrays as its arguments and returns a numpy float32 array.

Thanks to @ShadowRanger for saving 14 bytes, and Joel a further 2!

If the import can be dropped (since my lambda itself calls methods on numpy objects rather than any base numpy functions), I could save a further 13 bytes. I’m uncertain on this from the code golf standard rules.

Nick Kennedy

Posted 2019-09-15T13:33:20.810

Reputation: 11 829

It's shorter than Jelly and Ruby answers. Nice! – Eric Duminil – 2019-09-17T09:06:07.430

You can shave off 27 bytes (dropping it to 48 bytes) by removing the import entirely (just assume the caller passed you numpy.float32s so you can use their methods) and replacing both int32 uses with 'i' and the float32 usage with 'f'; the dtype parameter can be a string which gets converted to the real dtype for you, and conveniently, 'i' and 'f' are legal ways to make those dtypes, which removes the need for the function to import numpy stuff into its namespace at all. Not sure if it's Code Golf legal to remove the import but still assume numpy inputs... – ShadowRanger – 2019-09-17T19:41:07.003

I thought the imports had to be included, but I’m not sure. Thanks for the tip re the dtypes! – Nick Kennedy – 2019-09-17T19:44:22.287

@NickKennedy: Yeah, if the import is required, it only saves 8 bytes (two each from int32 to 'i', four from float32 to 'f'), but that's still something. If the import is strictly required, you could just change it to import numpy to assert the package exists, without using from numpy import* to extract names from it. That would get you another six bytes, down to 61 bytes total. – ShadowRanger – 2019-09-17T19:46:35.790

The f= part can be omitted per rules. Also, the inputs do not have to be converted to arrays, they can just be np.float32 numbers: TIO.

– Joel – 2019-09-17T21:08:54.187

6

Jelly + numpy, 89 77 bytes

“(F(“I^F(“IvF).item()”;"F“¢lẒṾ:/²)Ɓɱ¡vẠ⁷5Rʠ¡7ɼṆṪ{ė4¶Gẉn`¡Ð}ṫȥṄo{b»Ḳ¤¹ṣḢ}jʋƒŒV

Try it online!

Has the dubious honour of being longer than the Python 3 code it reproduces, largely because of the need to convert to/from numpy objecte and the fact that numpy isn’t loaded by Jelly so the __import__() built-in has to be used.

A monadic link taking the two floats as a list as its argument and returning a float.

Evaluates the following Python 3 code:

(__import__('numpy').float32(x).view("i")^__import__('numpy').float32(y).view("i")).view(__import__('numpy').float32).item()

where x and y are substituted with the input.

Nick Kennedy

Posted 2019-09-15T13:33:20.810

Reputation: 11 829

5

APL (Dyalog Unicode), 14 bytesSBCS

Full program. Prompts for 1-column matrix of two IEEE 754 64-bit floating-point numbers (binary64) from stdin. Prints one such number to stdout.

645⎕DR≠⌿11⎕DR⎕

Try it online!

 prompt (numbers that collapse to non-floats can be forced into floats with the function ⊃⊢⎕DR⍨645,⍨⎕DR)

11⎕DR convert to 1-bit Binary (1) Data Representation (2-row, 64-column matrix)

≠⌿ vertical XOR reduction

645⎕DR convert to 64-bit float (5) Data Representation (single number)

Adám

Posted 2019-09-15T13:33:20.810

Reputation: 37 779

4

VAX BASIC (later VMS BASIC, then Compaq Basic), 11 bytes

H = F XOR G

Seems a bit silly to me, obviously, older languages will do better because they didn't worry abut strong-typing issues as much.

RBarryYoung

Posted 2019-09-15T13:33:20.810

Reputation: 141

1Welcome to the site and nice first answer! I've edited out what appears to be an extraneous header, but if not feel free to edit it back in, along with any accompanying information – caird coinheringaahing – 2019-09-16T14:20:01.580

3

Octave, 59 bytes

@(a,b)(t=@typecast)(bitxor(t(a,u='int32'),t(b,u)),'single')

Try it online!

Typecast is the MATLAB/Octave way of casting without changing the underlying bits. This is required because bitxor only works on integers. No idea why they never implemented floating point numbers, even though you can explicitly specify the AssumedType as a third argument to bitxor. I guess the only use is recreational programming.

Sanchises

Posted 2019-09-15T13:33:20.810

Reputation: 8 530

Bitwise stuff on FP bit patterns is useful in assembly language to do things with the sign bit, or rarely to stuff an integer into the exponent field as part of an exp() implementation. But I assume Octave already has functions/operators for copysign and negation. And they don't care about micro-optimizations like using AND (with a constant mask) then XOR to conditionally flip the sign of one value based on the sign of another. In a real optimization project in asm (actually C with AVX intrinsics) I have used XOR of floats then looking at the sign bit to avoid cmp against zero. – Peter Cordes – 2019-09-17T07:36:08.467

2

JavaScript (Node.js),  105  101 bytes

Shorter Node version suggested by @Neil
Saved 4 more bytes thanks to @ShieruAsakoto

Takes input as (x)(y).

x=>y=>(v=Buffer(4),v.writeInt32LE((g=n=>v.writeFloatLE(n)&&v.readInt32LE())(x)^g(y)),v.readFloatLE())

Try it online!


JavaScript (ES6), 115 bytes

Takes input as an array of 2 floats.

a=>(v=new DataView(new ArrayBuffer(4))).getFloat32(v.setUint32([x,y]=a.map(n=>v.getUint32(v.setFloat32(0,n))),x^y))

Try it online!

Arnauld

Posted 2019-09-15T13:33:20.810

Reputation: 111 334

FYI Node's Buffer saves a few bytes: a=>(v=new Buffer(4),[x,y]=a.map(n=>v.writeFloatLE(n)&&v.readInt32LE()),v.writeInt32LE(x^y),v.readFloatLE()). – Neil – 2019-09-15T18:57:33.350

@Neil Thanks! (saved 2 more bytes by using a function instead of map) – Arnauld – 2019-09-15T21:50:11.400

1Dropping the new in new Buffer(4) should also work iirc – Shieru Asakoto – 2019-09-19T14:36:02.203

2

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

x=>BitConverter.Int32BitsToSingle(x.Aggregate(0,(a,b)=>a^BitConverter.SingleToInt32Bits(b)))

Try it online!

Embodiment of Ignorance

Posted 2019-09-15T13:33:20.810

Reputation: 7 014

1

You could use unsafe code for this, though I'm not sure if the "unsafe" keyword should affect the bytecount or not.

– negative seven – 2019-09-15T18:41:23.810

2

Perl 5 -p, 31 27 bytes

-4 bytes thanks to Grimy

$\=unpack f,$a^=pack f,$_}{

Try it online!

nwellnhof

Posted 2019-09-15T13:33:20.810

Reputation: 10 037

2

MATL, 10 bytes

4Z%Z}Z~9Z%

Try it online!

Splitting with Z} was shorter than taking two inputs ,4Z%]

Sanchises

Posted 2019-09-15T13:33:20.810

Reputation: 8 530

2

C, 23 bytes

f(int*x,int*y){*x^=*y;}

Try it online!

This might be a bit shifty; it takes the pointers to floats as pointers to ints.

However, it does work (this is C after all).

This takes advantage of acceptable input by taking a pointer to the variable and modifying it in-place. No (usable) value is returned.

S.S. Anne

Posted 2019-09-15T13:33:20.810

Reputation: 1 161

1

Wolfram Mathematica, 50 bytes

BitXor@@(FromDigits[RealDigits[#,2,32,0][[1]],2]&)

Although I strongly suspect that this could be more golfed, the two extra arguments in RealDigits function seem to be necessary for getting a correct result.

Try it online!

polfosol ఠ_ఠ

Posted 2019-09-15T13:33:20.810

Reputation: 519

1

Ruby, 78 67 bytes

-11 bytes thanks to @grawity.

->x{[x.pack("gg").unpack("NN").inject(&:^)].pack(?N).unpack(?g)[0]}

Try it online!

Input is an array of two floats.

Eric Duminil

Posted 2019-09-15T13:33:20.810

Reputation: 701

x.map{|v|[v].pack(?g).unpack(?N)[0]}x.pack("gg").unpack("NN") – user1686 – 2019-09-18T12:32:49.150

@grawity: Awesome, thank you very much! The code is still longer than in Python, though. :-/ – Eric Duminil – 2019-09-18T13:35:05.203

1

Lua, 73 bytes

a,b=('II'):unpack(('ff'):pack(...))print((('f'):unpack(('I'):pack(a~b))))

Try it online!

This code assumes 4-bytes unsigned integers and floats which is configuration on tio.run. Run as full program with input as arguments.

val says Reinstate Monica

Posted 2019-09-15T13:33:20.810

Reputation: 409

1

Java (JDK), 109 76 bytes

(a,b)->Float.intBitsToFloat(Float.floatToIntBits(a)^Float.floatToIntBits(b))

Try it online!

Been a while since I golfed in Java and I'm not certain if I need the declaration on the LHS as part of the byte count? If it used DoubleBinaryOperator the LHS would be shorter, but the RHS would have to use Double.doubleToLongBits and Double.longBitsToDouble, so that's actually longer.

Thanks to Neil for a substantial savings on the byte count!

David Conrad

Posted 2019-09-15T13:33:20.810

Reputation: 1 037

1IIRC you don't even need the assignment as part of the byte count, only as part of any test suite you might include in your Try it online! header. – Neil – 2019-09-18T12:37:57.850

@Neil Thank you! That makes a big difference! – David Conrad – 2019-09-18T18:01:06.220

0

Clean, 36 bytes

f::!Real!Real->Real
f _ _=code{xor%}

Try it online!

Thankfully the Real and Int types are the same sizes on 64-bit platforms...
Unfortunately requires a complete signature otherwise the graph turns into a pretzel and everything segfaults.

Οurous

Posted 2019-09-15T13:33:20.810

Reputation: 7 916