Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Десятичная дробь - не понятки...


Автор: ressac 19.12.2006, 20:19
привет всем. ;)


Код


#include <stdio.h>

main()
{
 int i,num; float num_f,a,b;
 printf("Enter the number: "), scanf("%f",&num_f);
 for(i=1;num_f;i++)
 {
  printf("\n\n%.80f",num_f);
  
  num_f*=10; //printf("\n\n%.80f",num_f);
  
  num=num_f; //printf("\n\n%d",num);
  
  num_f-=num; //printf("\n\n%.80f",num_f);
  
  
  printf("\n ------\nFINAL\n ------\nI:%d Num_f: %.80f\n---------",i,num_f);

  system("pause");
 }

system("pause");
}



Вот прога которая по идее должна считать сколько цифр после запятой, на теории всё работает , а вот на практике нет smile
например я ввожу цифру "0.01" а реально получаю вот что ---> "0.00999999977648258210000000000000000000000000000000000000000000000000000000000000" или "0.1"  а получаю "0.10000000149011612000000000000000000000000000000000000000000000000000000000000000"

так вот вопрос в том, откуда берутся эти левый цифры? 
в них вся проблема , если бы их не было цикл повторялся ровно столько сколько есть цифр в ведёном номере тойсть если "0.1" то он и повторится один раз но так как появляются другие цифры то ясно что он и будет повторятся больше чем один раз smile
короче SOMEBODY  HELP  ME PLEASE smile  smile 

спасибо пока. ;)

p.s. там ещё принтфы я напихал, это для того чтоб было наглядно видно что мы имеем после каждой операции. Я юзаю gcc компиль может в борлонде будет всё по другому хз.

Автор: nickless 19.12.2006, 20:37
Цитата(ressac @ 19.12.2006,  19:19)
откуда берутся эти левый цифры? 

Компьютер хранит числа в двоичной системе и с ограниченной точностью, поэтому числа, которые не возможно точно представить в двоичной системе, округляются. 
Hапример 1/10=1/(2*5), 5 на 2 не делится.
Если тебе не обязательно надо float, считывай в char* и считай цифры там, или сначала округляй float, пиши его в char* и считай там.

Автор: sergejzr 19.12.2006, 20:50
Возможных десятичных чисел - бесконечное количество, а вот бит для представления - количество очень даже ограниченное (32). Из этого следует, что невозможно любую десятичную дробь точно представить. Поэтому числа и "округляются"

Автор: ressac 19.12.2006, 20:59
nickless, 
не совсем тебя понял насчёт char

Автор: nickless 19.12.2006, 21:39
Цитата(ressac @ 19.12.2006,  19:59)
не совсем тебя понял насчёт char

Я имел ввиду число просто как строку сохранить, а потом просто посчитать сколько букв между точкой/запятой и концом строки.
Код
    char str[64];
    printf("Enter the number: "), scanf("%s", str);
// тут считать

Автор: Dov 19.12.2006, 21:53
Цитата(ressac @  19.12.2006,  19:19 Найти цитируемый пост)
так вот вопрос в том, откуда берутся эти левый цифры? 

Отсюда
Код
printf("\n\n%.80f",num_f);
А точнее отсюда
Код
%.80f

Ты указал точность: 80 знаков после запятой.

Автор: W4FhLF 19.12.2006, 21:53
Всё дело в том, что в данном случае тип float есть короткий формат представления дробных чисел в памяти, точность вычисления 6 знаков после запятой, после чего идёт округление самим арифметическим сопроцессором, при расчёте дробной части всё, что после 6 знака просто не учитывается, поэтому выводить 80 знаков после запятой не имеет никакого смысла. Максимальная точность у расширенного формата, там точность достигает 19 знаков после запятой. 

Автор: nickless 19.12.2006, 22:01
Цитата(Dov @ 19.12.2006,  20:53)
Ты указал точность: 80 знаков после запятой.

Даже если поставить 4 знака, считать всё равно будет неправильно, потому что число каждый раз округляется.

Автор: ressac 19.12.2006, 22:15
Цитата(nickless @  19.12.2006,  19:39 Найти цитируемый пост)
Я имел ввиду число просто как строку сохранить, а потом просто посчитать сколько букв между точкой/запятой и концом строки.

для этого вроде таблица нужна, или нет? если нет то каким образом считать.

Цитата(Dov @  19.12.2006,  19:53 Найти цитируемый пост)
Ты указал точность: 80 знаков после запятой.

это форма представления числа на экране, если я даже удалю из кода ".80" у меня серовно исход будет тот-же самый.


Цитата(W4FhLF @  19.12.2006,  19:53 Найти цитируемый пост)
Максимальная точность у расширенного формата, там точность достигает 19 знаков после запятой.  

если ты про double тот там тот-же исход, разве что в некоторых случаях слегка другой..

Автор: nickless 19.12.2006, 22:45
Цитата(ressac @ 19.12.2006,  21:15)
для этого вроде таблица нужна, или нет? если нет то каким образом считать.

Tаблица?
Инициализируешь num нулём, смотришь длину строки, в цикле с конца пропускаешь нули, потом увеличиваешь num, пока не найдешь точку, а если не найдешь, значит num = 0...

Автор: ressac 19.12.2006, 22:47
nickless, 
я вообщем не понял, о чём ты говоришь, я наверно просто ещё не знаком с этим, если тебе не трудно преведи малый пример. спасибо..

Автор: nickless 19.12.2006, 23:17
Код

#include <stdio.h>
#include <string.h>
main()
{
    int i, num;
    char str[64];
    printf("Enter the number: "), scanf("%s", str);
    printf("%s\n", str);

    num = 0;
    for (i=strlen(str)-1; i>=0; i--)
    {
        if (str[i] == '0')  // пропускаем нули в конце
            continue;
        if ((str[i] == '.') || (str[i] == '.'))
            break;          // нашли точку/запятую

        num++;

        if (i == 0)
            num = 0;      // не нашли точку/запятую
    }

    printf ("%d\n", num);

    return 0;

Автор: ressac 19.12.2006, 23:36
nickless, 
в принципе понял, я это ещё просто не изучал smile
интересно.
вот только одно не пойму зачем ты тут "i=strlen(str)-1" ставишь минус один?

спасибо smile

Автор: nickless 19.12.2006, 23:45
Цитата(ressac @ 19.12.2006,  22:36)
вот только одно не пойму зачем ты тут "i=strlen(str)-1" ставишь минус один?

Потому что массивы в C(++) с нуля начинаются, первая буква находится в str[0], последняя в str[length-1]

Автор: Dov 20.12.2006, 00:20
Код
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main()
{
    char  buf[81];
    char  num_f[81];
    char* p;
    int   count = 0;

    printf("Enter the number: ");
    scanf("%s", buf);
    sscanf(buf, "%s", num_f);

    if(strchr(num_f, '.'))
    {
        p = num_f + strlen(num_f) - 1;
        while(*p == '0')
            p--;
        while(isdigit(*p--))
            count++;
    }
    printf("\ncount: %d\n", count);

    return 0;
}

Автор: W4FhLF 20.12.2006, 08:56
Код

если ты про double тот там тот-же исход, разве что в некоторых случаях слегка другой..


Тут ещё зависит от разрядности целой части, double является длинным форматом представления float point чисел, где под мантиссу или дробную часть числа отводится 53 байта, что при нулевом значении целой части может дать точность до 16 знаков после запятой.

Автор: ressac 23.12.2006, 00:34
я вернусь к старому , к тому с чего и начал, проблему которую я тут запостил можно разрешить по старинке просто с for'ом? без char'ов и подобных вещей. просто один FOR.

Автор: Anikmar 23.12.2006, 00:59
На мой взгляд нельзя.
Когда ты вводишь цифры с клавиатуры - ты вводишь все равно char, что бы там не говорили.

ты вводишь 0.01 - это символы.

потом среда (программа) преобразует эти цифры в float. Бинарная точность float далеко не всегда совпадает с десятичной. Если ты ввел 0.01 - программа пытается преобразовать это во float с максимальной точностью. получается (это только пример) некое число в формате float. Оно может вглыдеть как 0.009999999999999999999999999999999999.
Если воспользоваться твоим алгоритомом - получишь результат, который ты уже видел.

Следовательно - чисто математическими методами здесь никак не обойтись - все равно будет погрешность.

Резюме:
Я бы задал некоторую точность: допустим программа работает с числами, которые не превышают 10 знаков после запятой. Тогда можно введенное число округлить до этой точности, а потом воспользоваться твоим алгоритмом. 

Я бы использовал метод:
Допустим ввели 120344.04563

Сначала - привел бы число к значению меньше 1 (последовательным делением на 10)
Получил бы результат 0.04563

Потом умножил бы на 10000000000 (до кличества знаков) - это уже можно делать в типе long int
В результате получил бы число
456300000
Дальше можно по алгоритму - с целыми числами он сработает (при некой модификации)

Моя мысля должна работать до предела знаков после запятой, равной длине (long) int...

Если ошибся - не бейте.

Автор: ressac 23.12.2006, 01:26
ты не ошибся вроде бы всё хорошо,только с нулями в конце что будем делать? (я не совсем понял этот момент).

Автор: Anikmar 23.12.2006, 10:16
Незначащие конечные нули - выбрасываем.

Наверное надо сделать цикл вроде
Код

int a = 456300000;
// Это наша дробная часть
while (((a%10) == 0)&&(a>0))
{
   a/= 10; // Пока делится на 10 без остатка - отбрасываем незначащие нули
}

// Теперь a состоит только из значащих цифр - действуем по алгоритму
int Count = 0; // Были только одни нули - количество цифр после запятой 0
while (a>=1)
{
  a /= 10;
  Count ++;
}
// Count - количество знаков после запятой


Что-то в этом роде

Видимо не проснулся - немного подправил

Автор: Dov 23.12.2006, 11:36
Цитата(Anikmar @  22.12.2006,  23:59 Найти цитируемый пост)
Получил бы результат 0.04563
...
Цитата(Anikmar @  22.12.2006,  23:59 Найти цитируемый пост)
В результате получил бы число456300000
...
Цитата(Anikmar @  23.12.2006,  09:16 Найти цитируемый пост)
// Count - количество знаков после запятой
...

По-моему, чего-то не того. 5 != 4.  smile 

Автор: ressac 23.12.2006, 11:55
хорошо только ведь этот блок будет отбирать и нужные нам нули , понимаешь о чём я говорю? 


Код

while (((a%10) == 0)&&(a>0))
{
   a/= 10; // Пока делится на 10 без остатка - отбрасываем незначащие нули
}


тойсть было "0,02" умножили на 100....... получили двойку в конце с кучей нолей и затем их убрали и один нужный нолик нам мы потеряли, в принципе можно совместить твой и мой алгоритм, мой будет считать сколько нолей до первой цифры (это у него получается вроде-бы если я не ошибаюсь) а твой затем будет переносить их на левую часть дроби и считать цифры, вроде должно пройти, но минус есть серовно, максимальное чилсо будет состоять из 10 цифр, ну это лучше чем ничего всё таки


2Dov

я не понял о чём ты. или ты о том-же о чём и я? smile

Автор: Anikmar 23.12.2006, 12:30
На самом деле - если работать чисто с числами там все равно будет куча цифр, которые пользователь не вводил.
Число 56.0475 будет представлено как 56.047499999999999999999999999

Так что все-таки проще разобрать введенную строку и посчитать количество знаков после запятой. Конечные нули можно и обрезать.
Это будет наипростейший и самый правильный выход.

А так, в большинстве случаев будет выдаваться результат, совпадающий с заданной точностью.

Автор: Dov 23.12.2006, 12:35
Цитата(ressac @  23.12.2006,  10:55 Найти цитируемый пост)
2Dov я не понял о чём ты. или ты о том-же о чём и я? 

ressac, я о том, что нужный ноль был утерян не после того, как получили двойку в конце с кучей нолей и затем их убрали, а  после того, как умножили на 100.

Автор: Rockie 23.12.2006, 14:27
Цитата(Anikmar @  23.12.2006,  00:59 Найти цитируемый пост)
Когда ты вводишь цифры с клавиатуры - ты вводишь все равно char, что бы там не говорили.ты вводишь 0.01 - это символы.потом среда (программа) преобразует эти цифры в float.

Вводишь пиксели а они потом там, по проводочкам, преобразуются smile



Автор: Anikmar 23.12.2006, 15:04
Цитата(Rockie @  23.12.2006,  14:27 Найти цитируемый пост)
Вводишь пиксели а они потом там, по проводочкам, преобразуются 


Ну если быть совсем точным, то при нажатии клавиши формируется прерывание 09, обработчик прерывания считывает SCAN-код клавиши, и помещает его в буффер 
Драйвер клавиатуры считывает из буфера этот скан-код, преобразовывает его в код клавиши и отправляет в буфер нажатых клавиш.

Винда берет этот код, посылает активному окну сообщение о нажатии клавиши.

Соответственно обработчик данного сообщения берет код нажатой клавиши и помещает его в сандартный поток ввода.

Несколько упрощенно, но где-то так.

Так вот: в стандартный поток ввода помещается char!  smile  smile 

Автор: Rockie 23.12.2006, 16:37
Цитата(Anikmar @  23.12.2006,  15:04 Найти цитируемый пост)
Так вот: в стандартный поток ввода помещается char!  smile  smile  


Anikmar, то есть я сейчас char считал?
Код
double i;
cin>>i;


Цитата(Anikmar @  23.12.2006,  00:59 Найти цитируемый пост)
Когда ты вводишь цифры с клавиатуры - ты вводишь все равно char, что бы там не говорили.

А если я Ctrl+Alt+Del жмакаю я какой char ввожу?.. 

Клавиатура здесь вообще не при чем, это не единственный способ ввести число.


Автор: ressac 23.12.2006, 20:49
силь больше нет думать smile
погрешности при вычислениях есть и будут...
у кого-то есть идеи получше ?


Код

#include <stdio.h>

main()
{
 int i=1,digits=-1; unsigned long num_w2=0; double num_w,num_f,num_fb;

 printf("Enter the number: ");scanf("%lf",&num_f);
 num_w=num_f;

  //считаем нули после запятой до любого числа ( кроме нуля естествено :) )
  for(;!num_w2;num_w*=10,num_w2=num_w,num_w-=num_w2,digits++)
  {
   if(!num_w2) num_fb=num_w;
   printf("\nS4itaem nuli posle zapyatoy: %lf\n",num_w);
   //system("pause");
  }

  printf("\nNuley posle zaptatoy: %d\n",digits);

  num_w2=num_fb*10e8;
  printf("\nLong int: %d\n",num_w2);

  //убираем нули 
  for(;!(num_w2%10);num_w2/=10)
  {
   printf("\nUberaem nuli: %d\n",num_w2);
   //system("pause");
  }

  //убираем девятки
  for(;num_w2%10==9;num_w2/=10)
  {
   printf("\nUberaem devyatki: %d\n",num_w2);
   //system("pause");
  }

  //считаем то что у нас осталось после всех фильтров
  for(;num_w2;num_w2/=10,digits++)
  {
   printf("\nS4itaem cifri: %d\n",num_w2);
   //system("pause");
  }

  if(num_f==0.9) digits=1; //на тот случай если 0.9

 //и сколько всего цифр у нас :)...
 printf("\nDigits final: %d\n",digits);
system("pause");
}

Автор: Anikmar 23.12.2006, 22:04
Цитата(Rockie @  23.12.2006,  16:37 Найти цитируемый пост)
Anikmar, то есть я сейчас char считал?

код C++
1:
2:

double i;
cin>>i;


Ну вообще-то я в шутку писал  smile В любом случае с клавиатуры ты вводишь те символы, которые нарисованы на кнопках, служебные отрабатываются по-особому, это не суть. Если ты вводишь последовательность символов 123, то с точки зрения клавиатуры - ты ввел три символа, а с точки зрения программы - это может быть и char и byte и int и string и float - смотря какого типа переменную поставишь. Но набрал ты 123 - так что это по-твоему? Число? Строка? Код символа? - Естественно это просто 3 символа. С кодами 49, 50 и 51. Во что их преобразует конкретная программа - это от программиста зависит.

Но на самом деле, если серьезно, когда ты вводишь с клавиатуры символы 345.04567, которые преобрразуются в double, а потом в отладчике посмотришь чему этот double равен - ты увидишь немного отличающееся значение. Это и есть погрешность преобразования десятичного числа в двоичное (коим double собственно и является).

Если в задаче стоит посчитать количество знаков после запятой в десятичном числе - нужно воспользоваться строковым выражением - т.е. преобразовать число в строку с заданным параметром округления.

Если число вводится с клавиатуры - надо сразу его обрабатывать как строку.
Если число формируется программно - точного результата достичь не получится - с этим ничего нельзя сделать, особенности архитектуры.

Тут можно использовать не двоичную систему - а прямую трансляцию десятичной в двоичную (точно не помню, по-моему такое называется BCD, но не уверен). Но в рамках поставленной задачи не уверен, что это необходимо.

Автор: ressac 24.12.2006, 04:09
а можно-ли одновремено сохранить число в строку(чар) и в перменую? в том момент когда мы вводим его? 

Автор: Anikmar 24.12.2006, 10:10
Цитата(ressac @  24.12.2006,  04:09 Найти цитируемый пост)
а можно-ли одновремено сохранить число в строку(чар) и в перменую? в том момент когда мы вводим его?  


Конечно. Вводим в строку, а потом строку преобразовываем в число.

Код

char Str[20];
float Value;

cin>>Str;
Value = atof(Str);


Автор: bsa 24.12.2006, 13:41
у него программа на C. Поэтому в его случае лучше использовать:
Код
char buf[32];
printf("Enter the number: ");
scanf("%s", buf);
sscanf(buf, "%f", &num_f); //или num_f = atof(buf);

Если же надо считать количество значащих разрядов после запятой у любого числа (а не только введенного с клавиатуры), то тут надо делать цикл от 0 до количества разрядов, точность которых гарантирована типом (для флоат кто-то тут написал 6 десятичных разрядов, для double - 19). Все что за пределами точности типа необходимо игнорировать.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)