All streams
Search
Write a publication
Pull to refresh
-1
0
Спиридонов Юрий Маркович @Innotor

Изобретатель

Send message
Отбрасывание «лишних» цифр повлияет на скорость обработки данных? На объем занимаемой памяти? Каким образом?

На точность. См. Действия над приближенными числами
Эта фраза означает, что если есть 64-битная ячейка памяти типа double, то промежуточные вычисления будут в формате 128 бит. При чем тут 32 бита, которые имеют меньшую разрядность?

Все промежуточные вычисления выполняются в более широком формате, а хранятся в памяти в базовом формате обмена. В 32-х разрядных машинах базовым является 32-х разрядное слово.
Прочитайте еще раз то, что я написал. И предыдущие сообщения этой части. Я писал про подсчет значащих цифр. После нормализации он отличается от числа после запятой на единицу. То есть если мы хотим округлить до 3 значащих цифр, это означает 2 цифры после запятой в нормализованном формате

О каком представлении числа вы говорите? О двоичном или десятеричном? Поскольку внутреннее представление это нормализованное двоичное, то посчитать цифры в нем, нет проблем. Но вам надо определить множитель для десятичного представления. Следовательно без printf вам не обойтись.
Вот исходное число 1980704062856 и было округлено до 7 значащих цифр. Никакой ошибки округлений здесь нет. Вы в одном случае приводите к float одно число, в другом другое, потому и результаты различаются.

Повторюсь еще раз. Число 1980704062856 является представимым в double числом. Представимым, значит точно представлено в выбранном формате. Но оно не представимо в формате float. В результате округления числа double до float мы получаем правильное округление двоичного числа, в соответствии с выбранным сценарием, прописанным в Стандарте. Мы получаем другое представимое число. И оно, не смотря на то, что двоичное округление было верным может не являться ближайшим.
Вот исходное число 1980704062856 и было округлено до 7 значащих цифр.

Вернемся к истокам. В результате неких вычислений вы получили число double:1.1100110100101011001010010111110110001000100110110001*2^40. Для нас это первичное число. Никакой информации кроме двоичной мантиссы и экспоненты мы не имеем. Чтобы посмотреть, что это в десятичном виде мы применяем printf. И видим 1980704062856.605712890625. Это первичное десятичное представимое число.
Но нас для расчетов вполне устраивает точность N=7 (или любая другая). Для этого первичное число надо округлить до 7 значащих цифр. В десятичной арифметике мы бы получили 1980704000000. Но оно непредставимо в float. Поэтому, максимум на что мы можем рассчитывать это на ближайшее к этому представимое число, которое можно сохранить в fljat.
А это 1.980703965184E12. В котором 7 первых цифр совпадают с погрешностью <=ulp с идеально округленным числом 1980704000000.
Ваша программа не может, она запрашивает эту информацию у пользователя

Еще раз повторюсь. Программа не знает с какой точностью вас интересуют вычисления. Эту точность надо задать.

Надо привести одну цитату, подтверждающую ваши слова.

Я не могу привести одну цитату. Принцип двоичных вычислений представляет собой взаимосвязанный комплекс аппаратных и программных средств. Который базируется на принципах закрепленных в стандарте.
Стандарт определяет основные форматы ( 3.1.1 Formats ) представления двоичных и десятичных чисел. Сюда входит формат хранения и обмена данными (3.6 Interchange format parameters), который определяет разрядность компьютера. Для двоичных чисел приняты 3 основных формата 32, 64 и 128 разрядов. В этих словах могут храниться как целые числа, так и упакованные числа с плавающей точкой. Как двоичные так и десятичные. Определен также арифметический формат (arithmetic formats), который может как совпадать с базовыми форматами, так и иметь расширенный ( extended precision format) или расширяемый формат (extendable precision format). Последние форматы используются в арифметических вычислениях для повышения точности. Но после всех вычислений они снова упаковываются в формат обмена. Операционные регистры АЛУ всегда имеют бОльшую разрядность чем ячейки памяти, т.к. после распаковки восстанавливается виртуальная единица в нормализованном числе и добавляются сторожевые биты для повышения точности вычислений. Ну, действительно, развивать эту тему дальше, это еще одна статья в коментах.
Вы писали «мы хотели бы, чтобы в памяти хранилось число 1980704000000». Если нас устраивает отклонение в некоторых пределах, то надо так и писать, но в этом случае подходят оба варианта.


Как раз нас устроило бы если бы в память можно было записать точное значение 1.980704*10^13. Но, поскольку это число непредставимо в float, в результате мы получаем 1.980703965184E12.
А раз printf умеет ее делать, значит и в своем коде можно сделать так же.

А как работает printf вас устраивает? Тогда можно.
Изначальное число у нас было 1980704062856.605712890625.
Вы его округлили до 1980704000000, тем самым внесли отклонение на 62856.
Далее вы предлагаете внести отклонение еще на 34816 (1980704000000-1980703965184).
Каким образом это увеличивает точность?

Если при измерении вы получили результат 12.12345, а класс точности прибора 0.01, что нужно сделать с лишними цифрами? Округлить. В результате мы получаем более точное измерение или нет?
Также и в нашем случае, если мы хотим получить точность вычислений до 7 значащих цифр мы свое число должны округлить до 7 значащих цифр. Но, поскольку число не представимо в flooat, мы получаем ближайшее к точно округленному числу представимое число.
Если мы хотим увеличить точность, надо брать ближайшее к исходному числу. Во float это 1980704096256 (ошибка 33400).

Совершенно верно. Но в double хранится двоичное число с какой-то экспонентой и без преобразования в десятичное представление вы не можете точно сказать на что надо умножить и разделить это двоичное число. А моя программа может.
Там нигде не написано, что 64-битный double преобразуется в 32-битный float.

Я не могу здесь пересказывать вам стандарт IEEE754. Я дал ссылку на Wiki… Там можно найти нужную литературу.
Не могут.

Могут. Десятичное 1.980704*10^13 преобразуется в
1.980703965184E12, в котором первые 7 цифр с погрешностью <=0.5ulp дают число 1.980704.
У вас оно вообще вводится пользователем.

Естественно, кто кроме пользователя владеет этой информацией. Откуда машине знать до какого количества десятичных цифр надо округлять.
Вы выдаете определение числа значащих цифр за преимущество своего алгоритма.

Да, по другому эта задача не решается так просто.
Кстати, тут вообще не очень понятно, что вы называете «задача решена»

Решена, поскольку очень просто округляет любое десятичное число в двоичном коде до нужного пользователю количества десятичных цифр.
Молодой человек! Если вы хотите постигнуть истину, а не самоутвердиться, пишите мне в личку или на мой блог. Я постараюсь найти время ответить вам.
В таких утверждениях надо приводить ссылки на тот стандарт, который вы имеете в виду, желательно с указанием цитаты.

IEEE Standard for Floating-Point Arithmetic (IEEE 754). Разделы: «Extended and extendable precision formats» и «Interchange formats».
Значит вы неправильно выбрали инструмент, потому что там 30 значащих цифр, а во float влазит 24. А в double это значение нормально влазит. Значит при использовании double оно округлится так, как нужно.

Ну, во-первых, в числе 1980704000000= 1.980704*10^13 всего 7 значащих десятичных цифр, которые гарантированно могут быть представлены в 24 разрядной мантиссе float с точностью <=0.5ulp (десятичной). А во-вторых, формат doable гарантированно может представить 15 десятичных цифр (где-то, кажется вы сами об этом упоминали). Так что число 1.980704*10^13 гарантированно «влазит» в float с указанной погрешностью и точно, как бы это вас ни коробило, в формат doable. Но если взять числа с большей экспонентой, например число 1*10^30, то оно уже и в doable не вместится.
Посчитать значащие цифры и отнять сколько надо.

У вас в формате double записано число 1.0110101111001100010000100100111111101011011110010111*2^140. Посчитайте сколько значащих десятичных цифр в этом числе и округлите до 3 значащих цифр. На основании того, что вы свои расчеты выполняете с точностью до 3 значащих цифр, а остальные для вас являются ложными, согласно теории приближенных вычислений.
Ваше достижение в том, что вы собрали 2 алгоритма вместе?

Что вы имеете ввиду? Какие 2 алгоритма я собрал вместе? Подскажите. Честное слово, я не нарочно:).
А вы загляните в IEEE754, там все описано.
Вопрос не в 64 или 32 словах обмена.
Процессор, как правило, вычисляет в расширенном формате, а затем пакует в более компактный. Для 64-х разрядных, это 80- битные и даже 128-битные операционные регистры. Но потом все равно приходится паковать. А именно здесь собака и порылась. Я тут выше уже давал ссылки на последние работы по этой проблеме. Пока то, что предлагается, ну крайне громоздко.
Может быть следующее пояснение расставит все по своим местам.
Согласно Стандарту обмен операндами в компьютере производится через внутренний формат обмена в 32- разрядном слове. Чтобы сохранить промежуточный результат в массиве, надо число double преобразовать в float.
Пусть у нас есть число в double:
1.11001101001010110010100 10111110110001000100110110001*2^40 и нам надо его запомнить с минимальной погрешностью.

Если округлить это число до 24 значащих двоичных цифр с погрешностью <0.5ulp, мы получим число 1.11001101001010110010101 *2^40
Десятичный эквивалент этого числа, который будет записан в формат обмена, будет:
11100110100101011001010100000000000000000.000=1980704096256.
Для лучшего восприятия я числа представляю в произвольном масштабе. При желании их можно нормализовать.
Нас интересует точность представления, равная 7 десятичным цифрам. Т.е., мы хотели бы, чтобы в памяти хранилось число 1980704000000. Тогда погрешность десятичного округления была бы <=0.5ulp, но уже десятичного ulp. Двоичный эквивалент числа 1980704000000 в формате double равен 111001101001010110010100 01000100000000000. Или округленное до 24 значащих цифр это число будет равно 111001101001010110010100*2^17. Это число ближайшее к правильно округленному числу 1980704000000 и как мы видим оно отлично от правильно округленного в двоичной арифметике первичного двоичного числа.
Таким образом, чтобы минимизировать потери, перед записью в формат обмена, двоичное число double должно быть правильно преобразовано к двоичному числу, которое должно являться представимым числом, ближайшим к правильно округленному десятичному числу.
При большом массиве обрабатываемых чисел использовать для округления функцию atof очень затратно. Конечно, лучше всего разделить число 1980704096256, в нашем случае на 10^6, округлить, а затем снова преобразовать во float для записи в память. Но как догадаться, что нужно разделить именно на 6? В моем алгоритме эта задача решена.
Вам кажется. Отсекая лишние цифры («мусор») точность повышается.
Если число приближенное, то да, погрешность <=ulp. А если оно точное, то даже записанное в форме с плавающей точкой, оно остается быть точным. Ну, не должен знать калькулятор биографию этого числа.
Я лично, работаю с изучением проблем двоичных вычислений. И пытаюсь как-то улучшить двоичную арифметику, чтобы она была точнее и быстрее. А мне все доказывают, что их устраивает статус-кво. Жаль. У нас разные цели.
Давайте, как-то определим круг обсуждаемых вопросов.
1. Предложен альтернативный известному алгоритм округления десятичных чисел в двоичном коде. Он работает.
Возражений не?
2. Десятичное округление десятичных чисел в двоичном коде может повысить точность вычислений. Приведен конкретный пример.
Обоснованных аргументов против этого факта пока высказано не было.
3. Вопрос -А на «хрена» это нужно?
См. п.2.
4. Чем меня не устраивает стандартная функция округления?
Если она вас устраивает, значит вы не сталкивались с задачами, где она облегчает жизнь. Пользуйтесь стандартной. Но реализовать стандартную функцию в железе затратно.

Для вас оно может быть точное, а может быть и не точное. Все зависит от его происхождения. Для калькулятора — это точное число, т.к. он ничего не знает об истории его рождения. Его задача, максимально точно сделать вычисления, не внося в них, по возможности, больших искажений, а как их интерпретировать, не его забота.
Мы имеем 2 числа. Они абсолютно точные. Я ручаюсь, сам придумал:).
И мне захотелось узнать, чему равна разность этих чисел. Имею право? Имею. Ждать мне некогда, матлабы и прочие инструменты слишком долго считают. Решил посчитать в двоичной арифметике, используя Стандарт для чисел с плавающей точкой. Набираю на клаве свои абсолютно точные числа 9876543210988,06 и 9876543210988,04. Поскольку все 15 цифр в числах верные, то и результат хочу получить с такой же точностью, т.е. с точностью до 15 значащих цифр.
Напечатал я одни числа, а на вход моего двоичного калькулятора поступают другие числа 9.876543210988060546875E12 и 9.8765432109880390625E12. Смотрю на сколько они отличаются от моих точных. И вижу, что погрешность первого числа составила 0,000546875, а второго 0,0009375. Многовато конечно, ну да ладно. Назад пути нет, процесс необратимый.
Залил я свои числа в калькулятор и на выходе получил ответ: 0.021484375. Да, что-то не очень. Я калькулятору — Ты что ж, гад, мне выдал? А он мне — Ничего не знаю, что компилятор мне дал, то я и посчитал. Посчитал, кстати, точно. Т.к. 9.876543210988060546875E12-9.8765432109880390625E12=0.021484375. Как ни крути, результат точный. И погрешность, ну строго по теории образовалась: 0,000546875+0,0009375=0.000484375
Кто же, думаю, виноват? Компилятор свое дело сделал точно, но числа получились приблизительными. Калькулятор посчитал точно, но ответ еще больше далек от правды. Что же делать? Присмотрелся к ответу, а там две цифры верные. Эврика? Надо отсечь лишние. Как, да просто округлить надо! А как это сделать, это совсем другая история.
Это вопрос для другой темы обсуждения. Мы же обсуждаем влияние десятичного округления двоичных чисел на точность вычислений.
Что касается порядка чисел, приведенных в примере, то я думаю где-нибудь в астрономии, космологии, или в микромире, вы вполне можете столкнуться с подобными величинами.
Абсолютно точно. Именно эти два числа.
Ну, хорошо. Вот вам пример.
Рассмотрим разность двух десятичных чисел 9876543210988,06-9876543210988,04=0,02. В формате double эта разность будет 9.876543210988060546875E12-9.8765432109880390625E12=0.021484375
Ошибка вычисления в двоичном коде относительно точного значения будет |0.02-0.021484375|= 0.001484375. Если же округлить полученный результат до 2 знаков после запятой получим 0,02. Или в формате double 2.00000000000000004163336342344E-2. Ошибка после десятичного округления двоичного числа составляет |0.02-0.0200000000000000004163336342344|≈0,4*10^-17.
Спасибо за диалог. Алгоритм мною разрабатывался для аппаратной реализации. Тестовая программа является эмулятором работы железа. Мне было важно убедиться, что алгоритм работает правильно. Что наша дискуссия и подтвердила.
Но это не проблема программы. Я думаю, это проблема округления 53-й значащей цифры в двоичном числе «бесконечной точности» после конвертации десятичного числа.

Information

Rating
Does not participate
Location
Минск, Минская обл., Беларусь
Registered
Activity