GNU sed, 236 bytes
/^0/bV
:
s/\b9/;8/
s/\b8/;7/
s/\b7/;6/
s/\b6/;5/
s/\b5/;4/
s/\b4/;3/
s/\b3/;2/
s/\b2/;1/
s/\b1/;0/
s/\b0//
/[^;-]/s/;/&&&&&&&&&&/g
t
y/;/1/
:V
s/111/3/g
s/3\b/3:/
s/311/33!/
s/31/3+/
y/3/1/
tV
s/1/+/
y/1:/!0/
/-/{s/-//
y/+!/!+/
}
y/!/-/
Try it online!
Explanation
The first half of the code (less the first line) translates decimal to unary and comes straight from "Tips for golfing in sed." Then it translates unary to balanced ternary one trit at a time, which I'll demonstrate by working an example manually.
Before the final output, the ternary digits -
, 0
, and +
are represented by !
, :
, and +
, respectively.
For an interesting result, we start with -48
, which has been converted to unary (with the -
intact). To calculate the first (right-most) trit, we have to calculate the remainder of 48÷3. We can do this by replacing the 111
s with 3
s:
-111111111111111111111111111111111111111111111111 │ s/111/3/g
# => -3333333333333333
48÷3 has no remainder, so no 1
s remain, and we know our first trit is :
(for 0), so we replace it:
-3333333333333333 │ s/3\b/3:/
# => -3333333333333333:
Now we have our "ones place," so we know the remaining 3
s represent the threes place. To keep the math working we have to divide them by 3, i.e. replace them with 1
s:
-3333333333333333: │ y/3/1/
# => -1111111111111111:
Let's double-check our math: We have 16 (unary 1111111111111111
) in the threes place and zero (:
) in the ones place. That's 3✕16 + 1✕0 = 48. So far so good.
Now we start again. Replace 111
s with 3
s:
-1111111111111111: │ s/111/3/g
# => -333331:
This time our remainder is 1
, so we put +
in the threes place and replace the remaining 3
s with 1
s:
-333331: │ s/31/3+/; y/3/1/
# => -11111+:
Sanity check time: We have a 5 (unary 11111
) in the nines place, 1 (+
) in the threes place, and 0 (:
) in the ones place: 9✕5 + 3✕1 + 1✕0 = 48. Great! Again we replace the 111
s with 3
s:
-11111+: │ s/111/3/g
# => -311+:
This time our remainder is 2 (11
). That takes up two trits (+!
), which means we have a carry. Just like in decimal arithmetic that means we take the rightmost digit and add the rest to the column to the left. In our system, that means we put !
in the nines place and add another three to its left, then replace all of the 3
s with 1
s to represent the 27s place:
-311+: │ s/311/33!/; y/3/1/
# => -11!+:
Now we don't have any 3s left, so we can replace any remaining unary digits with their corresponding trits. Two (11
) is +!
:
-11!+: │ s/11/+!/
# => -+!!+:
In the actual code this is done in two steps, s/1/+/
and y/1:/!0/
, to save bytes. The second step also replaces :
s with 0
s, so it actually does this:
-11!+: │ s/1/+/; y/1:/+0/
# => -+!!+0
Now we check if we have a negative number. We do, so we have to get rid of the sign and then invert each trit:
-+!!+0 │ /-/ { s/-//; y/+!/!+/; }
# => !++!0
Finally, we replace !
s with -
s:
!++!0 │ y/!/-/
# => -++-0
That's it!