Как стать автором
Обновить
0
0
Спиридонов Юрий Маркович @Innotor

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

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

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

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


Ваша программа. Вариант 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;

}


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

Согласен, в ВАШЕЙ программе 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;

}


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

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


Ваша программа с изменениями
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;

}

Итак, вскрытие показало следующее.
Для округления было взято число 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. Неправильно.

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

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

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

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

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

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

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

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

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

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

Вся информация для любого алгоритма естественно имеется, в том числе и в интернете. Вопрос, как распорядиться этой информацией. Вы, почему-то, для доказательства стандартности алгоритма округления, привели свою программу, в которой использовали frexp из моей программы, а не десятичный логарифм, который выполняет ту же задачу в широко известных стандартных алгоритмах округления. Хотелось бы увидеть хоть одну программу округления, где бы использовалась функция frexp.

Информация

В рейтинге
Не участвует
Откуда
Минск, Минская обл., Беларусь
Зарегистрирован
Активность