Библиотека функций для работы с вещественными числами
.doc#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;
}
