Tagged: float

Корозия Миталлляя

Корозия точности

Корозия Миталлляя

По оценкам специалистов различных стран эти потери в промышленно развитых странах составляют от 2 до 4 % валового национального продукта. При этом потери металла, включающие массу вышедших из строя металлических конструкций, изделий, оборудования, составляют от 10 до 20 % годового производства стали.

В компьютерных вычислениях тоже есть своя коррозия — «коррозия» точности при операциями над числами с плавающей точнкой. Об этом знают все, но всё равно обжигались:

Операции над двоичными числами (а в компьютере все они двоичные) вызывают много сюрпризов которые кажутся странными людям привыкшим к десятичной системе счисления:

не все десятичные числа имеют двоичное представление с плавающей запятой. Например, число «0,2» будет представлено как «0,200000003». Соответственно, «0,2 + 0,2 ≈ 0,4». Абсолютная погрешность в отдельном случае может и не высока, но если использовать такую константу в цикле, можем получить накопленную погрешность.

Поэтому когда считают что-то важное (например бабло) то используют хитрые классы обвёртки, типа BigDecimal, потому факап с примитивными типами неизбежен. Потому что есть ещё чудеса с переполнением, а в Си ещё и со знаком и эндингом. Естественно, скорости и удобства это не прибавляет. Хотя в Groovy с этим проще.
Кстати, если в контексте денег, то попробуйте новый JSR 354 — Currency and Money или на крайний случай его предшественник Joda Money. Ну и Фаулера почитайте для матчасти.

Так вот, в годы своей юности я написал калькулятор на Delphi, внутри которого был прикольный класс TFractNumber который мог считать без потери точности. Вообще. И никаких проблем как тут у него не было. И когда я через него, например, делил один на три а потом умножал на три то получал снова один а не 0,999999999…
Как такое возможно?
Я хранил число в виде обыкновенной дроби с числительным и знаменателем, все операции приводил к единому знаменателю, и сокращал дробь. Как в школе, помните?

Т.е. вместо десятичной дроби (а в случае процессора двоичной), в знаменателе которой может быть только десять в определённой степени, у нас мы может использовать любое число.

Так вот, такая запись позволяет записывать периодическую дробь и работать над нею как над целым числом.
Т.е. обычно в записи когда пишут 0.0909090909 можно отобразить через период 0,(09), и если привести к обычной дроби, то мы получим 1/11.

Правда с иррациональными числами проблемы так и останутся. Радует что на практике они не так часто попадаются.
Из недостатка у этого способа представления чисел то что оно требует больше места. Но я думаю, это не очень страшно в современном то мире — всё равно почти везде уже используются double и long вместо float.
А ещё, я думаю что любой студент сможет написать диплом с реализацией сопроцессора для обыкновенных дробей, чтобы увеличить производительность.

Это был кстати вообще один из первых классов в моей жизни с которого я начал погружение в ООП. Блин, да мне было лет пятнадцать.
Почему этого до сих пор не сделано в компьютерах, когда ракеты продолжают падать а банки терять деньги, я не так и не понял 😦