На самом деле это не шутка, что программирование стало вопросом копирования и вставки из различных источников. Да, это может сэкономить время, но когда вы просто копируете и вставляете, откуда вы знаете, что код работает? Даже высоко оцененный ответ SO мог иметь ошибку и имел ее.
Эта история появилась в нескольких местах с чувством возмущения тем, что фрагмент из столь немногих строк Java может быть так повторно использован и в то же время так испорчен. Как мог любой приличный программист предложить решение с таким серьезным и вопиющим недостатком? Как мог любой программист скопировать и вставить указанное решение, не проверив его и не заметив вопиющей ошибки? Я думал, что под взглядом тысяч глаз каждая ошибка должна быть мелкой, заметной и исправимой. О чем только думали пользователи!
И поэтому возмущение может продолжаться, но правда более интересна. Фактическая ошибка была тонкой, хотя и предсказуемой, когда я упомянул, что речь шла об арифметике с плавающей запятой. Большая проблема с использованием плавающей запятой заключается в том, что мы все просто предполагаем, что это сработает, хотя нам следовало бы знать лучше, и нам тысячу раз говорили быть осторожными. Давайте рассмотрим эту проблему более подробно.
Все это выяснилось, когда в академической статье, посвященной повторному использованию кода, было объявлено, что фрагмент кода на Java, который печатал количество байтов в «разумных» единицах, был наиболее скопирован и был встроен в более чем 6000 повторов GitHub. Это заставило автора, Андреаса Лундблада, еще раз взглянуть на нее, почти через десять лет после того, как он ее написал, и он обнаружил, что она была глючной.
Проблема заключается в том, чтобы напечатать значение, используя подходящие единицы измерения в качестве значения от 1 до 999,9, за которым следует единица измерения. Например, 999,999 должно быть напечатано как 1,0 М. Обратите внимание, что в этой спецификации печать его как 1000K неприемлема, и это немного сложнее достичь.
В предлагаемом решении использовался довольно простой подход. Если у вас есть значение и вы хотите масштабировать его в единицах K, M, G, T и т. Д., То все, что вам нужно, это
log(значение)/log(1000)
или
exp=log(значение)/3
В конце концов, журнал-это значение, на которое вы должны поднять 10, чтобы получить значение. Так:
log(123456789)/3 = 2,697..
и взятие целого значения дает в результате 2.
Это означает, что единицы измерения должны быть одинаковыми, а значение, записанное с одним десятичным знаком, равно:
123,4 М
Вы можете использовать:
exp=int(log(value)/3)
в качестве индекса в строковый массив для множителя и значения в выбранных единицах измерения просто:
значение/(1000^exp)
где ^ — это повышение до оператора мощности на языке, который вы используете.
Пока все хорошо, и это похоже на ответ из учебника. Что может пойти не так?
Серьезно, многие программисты просто приняли бы истину математики. Математика верна, значит, программа верна. К сожалению, математика верна только в том случае, если арифметика выполняется с идеальной точностью, а плавающая точка далека от совершенства. Первая проблема заключается в том, что 999,999 отображается как 1000,0 КБ. Причина этого в том, что log(999999)/3 равен 1.9999.. который при усечении до int равен 1, поэтому единица измерения равна КБ, а 999999/1000-999,999, что округляется до 1000,0 КБ. Как уже говорилось, правильный результат-1,0 М.
Вы можете видеть, что проблема заключается во взаимодействии между вычислением показателя в виде int и округлением, применяемым к масштабируемому значению. Предлагаемое исправление состоит в том, чтобы округлить показатель степени вверх, когда значение будет округлено до 1000:
если(значение >= (1000^exp)*(1000-0.05)) exp++:
Другое возможное решение состоит в том, чтобы проверить, что лог предлагаемого округленного значения меньше 3, и увеличить exp, если это так.
Есть и другие проблемы с вычислением, но все они одного и того же тонкого рода. Проблемы сводятся к тому, что длинный int имеет больше цифр точности, чем двойной. Вы можете прочитать сообщение в блоге автора, чтобы изучить их.
Все это доказывает, что с плавающей запятой и компьютерная математика в целом сложны, и такого рода ошибки очень легко сделать.
Является ли это серьезной ошибкой? Не совсем, учитывая, что человек не будет введен в заблуждение представленным результатом. Однако представьте, что данные передаются на какое — то промышленное устройство-оно вполне может выйти из строя. Было бы неплохо провести тест, чтобы убедиться, что данные находятся в правильном формате.
То, чем это не является, — это огромная неспособность сообщества программистов обнаружить вопиющую ошибку. Что это доказывает, так это то, что мы слишком охотно принимаем код, который, кажется, работает из переполнения стека, не тестируя его.