Я хочу использовать dc
для обработки некоторых базовых 16 чисел с шестнадцатеричными точками, но я сталкиваюсь с проблемами точности. Например, ниже я умножаю F423F.FD
на 100
оба гекса. Ожидаемый ответ F423FFD
, вместо этого, дает F423FFA.E1
близкий, но не достаточно точный, даже после округления.
$ dc
16 d i o F423F.FD 100 * p
F423FFA.E1
Я читал, что это dc
был калькулятор с неограниченной точностью, и это совсем немного. Я что-то не так делаю?
Спасибо за ваши ответы. Учитывая проблемы с dc
, я укусил пулю и написал свой собственный парсер для действительных чисел в других базах. Если кто-то заинтересован в коде, я могу опубликовать его здесь.
dc
можно использовать, а затем просто написать парсер напрямую! (Входные данные могут иметь или не иметь десятичную дробь, и могут быть в других базах, поэтому величина заполнения варьируется.)dc
отвечали: для правильной обработки недесятичных дробных цифр потребуется модель, совершенно отличная от модели десятичного масштаба, используемой dc и bc (как продиктовано POSIX для bc и исторической традицией для обоих). , так что технически это может быть исправленоdc
, но это, вероятно, сломаетсяbc
, так что классифицируется как WONTFIX.Выражается как десятичное число (используется
dc
для преобразования), это соответствует 999999,98 (округлено в меньшую сторону) × 256, то есть 255999994,88, что является F423FFA.E1 в шестнадцатеричном формате.Таким образом, разница возникает из
dc
-за поведения округления: вместо вычисления 256 × (999999 + 253 ÷ 256), которое даст 255999997, оно округляет 253 ÷ 256 и умножает результат.dc
это калькулятор произвольной точности, что означает, что он может рассчитывать с любой точностью, которую вы хотите, но вы должны сказать, что это такое. По умолчанию его точность равна 0, что означает, что деление дает только целочисленные значения, а умножение использует количество цифр во входных данных. Чтобы установить точность, используйтеk
(и имейте в виду, что точность всегда выражается в десятичных цифрах, независимо от входного или выходного радиуса):(Точность 8 цифр будет достаточно, поскольку это то, что вам нужно представить в 1 ÷ 256 в десятичном виде.)
источник
k
установлено:10 k 16 d i o F423F.FD p
→F423F.FA
, поэтому мне придется масштабировать все числа, прежде чем использовать их вdc
. По сути, это равнозначно предварительному разбору.dc
масштабирует свой ввод, используя только количество цифр, что мне кажется ошибкой (так как количество цифр рассчитывается с использованием входного радиуса, но применяется к десятичному значению).bc
которогоdc
он основан): «Внутренние вычисления должны выполняться как в десятичном виде, независимо от входных и выходных оснований, до указанного числа десятичных цифр».20 k 16 d i o 0.3 1 / p
(который печатает .19999999999999999). Поймите, что операция просто делится0.2
на1
(что в теории не должно изменять значение). Пока20 k 16 d i o 0.3000 1 / p
(правильно) печатает.30000000000000000
. (Продолжение)Проблема
Проблема в том, как dc (и bc) понимают числовые константы.
Например, значение (в шестнадцатеричном формате)
0.3
(разделенное на 1) преобразуется в значение, близкое к0.2
Фактически, простая константа
0.3
также изменяется:Кажется, что это странно, но это не так (подробнее позже).
Добавление большего числа нулей приводит к правильному значению ответа:
Последнее значение является точным и останется точным независимо от того, как можно добавить больше нулей.
Проблема также присутствует в BC:
Одна цифра на бит?
Весьма неинтуитивный факт для чисел с плавающей запятой состоит в том, что количество требуемых цифр (после точки) равно количеству двоичных битов (также после точки). Двоичное число 0,101 точно равно 0,625 в десятичном виде. Двоичное число 0.0001110001 (точно) равно
0.1103515625
(десятичным десятичным цифрам)Кроме того, для числа с плавающей запятой типа 2 ^ (- 10), которое в двоичном коде имеет только один (установленный) бит:
Имеет такое же количество двоичных цифр
.0000000001
(10), что и десятичные цифры.0009765625
(10). Это может быть не так в других базах, но база 10 является внутренним представлением чисел в dc и bc и, следовательно, является единственной базой, о которой нам действительно нужно заботиться.Математическое доказательство находится в конце этого ответа.
шкала до н.э.
Количество цифр после точки можно посчитать с помощью встроенной функции
scale()
вида bc:Как показано, 2 цифры недостаточно для представления константы
0.FD
.И, кроме того, просто подсчет количества символов, используемых после точки, является очень неправильным способом сообщить (и использовать) масштаб числа. Шкала числа (в любой базе) должна вычислять количество необходимых битов.
Двоичные цифры в шестнадцатеричном формате.
Как известно, каждая шестнадцатеричная цифра использует 4 бита. Поэтому каждая шестнадцатеричная цифра после десятичной точки требует 4 двоичных разряда, что из-за (нечетного) факта выше также требует 4 десятичных разряда.
Следовательно, для подобного числа
0.FD
потребуется 8 десятичных цифр, которые будут представлены правильно:Добавить нули
Математика проста (для шестнадцатеричных чисел):
h
) после точки.h
на 4.h×4 - h = h × (4-1) = h × 3 = 3×h
нули.В шелл-коде (для sh):
Который напечатает (правильно как в dc, так и в bc):
Внутренне bc (или dc) может привести к тому, что требуемое количество цифр будет соответствовать числу, вычисленному выше (
3*h
) для преобразования шестнадцатеричных чисел во внутреннее десятичное представление. Или какая-то другая функция для других баз (при условии, что число цифр конечно относительно базы 10 (внутренней от bc и dc) в такой другой базе). Как 2 я (2,4,8,16, ...) и 5,10.POSIX
В спецификации posix говорится, что (для bc, на котором основан dc):
Но «... указанное количество десятичных цифр». можно понимать как «… необходимое количество десятичных цифр для представления числовой константы» (как описано выше) без влияния на «внутренние десятичные вычисления»
Так как:
bc на самом деле не использует 50 («указанное количество десятичных цифр»), как указано выше.
Только если оно разделено, оно преобразуется (по-прежнему неверно, так как использует шкалу 2 для считывания константы
0.FD
перед ее расширением до 50 цифр):Однако это точно:
Снова, чтение числовых строк (констант) должно использовать правильное количество битов.
Математическое доказательство
В два этапа:
Двоичная дробь может быть записана как / 2 n
Бинарная дробь - это конечная сумма отрицательных степеней двойки.
Например:
= 0 + 0 × 2 -1 + 0 × 2 -2 + 1 × 2 -3 + 1 × 2 -4 + 0 × 2 -5 + 1 × 2 -6 + 0 × 2 -7 + 1 × 2 -8 + 1 × 2 -9 + 0 × 2 -10 + 1 × 2 -11
= 2 -3 + 2 -4 + 2 -6 + 2 -8 + 2 -9 + 2 -11 = (с удалением нулей)
В двоичной дроби из n битов последний бит имеет значение 2 -n или 1/2 n . В этом примере: 2 -11 или 1/2 11 .
= 1/2 3 + 1/2 4 + 1/2 6 + 1/2 8 + 1/2 9 + 1/2 11 = (с обратным)
В общем, знаменатель может стать 2 n с показателем положительного числа в два. Все члены могут быть затем объединены в одно значение a / 2 n . Для этого примера:
= 2 8 /2 11 + 2 7 /2 11 + 2 5 /2 11 + 2 3 /2 11 + 2 2 /2 11 + 1/2 11 = (выраженное с 2 11 )
= (2 8 + 2 7 + 2 5 + 2 3 + 2 2 + 1) / 2 11 = (извлечение общего множителя)
= (256 + 128 + 32 + 8 + 4 + 1) / 2 11 = (преобразовано в значение)
= 429/2 11
Каждая бинарная фракция может быть выражена как b / 10 n
Умножим a / 2 n на 5 n / 5 n , получив (a × 5 n ) / (2 n × 5 n ) = (a × 5 n ) / 10 n = b / 10 n , где b = a × 5 n , Он имеет n цифр.
Для примера имеем:
(429 · 5 11 ) / 10 11 = 20947265625/10 11 = 0,20947265625
Было показано, что каждая двоичная дробь является десятичной дробью с одинаковым количеством цифр.
источник