Откуда берутся сотые доли при умножении на дробное число?
Такое не только в js, но и в подавляющем большинстве других языков (см. https://0.30000000000000004.com )
Потому что процессоры не могут представлять дробные числа с бесконечной точностью. Про это есть стандарт IEEE 754, и это в принципе нормально (или просто все давно привыкли). Можно повышать точность вычислений, но тогда они будут длиться дольше, и можно округлять результаты.
Из-за представления чисел с плавающей запятой в памяти компьютера, как впрочем уже сказали выше.
Если нужны вычисления с заданной точностью - используйте тип Decimal>>> from decimal import *
>>> getcontext().prec = 3
>>> Decimal('48') * Decimal('0.2')
Decimal('9.6')
>>> a = Decimal('48') * Decimal('0.2')
>>> print(a)
Decimal('9.6')