Standardise a Phinary Number
Javascript (ES6) - 446 418 422 420 bytes

Minified:

F=s=&gt;{D=[];z='000000000';N=t=n=i=e=0;s=(z+s.replace(/^([^.]*)$/,'$1.')+z).replace(/~/g,'-').replace(/-?\d/g,s=&gt;((D[n++]=s/1),0));for(;i&lt;n-3;i=j){if(p=D[j=i+1]){if(!e&amp;&amp;p&lt;0){D=D.map(k=&gt;-k);N=~N;p=-p}e=1}d=D[i];x=D[i+2];m=D[i+3];if(p&lt;0){d--;p++;x++;e=j=0}if(p&gt;1){d++;m++;p-=2;e=j=0}if(!d&amp;&amp;p*x==1){d=p;e=j=p=x=0}D[i]=d;D[i+1]=p;D[i+2]=x;D[i+3]=m}return(N?'-':'')+s.replace(/0/g,()=&gt;D[t++]).replace(/^(0(?!\.))+|0+$/g,'')}


Expanded:

F = s =&gt; {
    D = [];
    z = '000000000';
    N = t = n = i = e = 0;
    s = (z + s.replace( /^([^.]*)$/, '$1.' ) + z).replace( /~/g, '-' ).
        replace( /-?\d/g, s =&gt; ((D[n++]=s/1),0) );

    for( ; i &lt; n-3; i = j ) {
        if( p = D[j = i+1] ) {
            if( !e &amp;&amp; p &lt; 0 ) {
                D = D.map( k=&gt;-k );
                N = ~N;
                p = -p;
            }
            e = 1;
        }
        d = D[i];
        x = D[i+2];
        m = D[i+3];

        if( p &lt; 0 ) {
            d--;
            p++;
            x++;
            e = j = 0;
        }
        if( p &gt; 1 ) {
            d++;
            m++;
            p-=2;
            e = j = 0;
        }
        if( !d &amp;&amp; p*x == 1 ) {
            d = p;
            e = j = p = x = 0;
        }

        D[i] = d;
        D[i+1] = p;
        D[i+2] = x;
        D[i+3] = m;
    }

    return (N ? '-' : '') + s.replace( /0/g, ()=&gt;D[t++] ).replace( /^(0(?!\.))+|0+$/g, '' );
}


The code produces a function F that performs the specified conversion.

It's a tough problem to golf. Numerous edge cases creep up that prevent simplification of the code. In particular, dealing with negatives is a pain, both in terms of parsing and in terms of logical handling.

I should note that the code only handles a "reasonable range" of inputs. To extend the domain of the function without bound, the number of zeros in z can be increased, and the constant bounding the while( c++ &lt; 99 ) loop can be increased. The range currently supported is already overkill for the supplied test cases.

Sample Outputs

F('1')          1.
F('9')          10010.0101
F('1~3.2~1')    -0.0101
F('0.~1021')    -0.
F('105.~2')     1010.0101
F('~31~5.~1')   -100000.1001


The -0. isn't pretty, but the answer is still correct. I can fix it if necessary.