Sure it is as you already know you will need a custom formatter … and it isn't rocket science:
I use an unconnected function node as editor:
var numFormatter = function (cell, formatterParams, onRendered) {
let result = Number(cell.getValue());
if (Number.isNaN(result)) return cell.getValue();
result = result.toFixed(formatterParams.precision);
result += (formatterParams.unit) ? formatterParams.unit : '';
return result;
}
some security checks here + flexible precision and the ability to add a unit symbol via formatter params to make it reusable
and then pass this to your table (assume you configured it in the ui-table config UI)
{
"tabulator": {
"columns": [
{
"field": "put your field property as configured in ui-tabel here",
"formatterParams": {
"precision": 2,
"unit": "°C"
},
"formatter": "function (cell, formatterParams, onRendered) { let result = Number(cell.getValue()); if (Number.isNaN(result)) return cell.getValue(); result = result.toFixed(formatterParams.precision); result += (formatterParams.unit) ? formatterParams.unit : ''; return result; }"
}
]
}
}
The visual Editor helps you pasting your function in the JSON (simply copy paste). The Editor does all the escaping of special characters like "
and CR
for you

CTRL+C and then

CTRL+A CTRL+V
and you are done
[{"id":"981dd4a03d7a0058","type":"inject","z":"d2952ff54c89c139","name":"ui_control","props":[{"p":"ui_control","v":"{\"tabulator\":{\"columns\":[{\"field\":\"putt your field id here\",\"formatterParams\":{\"precision\":2,\"unit\":\"°C\"},\"formatter\":\"function (cell, formatterParams, onRendered) { let result = Number(cell.getValue()); if (Number.isNaN(result)) return cell.getValue(); result = result.toFixed(formatterParams.precision); result += (formatterParams.unit) ? formatterParams.unit : ''; return result; }\"}]}}","vt":"json"},{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payloadType":"str","x":580,"y":340,"wires":[[]]},{"id":"bd294316568c3750","type":"function","z":"d2952ff54c89c139","name":"callback functions editor","func":"var numFormatter = function (cell, formatterParams, onRendered) {\n let result = Number(cell.getValue());\n if (Number.isNaN(result)) return cell.getValue();\n result = result.toFixed(formatterParams.precision);\n result += (formatterParams.unit) ? formatterParams.unit : '';\n return result;\n}","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":630,"y":380,"wires":[]}]
and you can do so much more like controlling the background or text color. (examples are here in the forum all over the place).

and the sum row then works too "bottomCalc": "sum"