Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Библиотека функций для работы с вещественными числами

.doc
Скачиваний:
10
Добавлен:
01.05.2014
Размер:
314.88 Кб
Скачать

#include <stdlib.h>

#include <stdio.h>

//////////////////////////////////////////////////////////

int dblasc2(My_Double example, char * str, int size, int Radix)

{

char bank[] = {'0', '1', '2', '3', '4', '5', '6', '7',

'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; //банк символов

const int MAX_NUMERATION = sizeof(bank); //максимально возможная степень счисления

const int MAX_FIDELITY = 3; //количество цифр после запятой

int i, j = 0, h = 1, length = 0, sign = 1;

My_Double T, T1, D, Copy(example);

if (str == 0 || Radix < 2 || Radix > MAX_NUMERATION) //нельзя писать в указатель NULL или если система счисления не от 2 до 16

return -1;

init(1, &D);

init(1, &T);

init(Radix, &T1);

if (example.mantiss < 0) //сводим работу с отрицательными числами к работе с положительными

{

sign = 0;

example.mantiss *= -1;

Copy.mantiss *= -1;

}

while (dblcmp(Copy, D) == 1) //идет подсчет количества разрядов в числе example (оно же Copy)

{

div(&Copy, &T1, &h);

length++;

// printf("Copy.mantiss %d, copy.exp %d\n", Copy.mantiss, Copy.exp);

}

if (dblcmp(Copy, D) == 0)

length++;

/* if(dblcmp(Copy, D) == 0)

length++;

if(dblcmp(Copy, D) == 0)

length++; */

// printf("Length %d\n", length);

if (length + 1 + MAX_FIDELITY > size) //если в заданный размер строки число не помещается

return 0; //то запись не происходит

i = 0;

while (i < length - 1) //мы должны получить основание максимального

{ //разряда нашего числа

mult(&T, &T1, &h);

i++;

//printf("T.mantiss %d, T.exp %d\n", T.mantiss, T.exp);

}

while (i >= 0)

{

h = quotient(example, T); //вычисляем цифру текущего разряда

rest(&example, &T); //спускаемся на разряд ниже

if (h >= 0 && h < MAX_NUMERATION) //если символ цифры содержится в банке символов

{

str[j] = bank[h]; //записываем его в строку

j++; //готовимся запсывать на следующую позицию строки

}

div(&T, &T1, &h); //уменьшаем основание т.к. перешли к новому разряду

i--;

}

str[j] = '.'; //целая часть закончилась

j++;

while (i > -MAX_FIDELITY-1) //начинаем дробную часть

{

h = quotient(example, T);

rest(&example, &T);

if (h >= 0 && h < MAX_NUMERATION)

{

str[j] = bank[h];

j++;

}

div(&T, &T1, &h);

i--;

}

str[j] = '\0'; //все, запись кончилась

if (sign == 0) //в зависимости от того, было ли число

{ //example отрицательным

for (i = length + MAX_FIDELITY + 1; i > 0; i--)//мы перезаписываем строку со знаком минус

str[i] = str[i-1];

str[0] = '-';

str[length + MAX_FIDELITY + 2] = '\0';

}

return length + (1 - sign) + 1 + MAX_FIDELITY;

}

//////////////////////////////////////////////////////////

int mdbasc2(My_Double number, char * str, int Radix, int size)

{

char bank[] = {'0', '1', '2', '3', '4', '5', '6', '7',

'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; //банк символов

const int MAX_NUMERATION = sizeof(bank); //максимально возможная степень счисления

const int MAX_FIDELITY = 3; //количество цифр после запятой

My_Double T, //"круглое" число, содержащее с данным числом одинаковое количество разрядов

Q; //основание системы счисления

int h = 0, //флаг показывает, не возникает ли переполнения при вычислениях

length = 0, //количество разрядов (длина записанной строки)

sign = 0, //знак числа: положит. - 0, отриц. - 1

i = 0,

z = 0; //значение текущего разряда

if (Radix < 2 || Radix > MAX_NUMERATION)

return -1;

init(Radix, &Q);

init(1, &T);

if (number.mantiss < 0)

{

sign = 1;

number.mantiss *= -1;

}

while ((dblcmp(number, T) == 1 || dblcmp(number, T) == 0) && h != -1)

{

mult(&T, &Q, &h);

length++;

}

if (h != -1) div(&T, &Q, &h);

// printf("number.mantiss %d, number.exp %d\n", number.mantiss, number.exp);

// printf("T.mantiss %d, T.exp %d\n", T.mantiss, T.exp);

// printf("Length %d\n", length);

if (length + MAX_FIDELITY + 1 > size)

return 0;

while (i < length)

{

z = quotient(number, T);

rest(&number, &T);

str[i] = bank[z];

div(&T, &Q, &h);

i++;

}

if (length == 0)

{

str[0] = '0';

i=1;

length = 1;

}

str[i] = '.';

for (i = length + 1; i < length + MAX_FIDELITY + 1; i++)

{

z = quotient(number, T);

rest(&number, &T);

str[i] = bank[z];

div(&number, &T, &h);

}

str[length + MAX_FIDELITY + 1] = '\0';

return length + MAX_FIDELITY + 1;

}

//////////////////////////////////////////////////////////

int member(char * bank, char symbol, int Radix)

{

if (bank == NULL || Radix < 0)

return -1;

int i, sign = 0;

for (i = 0; i < Radix; i++)

if (*(bank + i) == symbol)

sign = 1;

return sign;

}

///////////////////////////////////////////////////////////

int pos(char * bank, char symbol, int size)

{

if (bank == NULL || size < 0)

return -2;

int i, position = -1;

for (i = 0; i < size; i++)

if (*(bank + i) == symbol)

position = i;

return position;

}

//////////////////////////////////////////////////////////////

int asc2dbl(My_Double * pDbl, const char * str, int Radix)

{

char bank[] = {'0', '1', '2', '3', '4', '5', '6', '7',

'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

int length = 0, i = 0, minus = 0, point = 0, before_point = 0, after_point = 0;

int sign = 0, c, j;

int h = 1;

My_Double T, D, T1, T2;

if (pDbl == 0 || str == 0 || Radix < 2 || Radix > 16) //если параметры не те

return -3;

while (*(str + length) != '\0')

{

if (str[length] == '-' && length != 0) //если минус стоит

return -2; //не на своем месте

if (!member(bank, str[length], Radix) && str[length] != '.' && str[length] != '-' || point >= 2 || minus >= 2 || str[0] == '.')

return -2; //если символы не те или их слишком много

if (str[length] == '.') //подсчет количества точек

point++;

if (str[length] == '-') //подсчет количества минусов

minus++;

length++;

}

if (point >= 2) //если больше одной точки

return -2;

while (before_point < length) //подсчет количества цифр

{ //до и после запятой

if (*(str + before_point) == '.')

break;

before_point++;

}

if (point) //если в запись входит точка

after_point = length - before_point - 1; //или наоборот не входит

else //формулы для вычисления разные

after_point = length - before_point;

if (*str == '-')

before_point--; // не считаем знак "минус" за разряд

init(0, pDbl); //в pDbl записывается число, выражаемое строкой str

init(1, &D);

init(1, &T); //Т - основание текущего разряда (1 или 10 или 100 и. т. д.)

init(1, &T2); //Т2 - будем записывать сюда единицы текущего разряда (от 0 до 9)

init(Radix, &T1); //Т1 - основание системы счисления

if (*str != '-') //если первый символ в строке не '-'

i = 0; //считаем разряды с него

else //в противном случае

{

i = 1; //начинаем считать разряды со второго символа

sign = 1; //устанавливаем флаг

}

for (j = 0; j < before_point - i - 1 + sign; j++) //подсчитаем основание максимального разряда (единицы, десятки, сотни тысяч...)

mult(&T, &T1, &h);

while (*(str + i) != '\0') //пока не дойдем до конца строки

{

if (*(str + i) != '.') //если символ не является точкой

{

mdbcpy(&D, &T); //копируем основание текущего разряда

c = pos(bank, *(str + i), sizeof(bank)); //вычисляем единицы текущего разряда

init(c, &T2); //представляем единицы текущего разряда в типе My_Double

mult(&D, &T2, &h); //умножаем единицы на основание текущего разряда //(получится например 3000 или 2*9^4)

add(pDbl, &D, &h); //добавляем полученный разряд в тип My_Double

if (h == -1) //если происходит переполнение

return h; //никаких действий больше не совершаем

div(&T, &T1, &h); //переходим к следующему разряду

}

i++;

}

if (sign) //в зависимости от наличия/отсутствия в строке первого символа "-"

pDbl->mantiss *= -1; //умножаем -1 на полученное число

return length; //вернем количество использованных символов

}

/////////////////////////////////////////////////////////

5.1 Файл tests.h

int test_asc2mdb();

//функция тестирует функцию asc2dbl()

//проверяются ситуации:запись строки "1.0" в число типа My_Double,

//попытка переполнения типа при вводе слишком большой строки

//попытка записать строку с недопустимыми символомами в данной системе счисления

//попытка зписать строку с двумя десятичными точками

//попытка записать строку с символом минус после цифры или после десятичной точки

//попытка записать строку с несколькими симвлоами "-"

//попытка использования функции с неправильными параметрами

int test_add();

//функция тестирует функцию add()

//Проверяются ситуации: 1+(-1), 1+(-0.001)

//INT_MAX*2^10 + INT_MAX*2^10, переполнение мантиссы сверху

//-INT_MAX*2^2 - INT_MAX*2^2, переполнение мантиссы снизу

//INT_MAX*2 - INT_MAX/2

//INT_MAX*2^INT_MAX + 1; переполнение порядка сверху

//-INT_MAX*2^INT_MAX-1 переполнение порядка снизу

//попытка записать что-нибудь в указатель NULL

int test_div();

//функция тестирует функцию div()

//Проверяются ситуации: 1/2 = 0.5

//INT_MAX*2 / (INT_MAX/2) = 4; переполнение мантиссы

//(INT_MAX*2^INT_MAX)/(1/2) переполнение порядка сверху

//2^(-INT_MAX)/2 переполнение порядка снизу

//(1/12)/0 попытка деления на ноль

//попытка записи в нулевой указатель

int test_mult();

//функция тестирует функцию mult()

//Проверяются ситуации: -2*24 = -48

//2^INT_MAX * 2 переполнение порядка сверху

//2^(-INT_MAX) * 1/2 переполнение порядка снизу

//16*0 = 0;

//попытка записать что-либо в нулевой указатель

#endif

5. Файл tests.cpp

#include <limits.h>

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include "my_double.h"

#include "mdb_manipule.h"

#include "mdb_string.h"

#include "mul_div.h"

int test_asc2mdb()

{

char str1[4] = "1.0",

str2[12] = "-0.01",

str3[22] = "2147483644",

str4[30] = "-73424734576352654572766342.0",

str5[8] = "19443.0",

str6[6] = "1.1.1",

str7[6] = "5-322",

str8[9] = "-3-60-56",

str9[10] = ".-3218444";

My_Double rabbit, example;

enum {

OVER_FLOW = -1,

WRONG_SYMBOL = -2,

WRONG_PARAMETER = -3

};

int z, i = 0;

init(1, &example);

z = asc2dbl(&rabbit, str1, 10);

if (z != 3 || dblcmp(rabbit, example) != 0) //абсолютно нормальная сиуация

return 0;

char * superstr = "10000000000000000000000000000000000000000000000000000000000000000000000000000000";

z = asc2dbl(&rabbit, superstr, 10);

if (z != -1)

return 0;

z = asc2dbl(&rabbit, str5, 9); //недопустимый символ в этой системе счисления

if (z != WRONG_SYMBOL)

return 0;

z = asc2dbl(&rabbit, str6, 2); //две десятичные точки

if (z != WRONG_SYMBOL)

return 0;

z = asc2dbl(&rabbit, str7, 6); //минус в неподобающем ему месте

if (z != WRONG_SYMBOL)

return 0;

z = asc2dbl(&rabbit, str8, 9); //два знака минус

if (z != WRONG_SYMBOL)

return 0;

z = asc2dbl(&rabbit, str9, 11); //недопустимое сочетание символов

if (z != WRONG_SYMBOL)

return 0;

z = asc2dbl(&rabbit, "6 86r;", 16); //совершенно левые символы

if (z != WRONG_SYMBOL)

return 0;

z = asc2dbl(&rabbit, "34623", 1); //недопустимо малый параметр системы счисления

if (z != WRONG_PARAMETER)

return 0;

z = asc2dbl(&rabbit, "e343231", 17); //недопустимо большой параметр системы счисления

if (z != WRONG_PARAMETER)

return 0;

z = asc2dbl(0, "1", 12); //нельзя передавать нулевой указатель в качестве числа

if (z != WRONG_PARAMETER)

return 0;

z = asc2dbl(&rabbit, 0, 12); //нельзя передавать нулевой указатель в качестве строки

if (z != WRONG_PARAMETER)

return 0;

return 1;

}

///////////////////////////////////////////////////////

int test_add()

{

My_Double A, B, C;

int h = 1;

init(1, &A); //сумма 1 + (-1)

init(-1, &B);

add(&A, &B, &h);

if(A.mantiss != 0)

return 0;

init(1, &A); //сумма 1 + (-1/1000)

B.mantiss = -100;

B.exp = -3;

C.mantiss = 999;

C.exp = -3;

add(&A, &B, &h);

if (!dblcmp(A, C))

return 0;

A.exp = 10; //М*2^10 + М*2^10 = М*2^11

A.mantiss = MANTISS_MAX; //переполнение сверхбольшими числами

B.exp = 10;

B.mantiss = MANTISS_MAX;

C.exp = 11;

C.mantiss = MANTISS_MAX;

add(&A, &B, &h);

if (!dblcmp(A, C))

return 0;

A.exp = 2; //-M*2^2 -М*2^2 = -М*2^3

A.mantiss = -MANTISS_MAX; //переполнение сверхбольшими

B.exp = 2; //отрицательными числами

B.mantiss = -MANTISS_MAX;

C.exp = 3;

C.mantiss = -MANTISS_MAX;

add(&A, &B, &h);

if (!dblcmp(A, C))

return 0;

A.exp = 1;

A.mantiss = MANTISS_MAX;

B.exp = -1;

B.mantiss = MANTISS_MAX;

C.exp = 2;

C.mantiss = 5 * (INT_MAX / 8);

add(&A, &B, &h);

if (!dblcmp(A, C))

return 0;

A.exp = EXP_MAX; //сверхбольшое число, к которому

A.mantiss = MANTISS_MAX; //ничего нельзя прибавить

B.exp = -EXP_MAX;

B.mantiss = MANTISS_MAX;

C.exp = EXP_MAX;

C.mantiss = MANTISS_MAX;

add(&A, &B, &h);

if (A.exp != C.exp || A.mantiss != C.mantiss)

return 0;

A.exp = EXP_MAX; //число, от которого уже ничего нельзя отнять

A.mantiss = -MANTISS_MAX;

B.exp = EXP_MAX;

B.mantiss = -MANTISS_MAX;

C.exp = EXP_MAX;

C.mantiss = -MANTISS_MAX;

add(&A, &B, &h);

if(A.exp != C.exp || A.mantiss != C.mantiss)

return 0;

My_Double * A1 = NULL; //попытка записать что-то в указатель NULL

My_Double * B1 = NULL;

add(A1, B1, &h);

if (A1 != NULL || B1 != NULL)

return 0;

return 1;

}

//////////////////////////////////////////////////////////

int test_div()

{

My_Double A, B, C;

int h = 1;

A.exp = 0; //1/2 = 0.5 = 1*2^(-1)

A.mantiss = 1;

B.exp = 0;

B.mantiss = 2;

C.exp = -1;

C.mantiss = 1;

div(&A, &B, &h);

if (dblcmp(A, C) != 0)

return 0;

A.exp = 1; //M*2/(M/2) = M*2^2

A.mantiss = MANTISS_MAX;

B.exp = -1;

B.mantiss = MANTISS_MAX;

C.exp = 2;

C.mantiss = 1;

div (&A, &B, &h);

if (A.exp != C.exp || A.mantiss != C.mantiss)

return 0;

A.exp = EXP_MAX; //наибольшее возможное число

A.mantiss = MANTISS_MAX; //которое уже нельзя увеличить

B.exp = -1;

B.mantiss = -1;

div(&A, &B, &h);

if (A.mantiss != MANTISS_MAX || A.exp != EXP_MAX)

return 0;

A.exp = -EXP_MAX; //наименьшее возможное число,

A.mantiss = 1; //которое уже нельзя уменьшить

B.exp = 1;

B.mantiss = 1;

div(&A, &B, &h);

if (A.mantiss != 1 || A.exp != -EXP_MAX)

return 0;

A.exp = 1; //попытка деления на ноль

A.mantiss = 12;

B.exp = 1;

B.mantiss = 0;

div(&A, &B, &h);

if (A.mantiss != 12 || A.exp != 1)

return 0;

My_Double * A1 = NULL; //попытка записать что-то в указатель NULL

My_Double * B1 = NULL;

div(A1, B1, &h);

if (A1 != NULL || B1 != NULL)

return 0;

return 1;

}

////////////////////////////////////////////////////////////

int test_mult()

{

My_Double A, B, C;

int h = 1;

A.exp = 1; //простая проверка с небольшими числами

A.mantiss = -1;

B.exp = 3;

B.mantiss = 3;

C.exp = 4;

C.mantiss = 3;

mult(&A, &B, &h);

if (!dblcmp(A, C))

return 0;

A.exp = EXP_MAX; //случай когда при умножении происходит переполнение порядка

A.mantiss = MANTISS_MAX; //чересчур большой множитель

B.exp = 1;

B.mantiss = 1;

C.exp = A.exp;

C.mantiss = A.mantiss;

mult(&A, &B, &h);

if (A.exp != C.exp || A.mantiss != C.mantiss)

return 0;

A.exp = -EXP_MAX; //случай когда при умножении происходит преполнение порядка

A.mantiss = 1; //слишком маленький множитель

B.exp = -1;

B.mantiss = 1;

C.exp = A.exp;

C.mantiss = A.mantiss;

mult(&A, &B, &h);

if (A.exp != C.exp || A.mantiss != C.mantiss)

return 0;

A.exp = 2; //умножение на ноль

A.mantiss = 3;

B.exp = 1;

B.mantiss = 0;

mult(&A, &B, &h);

if (A.mantiss != 0)

return 0;

My_Double * A1 = NULL; //попытка записать что-то в указатель NULL

My_Double * B1 = NULL;

mult(A1, B1, &h);

if (A1 != NULL || B1 != NULL)

return 0;

return 1;

}