Форум программистов, компьютерный форум CyberForum.ru

Компилятор сломался или ошибка? - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 16, средняя оценка - 4.75
gmb124
8 / 4 / 1
Регистрация: 11.04.2012
Сообщений: 54
26.07.2012, 17:08     Компилятор сломался или ошибка? #1
Требуется написать программу, которая делает перевод вещественного числа в строку.
Возникает ошибка во время получения дробной части числа.
Например:

C++
1
2
3
double f = 12.23;
int whole = (int) f; //=12
double real = f - whole;  // должно быть 0.23 но может получиться = 0.2300000012
Почему так и как это можно исправить?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//обращение строки
void RevStr(char *str){
    char *start,*end;
    char t;
    
    start = str;
    end = &str[strlen(str) -1];
 
    while(end > start){
        t = *end;
        *end = *start;
        *start = t;
        start++; end--;
    }
}
 
//перевод вещественного числа в строку
char * ConvertToString(double f){
    char result[80];
    int whole;
    double real;
    
    whole = (int) f;    //переменная для целой части
    real = f - whole;   //переменная для вещественной части
    
    int i = 0;
    
    //целую часть заносим в строку в обратном порядке
    do{
        result[i] = (whole % 10) + '0';
        i++;
    }while(whole /= 10);
    
    result[i] = 0;
    
    //развернём строку
    RevStr(result);
 
    result[i++] = '.';
    
 
    if(real == 0){
        result[i] = result[i+1] = '0';
        result[i+2] = 0;
        return result;
    }
    
    int serv;
 
 
    //заносим в строку вещественную часть
    while(real){
        serv = (int)(real * 10);
        result[i] = serv + '0';
        real = real * 10 - serv;
        i++;
    }
 
    result[i] = 0;
    return result;
}
Лучшие ответы (1)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Catstail
Модератор
 Аватар для Catstail
21436 / 10221 / 1666
Регистрация: 12.02.2012
Сообщений: 17,096
26.07.2012, 17:35     Компилятор сломался или ошибка? #2
Вещественные числа хранятся и обрабатываются с погрешностью. И, поскольку, хранятся они в двоичном виде, вполне может оказаться, что конечная десятичная дробь будет двоичной периодической.
gmb124
8 / 4 / 1
Регистрация: 11.04.2012
Сообщений: 54
26.07.2012, 17:37  [ТС]     Компилятор сломался или ошибка? #3
Каким способом можно избежать этих ошибок?
Catstail
Модератор
 Аватар для Catstail
21436 / 10221 / 1666
Регистрация: 12.02.2012
Сообщений: 17,096
26.07.2012, 17:45     Компилятор сломался или ошибка? #4
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Некоторых ошибок подобного класса избежать невозможно. Но кое-какие рекомендации дать можно:

1) не используйте float, используйте double (погрешность будет меньше, но не исчезнет);
2) при вычислениях выражения преобразуйте так, чтобы избежать вычитания близких чисел (особенно плохо, если эта разность потом многократно умножается на что-либо; в этом случае ошибка может накопиться и стать огромной).
gmb124
8 / 4 / 1
Регистрация: 11.04.2012
Сообщений: 54
26.07.2012, 17:57  [ТС]     Компилятор сломался или ошибка? #5
Вот смотрите, если int занимает 4 байта, то в моём коде лучше взять float или double?
Catstail
Модератор
 Аватар для Catstail
21436 / 10221 / 1666
Регистрация: 12.02.2012
Сообщений: 17,096
26.07.2012, 18:04     Компилятор сломался или ошибка? #6
Э... А при чем здесь int? Целая часть числа float и, тем более, double, может быть такой, что никакого int не хватит. Пример: число Авогадро =~ 6.02*1023= 602000000000000000000000
Поэтому переводить числа с плавающей точкой нужно по-другому (без int-посредников).
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
26.07.2012, 20:02     Компилятор сломался или ошибка? #7
Цитата Сообщение от gmb124
Требуется написать программу, которая делает перевод вещественного числа в строку.

Почему так и как это можно исправить?
За подробностями представления вещественных чисел — google://IEEE 754. Там всё не так просто.
Почему: потому что точно с помощью float/double могут быть представлены только некоторые десятичные дроби.
Как исправить: никак. Можно только не использовать числа с плавающей точкой для точных вычислений.
За дальнейшими подробностями о том, как бороться с ошибками округления — Д. Кнут, Искусство программирования, том 2, глава 4, раздел 2.
Цитата Сообщение от gmb124
Каким способом можно избежать этих ошибок?
Не городить велосипеды, коли не умеем их проектировать, а написать нечто подобное:
C++
1
2
3
4
5
6
#include <stdio.h>
char* double2str(char *str, double num)
{
  sprintf(str, "%f", num);
  return str;
}
fasked
Эксперт C++
 Аватар для fasked
4924 / 2504 / 180
Регистрация: 07.10.2009
Сообщений: 4,306
Записей в блоге: 1
26.07.2012, 20:16     Компилятор сломался или ошибка? #8
Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
Не городить велосипеды, коли не умеем их проектировать, а написать нечто подобное:
Ручки бы вам поотрывать за такие велосипеды
Используйте stringstream.
gmb124
8 / 4 / 1
Регистрация: 11.04.2012
Сообщений: 54
26.07.2012, 21:10  [ТС]     Компилятор сломался или ошибка? #9
Я согласен, можно конечно использовать готовые средства. Но цель данной темы разобраться, как написать функцию, которая переводит числа вещественное в строку. Есть идеи?
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 320
Регистрация: 30.03.2009
Сообщений: 14,125
Записей в блоге: 26
26.07.2012, 21:36     Компилятор сломался или ошибка? #10
Если вдруг интересено
http://www.cyberforum.ru/blogs/18334/blog88.html
Catstail
Модератор
 Аватар для Catstail
21436 / 10221 / 1666
Регистрация: 12.02.2012
Сообщений: 17,096
26.07.2012, 22:37     Компилятор сломался или ошибка? #11
Те, кто не хочет "изобретать велосипеды", никогда не изобретут ничего путного. Если интересно, как преобразовать число с плавающей точкой в строку, то вот:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include "math.h"
#include "iostream.h"
 
char * Dbl2Str(double x)
{
    char * buf = new char[24];
    int  p10,i,n,q=100;
 
    for (i=0; i<24; i++) buf[i]=0;
 
    buf[0]='0';
    buf[1]='.';
 
    p10=(int)log10(x); // äåñÿòè÷Г*ûé ïîðÿäîê
 
    // äåñÿòè÷Г*Г*Гї Г*îðìГ*ëèçГ*öèÿ Г¬Г*Г*ГІГЁГ±Г±Г»
 
    if (p10 > 1)
    {   p10++;
        for (i=1; i<=p10; i++) x=x/10.0;
    }
    
    if (p10 < -1)
        for (i=1; i<=abs(p10); i++) x=x*10.0;
 
    // Ïîëó÷Г*ГҐГ¬ Г¬Г*Г*ГІГЁГ±Г±Гі
 
    for (i=1; i<=15; i++)
    {
        x=x*10.0;
        n=(int)x;
        x=x-n;
        buf[i+1]='0'+n;
 
    }
    
    buf[17]='D';
 
    // Г§Г*Г*ГЄ ïîðÿäêГ*
 
    if (p10 < 0)
        buf[18]='-';
    else
        buf[18]='+';
 
    p10=abs(p10);
 
    // ïîðÿäîê
 
    for (i=1; i<=3; i++)
    {
        n=p10/q;
        buf[18+i]='0'+n;
        p10=p10%q;
        q=q/10;
    }
 
    return buf;
 
}
 
int main(int argc, char* argv[])
{
 
    char *S1=Dbl2Str(0.00012345789);
    char *S2=Dbl2Str(663663.88958);
 
    cout << S1 << endl;
    cout << S2 << endl;
 
    delete [] S1;
    delete [] S2;
 
    return 0;
}
Функция переводит в строковый вид только положительные числа (настучал на скорую руку). Перевод отрицательных оставляю интересующимся в качестве упражнения.
kravam
26.07.2012, 23:00
  #12

Не по теме:

Сдаётся мне, вещественные числа представлены в памяти компьютера однозначно. Неоднозначен их вывод. Почему- не знаю.

Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 320
Регистрация: 30.03.2009
Сообщений: 14,125
Записей в блоге: 26
26.07.2012, 23:28     Компилятор сломался или ошибка? #13
Цитата Сообщение от kravam Посмотреть сообщение
Почему- не знаю
Как по твоему должна выглядеть печать числа 1/3 в десятичном виде?
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,268
27.07.2012, 00:05     Компилятор сломался или ошибка? #14
0,(3)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 320
Регистрация: 30.03.2009
Сообщений: 14,125
Записей в блоге: 26
27.07.2012, 08:18     Компилятор сломался или ошибка? #15
Цитата Сообщение от kravam Посмотреть сообщение
0,(3)
Всё-таки у тебя отсутствует воображение. Тогда переформулирую вопрос. Как по твоему должна выглядеть печать числа 1/7 в десятичном виде?
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,268
27.07.2012, 10:06     Компилятор сломался или ошибка? #16
Цитата Сообщение от Evg Посмотреть сообщение
Всё-таки у тебя отсутствует воображение


Цитата Сообщение от Evg Посмотреть сообщение
Как по твоему должна выглядеть печать числа 1/7 в десятичном виде?
0.142857
fasked
Эксперт C++
 Аватар для fasked
4924 / 2504 / 180
Регистрация: 07.10.2009
Сообщений: 4,306
Записей в блоге: 1
27.07.2012, 10:44     Компилятор сломался или ошибка? #17
Цитата Сообщение от kravam Посмотреть сообщение
0.142857
Всего 6 знаков?
golatin
259 / 216 / 38
Регистрация: 12.10.2011
Сообщений: 311
Завершенные тесты: 1
27.07.2012, 10:59     Компилятор сломался или ошибка? #18
1/7=0,(001)2
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,268
27.07.2012, 11:33     Компилятор сломался или ошибка? #19
Цитата Сообщение от fasked Посмотреть сообщение
Всего 6 знаков?
ну у меня больше не выводит если
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
27.07.2012, 15:43     Компилятор сломался или ошибка?
Еще ссылки по теме:

C++ Или я дурак, или компилятор смеется, вот только чувствую я дурак)
C++ Компилятор Visual C++ платный или нет
C++ Компилятор не дает написать main без int. Ошибка в книге, или в С так можно?

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 320
Регистрация: 30.03.2009
Сообщений: 14,125
Записей в блоге: 26
27.07.2012, 15:43     Компилятор сломался или ошибка? #20
kravam, опять фантазии нет. Тогда двигаемся дальше

C
#include <stdio.h>
 
int main (void)
{
  double d = 1.0/7.0;
 
  printf ("%.5f\n", d);
  printf ("%.6f\n", d);
  printf ("%.7f\n", d);
 
  return 0;
}
напечатает

Bash
0.14286
0.142857
0.1428571
Попробуй вычислить на листочке (столбиком) значение 1/7 и затем попробуй ответить на свой собственный вопрос:

Цитата Сообщение от kravam Посмотреть сообщение
Неоднозначен их вывод. Почему- не знаю.
Yandex
Объявления
27.07.2012, 15:43     Компилятор сломался или ошибка?
Ответ Создать тему
Опции темы

Текущее время: 21:51. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru