Почему JS самостоятельно изменяет числа?
console.log(Number.MAX_SAFE_INTEGER < 9999999999999999); // true
Используйте BigInt
console.log(9999999999999999n); // 9999999999999999n
Условно происходит округление.

На пальцах это выглядит примерно так. В памяти есть место для хранения целого числа, а также есть место для его порядка (степени). Размер первого места задаёт, сколько цифр может быть в числе, например 17 (но это примерно, потому что там биты, то есть число хранится в двоичной системе). Порядок же позволяет дописать очень много нулей к числу справа или слева. Количество нулей тоже не бесконечно, но очень большое, всем хватит.

Поэтому для очень широких чисел типа такого:
1230000000000000099999999999999999999999999999999
невозможно сохранить все цифры в точности. Оно просто не влезает в целую часть. Приходится брать за основу какой-то отрезок из 17 цифр. Очевидно, что логичнее начинать со старших разрядов:
12300000000000000 (17 цифр)
Ну а дальше встаёт вопрос, что делать с младшими? Порядок (степень) можно примерно сохранить, дописав нули. И в случае, если отбрасываем .....999999, то логичнее перевести единичку в следующий разряд. Получится:
1230000000000000100000000000000000000000000000000
(т.е. 1.2300000000000001e+48)

Повторюсь, это примерно, без подробностей, для общего понимания. На самом деле там куча нюансов.
0.1 + 0.2 == 0.3 //false

Чтобы не вникать в детали и при этом не иметь проблем с вещественными числами вообще, нужно не превышать разрядность целочисленной части и не иметь дело с дробными (т.е. пользоваться, как целыми). И как только эти правила нарушены, гарантия исчезает - и могут появиться погрешности.