[Question]
  [
This is different from [Floating Point XOR](https://codegolf.stackexchange.com/questions/192862/floating-point-xor), and more in the spirit of a comment by R.. GitHub STOP HELPING ICE on that challenge, but I thought of this idea independently.
## Generalized XOR
The bitwise exclusive or (XOR) operation is usually considered in terms of integers, but it can be naturally generalized (extended, in fact!) to real numbers. There are some finicky details when asking which representation to choose for finite expansions, but to simplify matters, we will only use binary floats.
For example, `3.141592653589793 ^ 0.2` is
```
3.141592653589793: 11.001001000011111101101010100010001000010110100011000
0.200000000000000: 00.0011001100110011001100110011001100110011001100110011010
                   11.000101110000110001011001101110111011011010010000010...
                       ≈ 3.090032203987506
```
The values were written out exactly and a bitwise XOR performed. Note that the result is not always exactly representable as a double, but we can round.
## Challenge
Given two positive (IEEE-754) double-precision floating-point numbers, find their XOR as *numbers*. The answer must have a *relative* accuracy of `1e-6`, i.e., the error should be at most `1e-6` times the correct answer. (The error bound is there because the result is not always exactly representable.)
Examples (rounded to within 1 ulp—answers may vary):
```
1 ^ 2 -> 3
3.141592653589793 ^ 2.141592653589793 -> 1
2.718281828459045 ^ 2.141592653589793 -> 0.5776097723422073  (funnily close to the Euler–Mascheroni constant)
1000000000 ^ 0.1 -> 1000000000.1
0.002 ^ 0.0016666666666666668 -> 0.0036340002590589515
0.002000000000000001 ^ 0.002 -> 8.6736173798840355e-19
1e+300 ^ 1 -> 1e+300
1e+300 ^ 4e299 -> 1.2730472723278628e+300
0.1 ^ 0.1 -> 0
7.888609052210118e-31 ^ 5.380186160021159e-32 -> 8.426627668212235e-31
```
### Notes
Subnormal numbers ([1](https://en.wikipedia.org/wiki/Subnormal_number)), nonpositive numbers, and non-finite numbers do not need to be supported, but it should output zero when the operands are equal. Standard loopholes are of course not allowed, and the inputs may be given in any format corresponding closely to a float (decimal, 64-bit integer bit representation, etc.). This is a code golf question; may the shortest answer win.
  
[Answer]
# JavaScript, 47 bytes
```
f=(x,y,k=8**341)=>k&&k*(x<k^y<k)+f(x%k,y%k,k/2)
```
[Try it online!](https://tio.run/##dU/tasMgFP29PoV/1mhqnVdjq9D0RUILIejYDLVUKcnTZ3FhZR3swIV7zz3nfny29zZ2t49r2t71NLkaD3SkvtZlKSsg9dGv177Ew8Gfx4MnG4eHV0/HOfybIFOyMXVttBHVqGmAInGiqJEMKlBG7JRU2uyNnPm/VNYJtgctdI5KGV6pf3TAf0ARZ5ApzjgXueIcds/Qjz5/Aizy7wvBbmSeBr@LygpjFjMsi04rF24I9zahhIJDj3fJ6qULlxh6y/rwjlPDZ19xLihKTZ5ZbI9z7jBjLBEyfQE "JavaScript (V8) – Try It Online")
`k` runs through every representable power of 2 and adds it to the result, or not, as appropriate.
[Answer]
# [Wolfram Language (Mathematica)](https://www.wolfram.com/wolframscript/), 58 bytes
```
Plus@@Apply@Xor//@{0,(i=#2;2^--i#&/@#)&@@@#~RealDigits~2}&
```
[Try it online!](https://tio.run/##bU7BasMwDL3nNwxhC45jybFjMzo02LmU7TIYC5jSboa0K212GCH59SzuCKyjD4Skp6cn7Xz7sdn5Nqz9uF2Mq@brRPRwODTf9PJ5LArqJL8JC4Z3WOd5YGlB7DYlIjY8bXzzGN5DexqwT8fVMezbV5bfL2lL7C2lbDk8r/1@6JIOOPY86ZSAErRDo5W2rnKK438mylBUYNHGKLWTpb4uAzmDSwGRkWJqkMcE5hJ2nqO8AJzV5@8gq9XkBX/qMqvRud9VmK9UwlprpJMaESSAzepcAddCWQnWgJn8YPo20tgn/fgD "Wolfram Language (Mathematica) – Try It Online")
Input two `Real`s in a list. Also works with an arbitrary even number of `Real` arguments.
[Answer]
# [Python 3](https://docs.python.org/3/), 57 bytes
```
f=lambda a,b,e=8**341:e and(a//e+b//e)%2*e+f(a%e,b%e,e/2)
```
[Try it online!](https://tio.run/##bVDLbsMgELz7K7hEAodgdjH2EikfgxuiIsUP2e4hX@9CqkZ125VmQcvszIjpsb6Pg9liP43zypbHUiSoJaxzePuYlzgO99jHlYMuSyu22@Xu@@7qmZedDBcqS1PDOTA/XLmvqnDsUhMHLMPxxv0hyC4hVJg2xzkvsTgwXnCjoAbrsLHGkmudkVqh3pWQBQeJ@fjLxt@TTEPVAiFl1Nbp2v5Pg5dFMoU80UprlLlDsy96Pe/DwZP9DAfhaJIU/LjXAZ372oRvj1YRUaOdtoigASicDEirDGmgBpqkBilqmmZVcWbTHIeVp1@UnRDF9gk "Python 3 – Try It Online")
[Answer]
# [C (gcc)](https://gcc.gnu.org/), ~~202~~ 196 bytes
*-6 bytes thanks to [@ceilingcat](https://codegolf.stackexchange.com/users/52904/ceilingcat)*
```
double a,b,t;long*c=&a,*d=&b,r,e,m=1L<<52;main(){scanf("%lf%lf",c,d);b>a?t=a,a=b,b=t:0;for(r=(*c&m-1|m)^(*d&m-1|m)>>(*c>>52)-(*d>>52),e=*c&m*4095;*c^*d&&!(r&m);r+=r)e-=m;*c=e|r&~m;printf("%f",a);}
```
[Try it online!](https://tio.run/##bY5bCsIwFES3ooIhibfSV9Qab9yAaxCSNBWhaSXWLx9br1H8FOZjOByYscnJ2nGs@5tp3USDgUG2fXfiFokGXiMxEMCBx@yw24lcen3uKLtfre4aOpu3TcwMLNRMGqX3A2rQaMDgsE1l0wcakHJLfJI9PDtSXv@qUhErJXKWRPgt4PBj8jKthOT2GF0ypYF4JsMCA3MJ@sjRPQJ5eXkJ5274fIj7msnnOBbLrMxEla9EITbVuircIk0n@T/6Bg "C (gcc) – Try It Online")
What the program basically does:
* save two doubles from `stdin` into variables `a` and `b`; if needed, swap the two doubles so `a>b` always;
* take the exponent of `a` as the exponent of the result;
* take the fraction parts of `a` and `b`, the leading implicit bit, left shift `b` (according to the exponents difference), and XOR the two values to obtain the fraction of the result;
* finally, in the result if there are leading zeros in the fraction, remove them (and decrement the exponent).
[Answer]
# x86-64 machine code, x32 ABI, ~~64~~ ~~58~~ ~~55~~ ~~54~~ ~~53~~ ~~49~~ ~~48~~ ~~46~~ 44 bytes
xxd output:
```
00000000: 87fc 5859 5e5a 6629 d17d 0548 96f7 d9eb  ..XY^Zf).}.H....
00000010: 01ca 6683 f940 7d0f 48d3 ee48 31f0 87d1  ..f..@}.H..H1...
00000020: 7805 48d1 e0e2 f951 5089 fcc3            x.H....QP...
```
In assembly, with comments:
```
xor_double:
    xchg esp,edi # save stack pointer and points new stack at the input
    # long doubles are 80-bit floating point numbers,
    # with the low 8 bytes giving the significand and the high 2 bytes giving the exponent
    # Conveniently, long doubles have an alignment of sixteen, so the "stack" now looks like:
    #  rsp    --> significand of first argument 
    #  rsp+8  --> exponent of first argument (plus six bytes of junk)
    #  rsp+10 --> significand of second argument 
    #  rsp+18 --> exponent of second argument (plus six bytes of junk)
    # So, just 4 pops puts all the arguments into registers:
    pop rax # rax=sig1
    pop rcx # rcx=junk48:exp1
    pop rsi # rsi=sig2
    pop rdx # rdx=junk48:exp2
    # check which argument is greater and swap the two if the second is greater. Otherwise swap them
    sub cx,dx   # find difference of exponents
    jge good    # jmp if exp1 >= exp2
    # exp1 < exp2, switch around the arguments
    xchg rsi,rax
    neg ecx
    # note that dx is unchanged and is now the biggest exponent- this will also be true in the other branch
    # This next two opcodes assemble together to EB 01 CA. If decoded from here, that is "JMP $+1 ; .byte CA", which skips the CA byte
    # But the "jge good" earlier jumps to the middle of EB 01 and so it is instead decoded as "01 CA", or "add edx,ecx"
    .byte 0xEB  
good:
    add edx,ecx
    # dx=cx-dx+dx=original value of cx, the biggest exponent
switched:
    cmp cx,0x40 #ratio >= 2**64, just return initial number
    jge dontxor
    shr rsi,cl  #shift the smaller significand so they line up
    xor rax,rsi
    
    # the significand of a floating point number always has 1 as its MSB.
    # so if it's missing, shift the bits around to restore it
    # i.e. multiply significand by two and subtract one off the exponent until the MSB is 1
    xchg ecx,edx
subnormal_loop: #multiply significand by two and subtract exponent by 1 until either exponent is zero or the number is normal
    js dontxor #done
    shl rax
    loop subnormal_loop #decrements ecx and jmp if not zero
dontxor:
    # return output in long_double[1]
    # put exponent in place
    push rcx
    # put significand in place
    push rax
    # restore the stack pointer
    mov esp,edi
    ret
```
Takes a pointer to an array of [80-bit x87 long doubles](https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format) and returns in the 2nd element of the array. Uses the [x32 ABI](https://en.wikipedia.org/wiki/X32_ABI) (not to be confused with x86-32), which is just like the usual System V ABI, but all pointers are 32 bits.
The padding outside the actual 10-byte long doubles is allowed to be garbage, not zero, which means we have to be careful when comparing or using FLAGS results from operations on exponents: 16-bit operand-size is necessary because we loaded the whole qword including high garbage into the full register.
Because it uses the x32 calling convention, you can compile this and link it with a C program to test it, with the function prototype `void xor_double(long double[2])`. You will need to compile it as x32, though.
The strategy is to shift the smaller number's significand to the right, by the difference in exponent. This aligns the significand to the place-value position of the significand of the larger, like they were fixed-point. (This may mean shifting out all the bits if the exponents are different by 64 or more, but x86 scalar shifts mask the count by &63 or &31 so we need to special-case that).
Then xor the aligned significands and use this as the 64-bit significand with the larger exponent.
80-bit long double has an *explicit* leading 1 in the 64-bit significand, unlike 64-bit double (IEEE binary64) using an implicit 1 (implied by a non-zero exponent field). That means shift/xor Just Works on the significand field directly.
But we still need to renormalize when equal exponents cause the leading 1 bits cancel between significands. Other than subnormals (where the exponent field is zero), `long double` values with bit 63 clear are not valid. (8087 and 80287 allow them as "unnormal" according to Wikipedia, but hardware operations on such a value on 387 and later treat them as invalid, producing NaN. All x86-64 CPUs include 387-compatible FPUs, and we need 64-bit mode to make it convenient to deal with a 64-bit significand.)
The renormalization uses a `loop` command, which decrements the exponent and jumps back to the start of the loop. This is necessary because if the significand is zero, the exponent must also be cleared or the result is an invalid floating point number. This actually clears `rcx` in that case, not just `cx`, which potentially makes the program take (much) longer, but is harmless otherwise.
The renormalization loop also handles the case of two equal inputs, where xoring aligned significands leaves zero. We shift left (and decrement the exponent) until a 1 bit appears at the top of the significand (normal case), or the exponent field becomes zero. (Producing a subnormal or a zero).
This handles the equal-input case because left-shifting an all-zero significand will never shift a 1 to the top, so eventually the exponent becomes zero. If there is a 1 bit somewhere but the exponent becomes zero before it gets to the top, in that case it is a valid subnormal value. (Too small to be representable as a normalized float.) The question doesn't require us to support subnormal inputs or outputs so the actual correct value isn't required in this case, although it probably is correct. This ended up being shorter than `lzcnt` / `shl` plus branching for special cases.
At the end, it returns a `long double*`, which "conveniently" points to `[rsp]` right after the function call. Returning a pointer into the calling function's stack frame is perhaps somewhat malicious compliance with the calling conventions, but it works!
Some general notes on registers:
* I avoid `rbx`, `rbp`, and `rsp` as registers because the System V ABI forbids changing them without saving their previous value, which costs some bytes.
* I avoid `r8-r15` because they cost an extra byte for most uses.
* In most cases using a 32-bit register saves some bytes, so I only use `cx` and `dx` when the junk in the higher bits of the registers would get in the way.
* Using `rax` instead of any other register for one of the significands saves a byte on `xchg`, since `xchg rax,[anything]` is two bytes instead of three.
* `ecx` has many special uses. It is the only register allowed for `shr [reg],cl`, and `rcx` is the only one used by `loop`. That is why the `xchg ecx,edx` is required to put the exponent in this special register.
[Try it online!](https://tio.run/##jVZtj@O2Ef6uXzG1GzTN2j692ZK3vQC5Qz6kaNACybfLYUFLtMxdmRRIaa3tn788JCVL@5JrDvCeTc7M88zwmSGZMfx8qJ/WVVF82QjZ8vrOPMmW9SRVo/lR9MGmqtWB1dQrfVeq7lDzINiUrGWBKZg83h2VPrP2NtiYVgtZ0eKbf/9I@PwmF0GDlfZ4x3Rl/tCO1t/T1fItIxuoUCW/q7ms2tNs89cTJ7tDwtA3gg5PLTd/seZ9X87M3nVGvzsI@Q7LC5Bved9@mdK5DQj/@uJUETfNipeClmTYIyfTsuKBGmULo4nJ0n83JPll2GQttWAhZNO1gQu0pFoB1cc2xDSnPFwfREvHWrHWMnJRSHbnA9dmNXhdRHtysWp1odwnQ5V4tA522YhKiqMoLA37sWsnUZ0ofm3L@0ZJLtsh9EclH7kUWKifVs/pnWyeTBKrEf4MC1JHQPUt53JFRrlwC5frApq4wFs9GKrFw1C3JZE2jf22xkHOSSLQUWjTogRV50LPHG5y7zAyfcP626bujOUy5AeL@04@/H0eJQrfgjW8ULZKb@FG@Svcl@b/B/gXtcJvME1xkI0hnDyOua5dqcYgBpJoFWleCQPxmFsvDjiQRnct7d/3oB1Ny4VbLvr3FizNb0FxtmusKvHXOsXTcumcyrlTPOqwOHEo9HISxWlKDq1Sac5GQZsLaxzx9qJIHL3SfD0myw39B@v6Igy/Opw9iukOVPQr0LCIRwG/UhyPXHNZcFu5sdDGmd9XnCqlSk/w/txYTJsoff@e5tzd2j/dEmSI3nA5qG4Q/rXMwdS8KM4KVXULkqOZi36MJlWLDE9oVhBFXp0sTkxW3HcSFqyybdyDqCqOox1Zr7GK7YvA8bIa7XBAHN3Zhnf2ytaFDpoh4Aj2q/WQmDGupqqxEwoC8aMW7qrizgvy@PEDhRF9/GFDPx2p5NaypKNWZ4IBX3nKiLb418//pb/eRPQP2lhNwmWxGk7WPAiI0JL5@IMT7EDjQ@dH02Ks@YI407UA8n13ti6@uc@iLGt3VJ6NUwW04ICFhHpZeeXGwMUxBrzStGBlSbzsV6j1wuF6emGPWBRYVD8lZnYDPUi26Ndlf4MvSotKSNwxj6zuHBVI6s3zCAKvBT7ELaAg2IZ9GtJSY7oqK6T4u@926dCkmredlkhEtAIIfuhetVgq2eIm8FI@aaehogY/cxJHXz9zRm@jaPMZ4@fiE8ag5NQ1XoOoB@S3Qgj3e8jz5ehGcuztmwACu7AnO5INRbbSAlPk518@bIZIxjWoaP9mcGbGwB2dcaV5sNZjh9jBY1qFq0eMd4DY8A2du7oVTf30jNHhyQnV5dUdWs0KTEVpj@H47DJB17TCTzmwsuqIZt2Hk8XV2QcIIe0lXt/hpmhuafmnMa842IwGMC5cq1y3APo/rpXVnuUxFM41sMX052rGY6UlvvDhcGsah4MlRs95wpIXmvvJjVQcs2E8YXg40CAYwo4X3yAt1bW4AuxEsDfr8Kj4FH0erOzexF9SU7PCc2o6c7IDf2Y4r9Ebtqy/Qvvjdeqav1Lc/lk9jk8Z9xtEv3BZ3jnu43vuzIQM7J/bGcBh0jKHlvlYMc5wyaA1pmfYJy2az1c02zffDhDr6Wnlr8zCXo7@gTd1yn6l95N7vlpHE3XX0bt4WsDkyGaZCdfwYThxBbcxWYd2PrNmYo5SfELtbrzX5@vFZTfCPg4DJ9WvZj5/67rUaRZeILwZquEdercCwGiAc6xckOvcsjA@x3t0G4r3DPD103mq@PI17PJruLPaj@EnT2cxe9y/TGsW7AW3l7ReYtn@sZUNkNvQMyVuHifaRqtKs7N/JeFd/jVyjWh4MBPD3MCdF5hyMzv9shsfEnOHm3TKwrpMOT/XslXb17V80aLlfwzgbIpamcFmTAtpvu4aPcjWy9jP0GsQ3vOiDr5EFAfxJkqj7T7ebZNtvs/2CSUvV96wiTdZlMe5/aTbfZhuA/gFKV9HaATa@v@DaBMnYZxE@ziOdlEUZVG22yfRdpvnURJnu5zf7EMbLN2F6S7J0zSJ4zhJk22S7xLY7wAJTGcWZJttvguzPAuTdJclWb7NYBzF4TYJk12OaGv7g6LNNsriaJulIdDSbJtFaZru4jQK4Z5kqbNLfwc "Assembly (gcc, x64, Linux) – Try It Online") Note that TIO doesn't appear to support the x32 ABI, so I simulated it by using `mmap` to create a new stack at a 32 bit address.
[Answer]
# [C (gcc)](https://gcc.gnu.org/), 223 bytes
```
typedef double D;D f(D x,D y){D z;long m,o,a,c,b,d;x<y?z=x,x=y,y=z:0;m=-1lu>>12;o=1l<<52;a=*(long*)&x;b=*(long*)&y;c=a>>52;d=b>>52;b=__builtin_clzl(a=(a&m|o)^(b&m|o)>>(c-d<53?c-d:53))-11;a=a?a<<b&m|c-b<<52:0;return*(D*)&a;}
```
[Try it online!](https://tio.run/##fVFtb4IwEP6sv6IxmSmukB5QBUvxCz9jmSkvGhIEw2ChOn@7K/g2t2SXtNd77ulzd21ibpPkfG7UPkuzDUqrNi4yFPEIbXCEOhIhZRwjdOBFVW7RjlREkoTEJOVdoFYH0ZFOKKLEYUn5TphQtGEINq8EFEHAbC7FDPdXZ8a04/EjUDwRMgw1IxXx4GOxXsdtXjR5uU6KQ4GlwHK6@6qMdxwPPgxxYqYBc1baLZljGCaAriBXMgh6SmLGfVHdSp01bV3OcKRLSX46f1Z5iprso8HXCTtym1UZ6DgeXYMaCT24TiqDj0f7Oi@bDZ68WMAydNnN8HJ4KyekV1EE1Zp7Go81F@1kXuJBcCgGBNm90BA4FrjAfHvOHOb5C9/Ryd/QnWxbC/Bsr18u86nL/iMDvRlB1II7Ti1K7R6iFObP5j2T6JPB5c6jdchenV4c/iBuZvv@Dy24d3D5AkT125y/AQ "C (gcc) – Try It Online")
(-11 bytes @ceilingcat)
I thought directly manipulating the bit representation would be the shortcut, but it wasn't..
A bit ungolfed,
```
typedef double D;
D f(D x,D y){
  D z;
  long m,o,a,c,b,d;
  x<y?z=x,x=y,y=z:0;
  m=-1lu>>12;
  o=1l<<52;
  a=*(long*)&x;
  b=*(long*)&y;
  c=a>>52;
  d=b>>52;
  b=__builtin_clzl(a=(a&m|o)^(b&m|o)>>(c-d<53?c-d:53))-11;
  a=a?a<<b&m|c-b<<52:0;
  return*(D*)&a;
}
```
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), 45 bytes
```
Ｆ²⊞υＮ≔Ｘ²⁺φ²³η≔⁰ζＷ⌈υ«Ｆ﹪ΣＥυ‹κη²≧⁺ηζ≧﹪ηυ≧∕²η»⭆¹ζ
```
[Try it online!](https://tio.run/##bY7BisIwEIbP9inmOGGrpHEv4knwItil6BNUzTbBtJGmo8suPnuc2MOCeMrk/4bvn6Op@6OvXYzfvgdUAioKBimHTXeh4Yvag@5RiGW2CsE2HVb@xoHKoXIUsJBS5qDmQuRg/pc4@@XfzVinAcv6x7bUIgkBf9nkWVT6EzmPe47L@pL6tjoEPCdNkineZTD6drYxA6ZCxqN68gJH3xPTG7y2V3vSrB3PvGdVb7sB9wM/TTqgSFqxjLGYSf0xlxI@06AWizi9ugc "Charcoal – Try It Online") Link is to verbose version of code. Note: Charcoal requires floating-point numbers to contain a `.`, which is why the link uses `1.0e+300` as the first input. Explanation:
```
Ｆ²⊞υＮ
```
Input the two numbers.
```
≔Ｘ²⁺φ²³η
```
Start at `2¹⁰²³`.
```
≔⁰ζ
```
Start with a zero result.
```
Ｗ⌈υ«
```
Repeat until the inputs are zero.
```
Ｆ﹪ΣＥυ‹κη²
```
If exactly one of the inputs is less than the power of `2`, then...
```
≧⁺ηζ
```
... add that power of `2` to the total.
```
≧﹪ηυ
```
Subtract the power of `2` from those inputs that are not lower.
```
≧∕²η
```
Halve the power of `2`. (Sadly I can't use `Halve` here because it rounds the result for some inexplicable reason.)
```
»⭆¹ζ
```
Format the result as a float. (Charcoal defaults to formatting large floats as an integer.)
[Answer]
# [Swift Language](https://www.swift.org), 113 bytes
```
typealias D=Double;let c=D(bitPattern:1<<32);let x:(D,D)->D={D(bitPattern:($0*c).bitPattern^($1*c).bitPattern)/c}
```
[Try it online!](https://tio.run/##bY/dSsQwEIXvfYpc7EWiaZzJX5N161UewCcQuiVCoKxlN@KK@Ow1VRabxQMJzJczcyan9/SS9Tznjyn2Y@pPJHTh9W0/xocxZjJ0ge5TfupzjsfDFnc7JdnPy3lLAw@seQzdZ@WhG7gdmPgjz3SDNWH3w9c8HdMh0zNFLhm7uVRiSpyUq8EVlKJFJ91ytPGgDSdSoEbjpTXKON96tbIjXMQJiPUgEABygQBoa7lrG1TC3671qhjv1BKB/zAdpffVRLzepRXOOQsejJQIiC42qpiMUA7QWbQlDcsXC15S528)
Based on the 87 byte GCC answer. Unfortunately swift's verbosity gets in the way of brevity when bitcasting
[Answer]
# [Assembly (gcc, x64, Linux)](https://gcc.gnu.org/), custom ABI, ~~39~~ 37 bytes
xxd dump:
```
00000000: 5f58 595e 5a29 d17d 0548 96f7 d9eb 01ca  _XY^Z).}.H......
00000010: e305 48d1 eee2 f948 31f0 87d1 7805 48d1  ..H....H1...x.H.
00000020: e0e2 f957 c3                             ...W.
```
Uses the System V ABI, but returns long doubles in cx:rax, high bits of variables passed on the stack must be zeroed, and `rsp` is decremented by `0x20` per call.
```
xor_double:
    # Custom ABI: like System V but returns long doubles in cx:rax, zeroes out high bits of stack variables, and decreases rsp by 0x20
    # System V passes long doubles on the stack. long doubles are 80-bit floating point numbers,
    # with the low 8 bytes giving the significand and the high 2 bytes giving the exponent
    # Conveniently, long doubles have an alignment of sixteen, so the stack looks like
    #  rsp    --> return address
    #  rsp+8  --> significand of first argument (plus six bytes of zeroes)
    #  rsp+10 --> exponent of first argument
    #  rsp+18 --> significand of second argument (plus six bytes of zeroes)
    #  rsp+20 --> exponent of second argument
    # So, just 5 pops puts all the arguments into registers:
    pop rdi # rdi=return address
    pop rax # rax=sig1
    pop rcx # rcx=0:exp1
    pop rsi # rsi=sig2
    pop rdx # rdx=0:exp2
    # check which argument is greater and swap the two if the second is greater. Otherwise swap them
    sub ecx,edx   # find difference of exponents
    jge good      # jmp if exp1 >= exp2
    # exp1 < exp2, switch around the arguments
    xchg rsi,rax
    neg ecx
    # note that edx is unchanged and is now the biggest exponent- this will also be true in the other branch
    # This next two opcodes assemble together to EB 01 CA. If decoded from here, that is "JMP $+1 ; .byte CA", which skips the CA byte
    # But the "jge good" earlier jumps to the middle of EB 01 and so it is instead decoded as "01 CA", or "add edx,ecx"
    .byte 0xEB  
good:
    add edx,ecx
    # dx=cx-dx+dx=original value of cx, the biggest exponent
switched:
    jrcxz equal
    shr rsi #shift the smaller significand so they line up
    loop switched
equal:
    xor rax,rsi
    # the significand of a floating point number always has 1 as its MSB.
    # so if it's missing, shift the bits around to restore it
    # i.e. multiply significand by two and subtract one off the exponent until the MSB is 1
    
    xchg ecx,edx
subnormal_loop: #multiply significand by two and subtract exponent by 1 until either exponent is zero or the number is normal
    js done #not subnormal
    shl rax
    loop subnormal_loop
done:
    push rdi #push return address
    ret
```
[Try it online!](https://tio.run/##lVZtb9s2EP6uX3FzVqxDbFdvtpVsLdAU/dBhxQa02JeuCGiJkplIpEpKttw/n92Rki0n7tAGcBKTd/c8d/fwSGYMr9blflak6cNcyIaXt2YvG9aBVLXmuei8eVGqNSuhU/o2U@265J43z1jDPJMymd/mSlesufbmptFCFjB59udbwM@/cuLVuNLkt0wX5pt2MHsFB8tzRhQoVRm/Lbksms1o8@OGA@2AMPBMwHrfcPMTmXddNjJ70Rr9Yi3kC1yeIPmGd83DMZ1rzwP8uYA3rWlUBa9v3l1DKe45fNibhlfwD6zbBjRvWi0NlApjOk8DQoJm3bXOuil85VrhkkLbjSg2sBYNfsvBNCy9hy3TgpHPFJjMIOOp5sygvTY1Mge/C/2ByAG3ZoZMTiCVhAbztlHnp1tMc0j8GQJDXirWUPa1wsKCbKs112baA@xEs7FRSrWDxBUOCrElBxtcFFLkIiWm9KE1m1P41JZ3tZJcNkMRldxyKXCh3E9P6W3YlmM4YCWGr9DCVkd0DedyCkYd80I/dW9sE/qwtkz4M0O1uE4AyzLNjRkZXCbOYEwfIXKhTYPFKVoL@rwuW0O4fS5o4Vr36zhU4NtQQ3ZP45wYJ@dwDU8VFfDHgMOnwI8CDSpRU7hDycICm1wbqFvUGytLW8bBliTaKCxZIVBT2vRiRwfQmcAo@PvlmYJaAxwDF/T7JSYWHJdTu5x2L/1rZDnaMDagEWQfjnCsfdbbh4PK0w3HTu82It0ca4QnucCDgVSt8MyO1TafZqdA5E4hrhpHyzn8het6Jww/OFQOxbRr4Gk35ciBMHNBZ0/kOddcppyKOxTa5X1XcCiUygAcybuqJlzKE169hDF/u/a7XUL14pGyeai2Py@HDjj7Lt0UVJsp1tMuSF4QtSGaVA1muWENEFdMrpXphsmCuxOICxLPKgVei6Lg2PaB@AxXcXsnsPWsxGO0xkC65TScyF5RcWCtGQYc0D6Sh8Q5aAurapqiKB53HaC7Krj1Qum8vQE/gDev5/Aup7GFlhnkGgclGvCp44zRJn@8/xt@vgzgN5iTvtFlMu3ba@4FCpTIvHltxd/TuMFZSauToewT4EyXApHv2opc3FCoRJaVtluOjZUGCsICC4nKZtmBG0MuljHCKw0TlDXVdIrFnlhcR8/vMBZ4hHptl0d2PT2UbNrNsu4S/1FaFELiPbhlZWupoKzO9sPznBh4H/cOj8pX4F9aVjpNbrQ7KmYjcpe/qfDcYtLjAeLm4R6HoOTQ1tYVx2INQ3TPhnQYeJvRQZ1i3KHFj@c4MmbnrwVUzY7taT4bCKh8dHG9/3Az7yMZe/RE84vBRhiD7qj3A3d7zQ26p0mDlyjeQ2IYU2LO51C1ZSPqcn/CCG89Up9Ntl03mqU47STVNj@5WfAoNMKNNWRFLQ9ckncG7xbZUPIXeH4oiqQHROkd/rulkl3DxXcTOIDiZtAjc2EPw2ELGdDgJnURqb6K9ohadOKGaiQ1OZ5fB569AEoYpsAdvihOyXpeb@s6W7dm40a1@@/pqMalBy6zW@syvNYqJqRHv3ptY4p4uUxn9plBK1vKAFV3kA9H@fCeVckZYU5Hz65PWtSf7WaltnaQPe9BZ8enlLvIUrqE3IPuMIQJ3GLbfpwF3WLg7AuDT2j7ebqvKv@Ey/i1acnAcRvZWK@RQ2dXLv0u8D8fWdkg7isOdkIP@zag3GR2Avj08XqswY@hjmoxBrJ7o2f1qN3dd30Zd@sMmUMKj9k/IYW1sNJDZq43FxmOXyvuWqtCs8o9I/ABfRL/iOhCipp7B43wEwPbbSSJn6ND1g7X6djhMj5mQS7HEp2KkIb1/4twp0XDvw1gbdJSmd5mSAvTfCp33DgmktknxTEI73haeg8BhF44D@JgcRUuF9EiuVpdRRA9XjljE85XQRIm9IkXV3688NDPi/ks8H0fFu6vF8zDyA@j4CoMg2UQBKtgtbyKgsUiSYIoXC0TfnnlU7B46cfLKInjKAzDKI4WUbKM0H6JkIhpzbzVfJEs/VWy8qN4uYpWyWKFxkHoLyI/WiYYbUZfIJgvglUYLFaxj2jxarEK4jhehnHgo3u0iq1d/B8 "Assembly (gcc, x64, Linux) – Try It Online")
The overall strategy is similar to my [other answer](https://codegolf.stackexchange.com/a/247235/58834), and most of @PeterCordes's very helpful contribution still applies. This answer saves:
* two bytes by avoiding otherwise useless pushes at the end (and thus decrements `rsp` by two). This also makes a loop strategy `jrcxz; shr rsi; loop` shorter than `cmp cx,0x40; jge; shr rsi,cl`. I can't use that in the first answer because it depends on `rcx`, which is full of junk.
* one byte every time `ecx` or `edx` is used instead of `cx` (which can only be done by insuring that the higher bits are set to zero in the function call).
* five bytes by returning in `rax:rdx` instead of the x87 stack, which removes the need to push the result back on the stack and use `fld tbyte ptr [rsp]` to load it into the x87 stack.
[Answer]
# [Vyxal](https://github.com/Vyxal/Vyxal) `ḋ`, 10 bytes
```
k⁋E*÷꘍k⁋E/
```
[Try it Online!](https://vyxal.pythonanywhere.com/#WyLhuIsiLCIiLCJr4oGLRSrDt+qYjWvigYtFLyIsIiIsIlszLjE0MTU5MjY1MzU4OTc5MywwLjIwMDAwMDAwMDAwMDAwMF0iXQ==) or [Verify all test cases!](https://vyxal.pythonanywhere.com/#WyLhuItqIiwixpsiLCJr4oGLRSrDt+qYjWvigYtFLyIsIjtaxpvDt8O44biLJHbDuOG4i2AsIGBqw7hCJFwiYCA9PiBgaiIsIltbMSwyXSxbMy4xNDE1OTI2NTM1ODk3OTMsMi4xNDE1OTI2NTM1ODk3OTNdLFsyLjcxODI4MTgyODQ1OTA0NSwyLjE0MTU5MjY1MzU4OTc5M10sWzEwMDAwMDAwMDAsMC4xXSxbMC4wMDIsMC4wMDE2NjY2NjY2NjY2NjY2NjY4XSxbMC4wMDIwMDAwMDAwMDAwMDAwMDEsMC4wMDJdLFszLjE0MTU5MjY1MzU4OTc5MywwLjIwMDAwMDAwMDAwMDAwMF0sWzFlKzMwMCwxXSxbMWUrMzAwLDRlKzI5OV0sWzAuMSwwLjFdXSJd)
For the `0.002000000000000001 ^ 0.002` test case, it unfortunately fails because it seems to automatically round it to `0.002`. It does work with three zeros removed, though. This seems to be a problem with `sympy.nsimplify`:
```
>>> sympy.nsimplify(0.002000000000000001, rational=True)
1/500
>>> sympy.nsimplify(0.002000000000001, rational=True)
2000000000001/1000000000000000
```
(from Python REPL)
## How?
```
k⁋E*÷꘍k⁋E/
k⁋         # Push 1024
  E        # Push 2**1024
   *       # Multiply the (implicit) input pair by that
    ÷      # Push both elements of the multiplied pair to the stack
     ꘍     # Bitwise XOR them
      k⁋E/ # Divide by 2**1024
```
[Answer]
# [Julia 1.0](http://julialang.org/), 51 bytes
```
f(a,b,e=8^341.)=e>0&&(a÷e+b÷e)%2*e+f(a%e,b%e,e/2)
```
[Try it online!](https://tio.run/##dZDfSvNAEMXv8xRjwZJ8TdaZ2f9CeulTSCFtt5CPEEtawQvfywfwweok0YqIAxvYM7@ZPSf/n7u2oZfL5ZA35bZMddhoQ6qo0xqXy7x5f0urrXyKW/6XVgLdpnIrJ91xcTk8DdBC28OQmn3X9umUFxlIjat2UMOxGU5J5Q/dU3N2poTTsWvPeVvCsHjcvFbrRTHzg7CTgfl6HNr@3PX5pEC9huELu6lhB8vllVjs28PhHhaysdoVWer3F4INMFRr0JlWZMhGdlbbEH3UY@uXJihlrDwFDuMxNqKxf6GorPcOo/esDTN6nRF@lQyhomnjVVOUoULkqYdI7meFeSmidtoIzvK6vGbJzmP4o@hzyxQwKOe1I699DMGgtjZVFDNKKz1ZmY1Mt2/RJI5xaij2Go1nCcI@OA4zOQa4xsDMqxCC5EXLTEgUUqVHwCodkIIjJ25IfpPon64MO8deojExazsOfAA "Julia 1.0 – Try It Online")
port of [m90's answer](https://codegolf.stackexchange.com/a/247204/98541)
I tried so many things but always landed on 51 bytes
[Answer]
# C (gcc), 87 bytes
```
typedef double d;d c=1LL<<32;d x(d a,d b){return((long long)(a*c)^(long long)(b*c))/c;}
```
In the spirit of the Python answer above, converts to a fixed point integer and does the XOR, before converting back. It only has 32 bits of precision to either side of the decimal point, assuming `long long` is 64 bits wide, which is exactly the worst precision allowed by the challenge (1e-6). Because of this, the 1e300 answers overflow.
If your platform has 64-bit `long`s 11 bytes can be saved, for a total of 76 bytes:
```
typedef double d;d c=1L<<32;d x(d a,d b){return((long)(a*c)^(long)(b*c))/c;}
```
[Answer]
# [Stax](https://github.com/tomtheisen/stax), 25 [bytes](https://github.com/tomtheisen/stax/blob/master/docs/packed.md#packed-stax)
```
∩√=êⁿ;r⌂$══▓ìêCO¥ÇnµtƒÉd+
```
[Run and debug it](https://staxlang.xyz/#p=effb3d88fc3b727f24cdcdb28d88434f9d806ee6749f90642b&i=1.0+2.0%0A3.141592653589793+2.141592653589793%0A2.718281828459045+2.141592653589793%0A1000000000.0+0.1%0A0.002+0.0016666666666666668%0A0.002000000000000001+0.002%0A1e300+1.0%0A1e300+4e299%0A0.1+0.1%0A7.888609052210118e-31+5.380186160021159e-32&a=1&m=2)
[Answer]
# [C (gcc)](https://gcc.gnu.org/), 96 bytes
```
#define D double
#define P*(long*)
D z;D f(D x,D y){P&z=P&x^P&y;z=x>=y&x<2e-308?z:f(y/2,x/2)*2;}
```
[Try it online!](https://tio.run/##fZDhToMwFIV/r09xMyMpWFhbYKMi8w8PwAMYE4WykMxikBlg2bNjGRuKJt6kTe/p6Xdumtq7NO37m0zmhZIQQ1YeXvcSXYXEwvtS7SwTxdCFMeQ4hobE0JrHxOiixGieE6MNu6jZRq3RPHBpuzR47O5z3K44aVbctHh46j/LIoNaftR4DNCQS5RGwREtLk0Fkc7Ql60ZosV7Vag6x8tbh/kSxt3ejocntSQDpSVQae8JIe2Ft5dC4TPwHMYI8AF0blyHecwXfO27fiA2wtWXv6XJzJ0NC3gwLM8X1PP/MzN6LQLUYZNOHUr5IFHK1vMK5iY6Kza@@R6dyTt3gLM/iie5ED9YbJqgkvWhUkD13/Rf "C (gcc) – Try It Online")
subnormal number is good
# [JavaScript (Node.js)](https://nodejs.org), 81 bytes
```
f=(x,y)=>x+y>1e60?+[g(x)^g(y)]:f(x*2,y*2)/2
g=x=>BigInt(x%1?0:'0o'+x.toString(8))
```
[Try it online!](https://tio.run/##fY5NT8MwDIbP66/IBS1ZS7DTjzVD7SRunDlOIE0ljYqmBK0BpUL89tJ2MChIvJIPfvLY8dP@dd9Wx@bZXRr7qPq@LqiPOlaUPuxKVBlsw52mnj1o2rH7TU39SkTdSrArEejCF@VNo2@No/4Ct7BZgl2Gnjt7546N0TRnrK9fTOUaa4hT7eBFpGPkLVhU1rT2oPjB6glGpJ5@ZtfBexAsJhkjIob@1MQcE0ylyNI4zeVaxsPjb3SWBV9jLvKxklRCkv4nI3wlIsDxzIEDiBEBYDZPPpdgFjzNfJ@OKozH5fiHJEpI@WMXfl7QfwA "JavaScript (Node.js) – Try It Online")
Base 8 to avoid `e+66`, `>1e60` to avoid overflow
]