• Правильное округление десятичных чисел в двоичном коде
    0
    Да, правильно. Десятичные числа ваша программа обрабатывает правильно. У меня задача стояла несколько по другому. Есть двоичное нормализованное число, допустим float (в double масштаб изменится), допустим 1.11100111011000011110011*2^-78. Нужно преобразовать его так, чтобы новое число было двоичным эквивалентом округленного до N значащих цифр десятичного числа. Вычисление lg очень затратно. Требует большого количества делений и сравнений. Поэтому разработал тот, который и представил. Он рабочий и не требует вычисления lg. Я не нашел в инете программ, где использовалась бы функция frexp для округления.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Я изменил параметры в вашей программе так И вот что получается. Округление double до 7 значащих цифр для d2 выполняется правильно. Но конвертация в float выполняется с погрешностью. Работая с float мы рассчитываем, что 7-ми значное число будет представлено с минимальной погрешностью. Поэтому, прежде чем конвертировать из double (или другого более широкого формата) в float оно должно быть сначала правильно округлено. Округление через log очень затратно, поэтому в процессоре просто округляют двоичное число с использованием любого известного сценария округления. Мой алгоритм позволяет перейти к float с правильным округлением с минимальными затратами.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Я вам дал ссылку на онлайн редактор. Напишите пожалуйста там код, который подтверждает ваши слова.
    Я ничего не менял в вашей программе. Как она вами была туда помещена, так я ее и запустил. На выходе программы получились те числа, которые вы привели в тексте вашего комента выше. Они полностью совпадают с теми, что получены в результате работы вашей программы. См. Скриншот
    Именно эти числа я и приводил в качестве доказательства своей правоты. Что я сделал не так?
  • Правильное округление десятичных чисел в двоичном коде
    0
    За науку спасибо. Почти разобрался, кроме портабельного вывода.
    Раз вы не можете разобраться, что вы запускаете на своем компьютере, пишите пожалуйста код там, запускайте, проверяйте
    Я не подвергаю сомнению работу вашей программы. Я верю числам, которые ваша программа выдает. Я говорю, что эти числа иногда имеют погрешность больше, чем можно получить, если использовать мой алгоритм. Доказательства я привел выше.
  • Правильное округление десятичных чисел в двоичном коде
    0

    Округленные числа, приведенные вами выше: 5.50624235984287242318e-17
    5.50624235984286995799e-17
    Правильные. Есть числа, которые неправильно округляются и вашей программой и моей. Мы обсуждали это ранее.
    Рассмотрим вторую группу чисел.
    Округленное до 7 значащих цифр число 1980704062856.605712890625= 1.98070406285660571289e+12 равно 1.980704e+12. Запишите это число в doable и получите то же число 1.980704e+12. Т.е. это представимое число. В вашей программе оно округлено до 1.98070406285661010742e+12. Это правильно?

  • Правильное округление десятичных чисел в двоичном коде
    0

    Да не нервничайте вы так. И перестаньте бросаться словами — "вы врете". Мы просто разговариваем пока на разных языках. Я пытаюсь понять ваш язык. Попробуйте и вы понять о чем я говорю. Вы классный программист. Но речь идет о математике. Я запустил вашу программу, текст которой я вам привел выше. Могу повторить.


    Ваша программа. Вариант 2
    include <stdio.h>
    include <stdlib.h>
    include <math.h>

    double roundToSignificantDigits(double value, int nSignificantDigits)
    {
    int normalizationDegree = (int)floor(log10(fabs(value))); // or frexp * log2
    int nDigitsAfterDotNormalized = nSignificantDigits — 1;
    int nDigitsAfterDot = nDigitsAfterDotNormalized — normalizationDegree;


    double multiplier = pow(10, nDigitsAfterDot);
    value = floor(value * multiplier + 0.49999999999999994) / multiplier;
    
    return value;

    }


    int main()
    {
    double d1 = 5.50624235984287224709987640380859375e-17;
    double d2 = atof("1980704062856.605712890625");


    printf("%.20e\n", d1);
    printf("%.20e\n", roundToSignificantDigits(d1, 15));
    printf("%.20e\n", (float)d1);
    printf("%.20e\n", (float)roundToSignificantDigits(d1, 7));
    
    printf("%.20e\n", d2);
    printf("%.20e\n", roundToSignificantDigits(d2, 15));
    printf("%.20e\n", (float)d2);
    printf("%.20e\n", (float)roundToSignificantDigits(d2, 15));
    
    return 0;

    }


    Запустив программу, я на консоле получил те числа, которые и привел выше. Где я вас в чем обманул? Вывести их другим способом я не умею. Скриншот прикрепить тоже. Я выписал их с консоли.
    Еще раз говорю, проблема не в программе, а в двоичных числах, которые используются как эквивалент десятичных. Если в программе я допустил ошибку, исправьте. Если вы готовы, мы можем обсудить те числа, которые вы привели в этом коменте.

  • Правильное округление десятичных чисел в двоичном коде
    0

    Согласен, в ВАШЕЙ программе atof используется неправомерно. Строку мы вводим не с консоли, а задаем прямо в теле программы константой. Зададим наше число правильно.


    Ваша программа. Версия 2
    include <stdio.h>
    include <stdlib.h>
    include <math.h>

    double roundToSignificantDigits(double value, int nSignificantDigits)
    {
    int normalizationDegree = (int)floor(log10(fabs(value))); // or frexp * log2
    int nDigitsAfterDotNormalized = nSignificantDigits — 1;
    int nDigitsAfterDot = nDigitsAfterDotNormalized — normalizationDegree;


    double multiplier = pow(10, nDigitsAfterDot);
    value = floor(value * multiplier + 0.49999999999999994) / multiplier;
    
    return value;

    }


    int main()
    {
    double d1 = 5.50624235984287224709987640380859375e-17;
    double d2 = atof("1980704062856.605712890625");


    printf("%.20e\n", d1);
    printf("%.20e\n", roundToSignificantDigits(d1, 15));
    printf("%.20e\n", (float)d1);
    printf("%.20e\n", (float)roundToSignificantDigits(d1, 7));
    
    printf("%.20e\n", d2);
    printf("%.20e\n", roundToSignificantDigits(d2, 15));
    printf("%.20e\n", (float)d2);
    printf("%.20e\n", (float)roundToSignificantDigits(d2, 15));
    
    return 0;

    }


    Но результат тот же. И виновата в этом не ваша программа, а двоичная арифметика.

  • Правильное округление десятичных чисел в двоичном коде
    0

    Я внес изменения только те, что вы мне рекомендовали.


    Ваша программа с изменениями
    include <stdio.h>
    include <stdlib.h>
    include <math.h>

    double roundToSignificantDigits(double value, int nSignificantDigits)
    {
    int normalizationDegree = (int)floor(log10(fabs(value))); // or frexp * log2
    int nDigitsAfterDotNormalized = nSignificantDigits — 1;
    int nDigitsAfterDot = nDigitsAfterDotNormalized — normalizationDegree;


    double multiplier = pow(10, nDigitsAfterDot);
    value = floor(value * multiplier + 0.49999999999999994) / multiplier;
    
    return value;

    }


    int main()
    {
    double d1 = atof("5.50624235984287224709987640380859375e-17");
    double d2 = atof("1980704062856.605712890625");


    printf("%.20e\n", d1);
    printf("%.20e\n", roundToSignificantDigits(d1, 15));
    printf("%.20e\n", (float)d1);
    printf("%.20e\n", (float)roundToSignificantDigits(d1, 7));
    
    printf("%.20e\n", d2);
    printf("%.20e\n", roundToSignificantDigits(d2, 15));
    printf("%.20e\n", (float)d2);
    printf("%.20e\n", (float)roundToSignificantDigits(d2, 15));
    
    return 0;

    }

  • Правильное округление десятичных чисел в двоичном коде
    0

    Итак, вскрытие показало следующее.
    Для округления было взято число d1=5.50624235984287224709987640380859375e-17. Которое на консоль было выведено как 5.5062423598428724e-17.
    Округленное до 15 значащих цифр это число должно быть равно 5.50624235984287e-17. Ближайшее к нему представимое число-double равно 5.5062423598428699579894231941E-17.
    (Использовался калькулятор) На консоль выведено число 5.50624235984287E-17. Неправильно.
    Число-double d1 преобразованное к float равно 5.50624222925600605681173416173E-17(См. на калькуляторе). На консоль выведено число 5.5062422292560061E-17. Правильно.
    Округленное до 7 значащих цифр это число должно быть равно 5.062422E-17. Ближайшее к нему представимое число должно быть равно 5.06242211118537429118124926219E-17. На консоль выведено число 5.06241898383761E-17. Неправильно.

  • Правильное округление десятичных чисел в двоичном коде
    0
    Почему не в double? Как, по-вашему, должно выглядеть «выводить результат в double»
    Формат double, это формат двоичного числа с плавающей точкой. У него есть мантисса и экспонента. Так что это число должно выводиться — мантисса+экспонента, только в десятичном эквиваленте. А то, для экспоненты, скажем =1020, замучаешься искать значащие цифры на консоле. Или я не прав?
    Я в вашей программе ничего не менял, кроме коэффициента nSignificantDigits=15, задающего точность. Поэтому ваше число double d1 = atof(«5.50624235984287224709987640380859375e-17»); если его округлить до 15 значащих цифр должно иметь 15 значащих цифр и должно быть равно 5.50624235984287. Но программа округляет его до 14 цифр и дает результат 5.5062423598429. Не спорю, правильный.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Если чем обидел, простите. В вашей программе я ввел nSignificantDigits=15, но получил округление только до 14 цифр. Или что-то ни так?
    Раз еще и про производительность начали говорить, то показывайте как замеряли производительность.
    Я уже ранее говорил и вы, вроде, не возражали, что считать в регистр двоичную экспоненту, которая хранится в готовом виде в коде числа double, а затем умножить ее на коэффициент, значительно проще, чем выполнить итеративный алгоритм вычисления логарифма. Мне кажется это очевидно.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Кстати, а почему ваша программа выводит результат не в double, а в строку? Это что же, потом ее опять надо конвертировать в doable?
  • Правильное округление десятичных чисел в двоичном коде
    0
    вы, судя по всему, не разбираетесь ни в программировании, ни в математике, и не сможете правильно оценить сами,

    Ну, что же вы меня все время роняете? С вашей помощью, думаю, разберемся.
    Округляет ваша программа правильно, но только до 14 значащих цифр. А моя до 15 и в разы быстрее. Она рассчитана на железо.
    Вы правильно сделали, что выбрали вариант с логарифмом, он все же проверен многократно. А моя программа, в вашем исполнении, страдает изъянами. Уж, извините.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Даже если у меня в программе где-то ошибка в вычислениях, и результаты различаются, это именно ошибка, а не намеренно написанный алгоритм.
    Мы еще не начали, а вы уже оправдываетесь. Выберите ту стандартную программу, которая широко используется в интернете и которая 100% решает задачу десятичного округления двоичных чисел. Вы же мне столько ссылок давали.

  • Правильное округление десятичных чисел в двоичном коде
    0
    Давайте сначала уточним, какая из программ более ваша, которая коэффициент определяет через log, или та, где используется frexp. С какой будем сравнивать?
  • Правильное округление десятичных чисел в двоичном коде
    0
    :-)
  • Правильное округление десятичных чисел в двоичном коде
    0
    :)
  • Правильное округление десятичных чисел в двоичном коде
    0
    Практика — критерий истины.
    Предлагаю дуэль.Сражаться будем не языком, а цифрами. Согласны?
    Если да — вот мои условия.
    Вы выбираете одну, самую стандартную программу из тех, что вы здесь предлагали или из тех, что найдете в интернете. Но одну, и самую стандартную:)!
    И мы будем (стреляться) округлять те числа, которые предложим друг-другу. Каждому дается три выстрела. Оружие в процессе дуэли не менять. Согласны?
    Или и дальше будем соревноваться в словесной изящности?
  • Правильное округление десятичных чисел в двоичном коде
    0
    Речь идет об округлении, а не о вычислении логарифмов.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Нет такого термина «десятичное число, представленное двоичным кодом». Есть просто число, которое может быть представлено… двоичной или еще какой-то системе счисления.
    Вы не находите, что «нет» и «есть» здесь взаимоисключающие утверждения?
    Нет там разных алгоритмов. Все реализации описываются выражением «умножить, округлить до целого, поделить». Потому он и стандартный

    Ни одна из этих операций (умножить, округлить...) не является стандартной. Каждая может быть реализована множеством способов (алгоритмами). Все зависит от процессора. Также и реализация «умножить, округлить до целого, поделить» имеет множество вариантов даже в рамках одного языка. А раз так, у каждого есть свои достоинства и недостатки. Стандартной реализации нет.
    Я подразумеваю те алгоритмы и их реализации, которые решают задачу округления числа с плавающей запятой до N десятичных знаков после запятой либо значащих.

    Как же так, если
    В формате double используется двоичная система счисления, никакой десятичной там нет,

    Потому что вычисление степени десятки и округление это разные алгоритмы.

    Впрочем, как умножение, возведение в степень и проч. Собранные вместе определенным образом эти алгоритмы образуют алгоритм округления десятичного числа, представленного в двоичной системе.
    На той страничке google десятичный логарифм используется почти в каждой ссылке.

    Приведу вам ваши же слова:

    Надо привести одну цитату, подтверждающую ваши слова. Судя по тому, что вы уходите от ответа, вы сами ее найти не можете.

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

    Вся информация для любого алгоритма естественно имеется, в том числе и в интернете. Вопрос, как распорядиться этой информацией. Вы, почему-то, для доказательства стандартности алгоритма округления, привели свою программу, в которой использовали frexp из моей программы, а не десятичный логарифм, который выполняет ту же задачу в широко известных стандартных алгоритмах округления. Хотелось бы увидеть хоть одну программу округления, где бы использовалась функция frexp.
  • Правильное округление десятичных чисел в двоичном коде
    0
    «можно через двоичный, умноженный на коэффициент.»
    Вы эту фразу имеете ввиду?
    Это высказывание моего оппонента. Здесь имелось ввиду, наверное, десятичный логарифм двойки. По крайней мере, именно в этом смысле я воспринял его вольное высказывание. Что же касается равенства lbX=logX, то однозначно это неверно.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Я написал алгоритм, который описан в этом комментарии

    В этом коментарии нет алгоритма округления десятичного числа представленного двоичным кодом.
    («умножение числа и последующее после округления деление») и встречается много где в интернете (1, 2, 3, 4).

    Вы правы, алгоритм «умножение числа и последующее после округления деление» много раз встречается в интернете. Только в ваших ссылках я насчитал более 20 алгоритмов округления. Но, «стандартного» не обнаружил.
    что вас не устраивает в существующих средствах?

    Под средствами вы понимаете множество существующих алгоритмов или набор стандартных функций?
    Можно через десятичный логарифм, можно через двоичный, умноженный на коэффициент. Математически они эквивалентны. Через десятичный логарифм в интернете встречается чаще,

    Вы мне дали ссылку на поисковую страничку google и предлагаете просмотреть все ссылки, чтобы убедиться, что «Через десятичный логарифм в интернете встречается чаще». Но почему-то на страницах, которые вы указали в предыдущих 3-х ссылках я не встретил ни одного алгоритма использующего этот способ.
    Что касается «можно через двоичный, умноженный на коэффициент.» Но, что-то я тоже не нашел ни одного алгоритма, кроме моего и чудесным образом «вашего», где бы использовался такой способ определения коэффициента.
    Я написал алгоритм… Если это то же самое, что написали вы, тогда непонятно, почему вы говорите, что что-то где-то неправильно округляется.

    Вы написали программу по моему алгоритму. Но дьявол кроется в деталях.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Замечательно! Вы взяли мой алгоритм, один к одному его переписали и выдали его за свой. Может быть вы мне тогда объясните, зачем вы применили эту функцию -(int)floor(e * 0.301)? И дадите ссылку на стандартный алгоритм, где она используется. Буду очень благодарен.
  • Правильное округление десятичных чисел в двоичном коде
    0
    На какую степень десятки надо умножить двоичное число, приведенное мною?
  • Правильное округление десятичных чисел в двоичном коде
    0
    В результате некоторых вычислений вы получили какое-то double-число: 1.0011… 00011*2^123 (любое). Вам из него надо получить двоичное число, равное ближайшему к правильно округленному до 7 десятичных знаков десятичному числу.
    Все, больше вам ничего не известно. Как это сделать без конвертации этого числа в десятичный код стандартной программой округления?
  • Правильное округление десятичных чисел в двоичном коде
    0
    Еще раз повторю. Речь идет не об округлении десятичных чисел, выведенных на консоль. А о десятичном округлении двоичных чисел doable, десятичный эквивалент которых вам неизвестен пока вы не преобразуете doable в десятичное число с помощью printf.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Дело же в остальном алгоритме.

    Да нет, квинтэссенция моего алгоритма именно в том и состоит, что по двоичному doable определяется десятичное значение экспоненты. Когда это значение определено, дальше преобразования идут по стандартному алгоритму десятичной арифметики. Когда вы применяете функцию atof, вы в ней задаете десятичное значение экспоненты, вводя ее при печатании вручную. Но в doable может храниться произвольная двоичная экспонента для двоичного числа, десятичное значение которого вам не известно…
  • Правильное округление десятичных чисел в двоичном коде
    0
  • Правильное округление десятичных чисел в двоичном коде
    0
    Другая проблема возникает при сохранении чисел из более широкого формата в более узкий. Например, double в float, расширенный doable в простой double. Последний случай сэмулировать сложно, поэтому я написал тестовую программу округления более широкого формата до более низкого с использованием double. Другие случаи просто масштабируются.
    Так вот, проблема в том, что до одного и того же числа можно округлить разные числа. Например, два double — числа 5.50624245984287807511338305951E-17и 5.50624155984287824709987743381E-17, округленные до 14 и менее значащих цифр будут иметь одно и то же значение. (Я специально увеличил |значение| экспоненты). Так, округленное до 7 значащих цифр наше число, это 5.506242. Опять же, в десятичной арифметике, ни каких проблем здесь не возникает. Берем и округляем, если вычислили десятичную экспоненту. Но в двоичном представлении все гораздо сложнее.
    Возьмем число 5.50624245984287807511338305951E-17. Его двоичное представление будет таким 1.1111101111011100011110010110010011010000110111101111*2^-55.
    Мы знаем, что в мантиссу float можно записать 24 бита. Округляем наше double-число до 24 значащих цифр с погрешностью <=0.5ulp. Получим 1.11111011110111000111101*2^-55= 5,5062425601282510780228041102902e-17≈5,506243. В результате правильного округления двоичного числа double с погрешностью <0.5 ulp мы получили округление его десятичного эквивалента c погрешностью>1ulp. Ближайшим же к правильно округленному десятичному числу будет float-число 5.50624189838376103560066421316E-17.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Спасибо за толковый ответ.
    Округление десятичного числа с плавающей запятой — тривиальная задача, если известна его экспонента и что число — нормализованное. Проблема заключается в нахождении значения десятичной экспоненты. Функция frexp в моем алгоритме просто считывает значение двоичной экспоненты e в числе double. Не знаю, насколько это легко выполнить программно, но аппаратно это осуществляется за 1 такт. Для определения десятичной экспоненты осталось вычислить floor(e*double(value*0.301)). Думаю, это реализовать проще, чем вычислить log 10(value). А что вы думаете?

  • Правильное округление десятичных чисел в двоичном коде
    0
    Спасибо. Только, поскольку я в программировании чайник, не могли бы вы эту программу расписать алгоритмически или на языке математики?
  • Правильное округление десятичных чисел в двоичном коде
    0
    Я тоже.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Производительность достигается распараллеливанием вычислений, а не перемалыванием чисел-монстров. Но, на эту тему я дальше спорить не буду. К моему алгоритму это отношения не имеет.
  • Правильное округление десятичных чисел в двоичном коде
    0
    И он, что характерно, с этим справляется.

    Да вот, оказывается, не очень. Зачем-то заменили 32-разрядные компьютеры на 64-разрядные, а теперь и на 128. А еще работают над 256 разрядными? Вам ни кажется это странным? Вы представляете себе это десятичное число с плавающей запятой? А все ради нее, ради точности стараются.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Вставляйте округление в место, где посчитаете нужным, продемонстрируйте результат.

    Своими ручками пожалуйста. Вставьте где нужно мое округление и покажите, что оно не работает. Вся информация для этого в статье и в коментах. Если будут вопросы, обращайтесь.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Для вас математика — учебник Брадиса. Для меня -Modern Computer Arithmetic Richard P. Brent and Paul Zimmermann
    На том простом основании, что все компьютеры работают по теории, изложенной в этом учебнике, а не в учебнике Брадиса. Поэтому мы с вами на разных языках разговариваем.
  • Правильное округление десятичных чисел в двоичном коде
    0
    Вы плохо усвоили, то, что написано мною выше. Т.к. не читали. П вотому что не написали ни одного возражения, кроме набора тривиальных фраз.
    Проверить это утверждение очень легко. Сосчитайте с использованием округления то, что легко проверяется аналитически (sin(pi) через ряд), или сравнением (дважды обращенная матрица равна сама себе).

    Вы жаждете, чтобы я сам себя «зарезал», а вы постояли в сторонке, а потом сказали — Ну, вот, что я говорил. Нет, батенька, потрудитесь сами. Получите результаты, правильно их интерпретируйте, а затем мы с вами их обсудим. ОК?
  • Правильное округление десятичных чисел в двоичном коде
    0
    Как-то вы неясно излагаете свою мысль. Что? Вопросы точности разработаны так давно, что уже не актуальны? Или вас возмущает факт «рождения хренов с горы»? Что мы обсуждаем то? И где здесь математика?
  • Правильное округление десятичных чисел в двоичном коде
    0
    Да, вы правы. В Багдаде не спокойно:).
    У.Кэхэна читал, Д.Голдберга читал, Харрисона читал, Циммермана читал. Брадиса? Кажется в школе проходили. А в чем дело?
  • Правильное округление десятичных чисел в двоичном коде
    0
    Если вы ознакомились со статьей «Что такое точность?», то в рамках рассматриваемой там терминологии можно сказать. Если длинна мантиссы двоичного нормализованного числа не меняется, то любые арифметические преобразования в парадигме IEEE754 не приводят к изменению точности p (precision) представления числа. Если она меняется, например для субнормальных чисел, то точность (precision) уменьшается.
    Для десятичных чисел в двоичном коде другая ситуация. Изменение длинны двоичной мантиссы может приводить к потере точности десятичного числа, может повышать точность, а может и не менять ее. Повышение точности в смысле accuracy, т.е. близости к теоретическому результату мы и обсуждали в комментах выше.
    Ну и пример того, когда десятичная точность не меняется при сокращении длинны мантиссы. Если число точное (exact) и представимо в мантиссе с p разрядами, то оно представимо и точно (exact) в мантиссе с p+n разрядами. Отсюда, усечение n разрядов двоичной мантиссы приводит к уменьшению precision, но exact и accuracy не меняются. Так, число 0.125 можно представлять 64-мя,24-мя или 3-мя значащими цифрами, его значение не изменится.