Библиотека функций для работы с вещественными числами
.docМинистерство образования и науки РФ
Санкт-Петербургский государственный электротехнический университет «ЛЭТИ»
Кафедра МО ЭВМ
Отчет
по результатам производственной практики
Выполнил: студент гр. 3341 Рыжок М.
Проверил: Романцев В. В.
Санкт-Петербург 2006 г.
Отчет по результатам производственной практики.
Место прохождения: филиал ООО «Келман Лимитед»
Сроки прохождения: с 26 июня по 22 июля.
Цель практики: приобрести необходимые знания и навыки в разработке программного обеспечения на языке С.
Краткое описание производственной среды:
Руководитель практики: Пименов Валентин Евдокимович
Руководитель от кафедры: Романцев Вениамин Викторович
Задание на практику: Разработать на языке С библиотеку функций, позволяющую осуществлять вычисления для типа чисел с плавающей запятой, в т. ч. арифметические действия, ввод из строки и преобразование в строковую форму. Тип чисел с плавающей запятой реализовать на базе существующих встроенных в языке С типов int и char.
Проектирование задания
Новый тип данных с плавающей запятой реализуется в виде структуры с помощью оператора преобразования типов typedef. Число с плавающей запятой представляется в памяти как mantiss*2^ exp, где mantiss – целочисленное значение, а exp – символьное. При выполнении умножения чисел с плавающей запятой, их мантиссы (mantiss) умножаются, а порядки (exp) складываются. При делении чисел мантисса делимого делится на мантиссу делителя, а порядок делителя отнимается от порядка делимого. Сложение и вычитание осуществляются следующим образом: сначала порядки слагаемых приводятся к одному значению путем умножения / деления мантиссы числа пополам и соответствующего уменьшения / увеличения порядка. Арифметические действия не выполняются, если в ходе приведения порядков значения мантиссы или порядка превышают установленные для них максимальные значения.
Представление типа чисел с плавающей запятой с помощью строки осуществляется в любой системе счисления последовательным выделением разрядов числа (алгоритм аналогичен переводу чисел в другую систему счисления). Ввод числа при помощи строки выполняется с помощью последовательного умножения значения разряда на основание разряда и сложением с суммой уже имеющихся разрядов.
Функции библиотеки для чисел с плавающей запятой
Имя функции |
Предназначение |
reduction |
Приводит к одному порядку два числа с плавающей запятой |
div |
Делит одно число на другое |
mult |
Умножает одно число на другое |
sub |
Вычитает из первого числа второе |
add |
Прибавляет к первому числу второе |
quotient |
Аналог целочисленного деления. Возвращает целое значение, во сколько раз одно число больше другого |
rest |
Вычисляет остаток от деления чисел с плавающей запятой |
init |
Начальная инициализация целым значением |
dblcmp |
Сравнение двух чисел с плавающей запятой |
mdbcpy |
Копирование числа с плавающей запятой. |
member |
Вспомогательная функция. Проверяет наличие символа в массиве символов |
pos |
Вспомогательная функция. Ищет заданный символ в заданном массиве символов и возвращает его позицию. |
asc2dbl |
Преобразует строку символов в число с плавающей запятой |
dblasc2 mdbasc2 |
Преобразуют число с плавающей запятой в строку символов. |
test_asc2mdb |
Тестирует функцию asc2dbl |
test_add |
Тестирует функцию add |
test_div |
Тестирует функцию div |
test_mult |
Тестирует функцию mult |
Приложение 1.
Текст функций библиотеки.
Файл my_double.h – описание типа
#ifndef MY_DOUBLE_H_
#define MY_DOUBLE_H_
#include <limits.h>
typedef struct my_Double
{
char mantiss;
char exp;
} My_Double;
enum {
//EXP_MAX = CHAR_MAX,
//MANTISS_MAX = INT_MAX
EXP_MAX = CHAR_MAX,
MANTISS_MAX = CHAR_MAX
};
const long MAX_MANTISS = 1000000000;
#endif
1.1. Файл adduction.h
#ifndef ADDUCTION_H
#define ADDUCTION_H
#include "my_double.h"
void reduction(My_Double * example, My_Double * pattern);
//Функция приводит переменные example и pattern к одному значению //порядка
//Возможна потеря точности
//Ограничения на входные параметры :example и pattern не должны быть //пустыми указателями
#endif
1.2 Файл adduction.cpp
#include "my_double.h"
#include <limits.h>
#include <stdio.h>
////////////////////////////////////////////////////
void reduction(My_Double * example, My_Double * example1)
{
if (example == NULL || example1 == NULL)
return;
int stop = 0;
if (example->mantiss == 0)
example->exp = example1->exp;
if(example->exp == example1->exp)
return;
else
{
while ((example->exp != example1->exp) && !stop) //пока не сравняются порядки
{ //или пока не произойдет переполнения или потери точности
if (example->exp > example1->exp)
{
if (((example->mantiss > 0)&&(example->mantiss < MANTISS_MAX / 2)) || ((example->mantiss < 0) && (example->mantiss > -MANTISS_MAX / 2)))
{
example->exp--;
example->mantiss *= 2;
}
else //if (pattern->mantiss / 2 != 0)
{
example1->exp++;
example1->mantiss /= 2;
}
/* else
stop = 1;*/
}
else
{
if (((example1->mantiss < MANTISS_MAX / 2) && (example1->mantiss > 0)) || ((example1->mantiss < 0) && (example1->mantiss > -MANTISS_MAX / 2)))
{
example1->exp--;
example1->mantiss *= 2;
}
else //if (example->mantiss / 2 != 0)
{
example->exp++;
example->mantiss /= 2;
}
}
}
}
}
2.1 Файл mul_div.h
#ifndef MUL_DIV_H_
#define MUL_DIV_H_
#include "my_double.h"
void div(My_Double* dividend, const My_Double* denominator, int * h);
//Функция делит числа типа My_Double как вещественные числа
//Результат деления записывается в первый параметр
//и присваивает h = -1 если происходит переполнение типа
//Ограничения на параметры: dividend и denominator не должны быть пустыми указателями
//и мантисса denominator не должна быть равна 0
void mult(My_Double * A, const My_Double * B, int * h);
//умножает числа типа My_Double как вещественные числа
//Результат умножения записывается в первый параметр
//и присваивает h = -1 если происходит переполнение
//Ограничения на параметры: dividend и denominator не должны быть пустыми указателями
void sub(My_Double * U, const My_Double * V, int * h);
//вычитает числа My_Double одно из другого (*U - *V)
//и присваивает h = -1 если происходит переполнение
//Результат вычитания записывается в первый параметр
//Ограничения на параметры: U и V не должны быть пустыми указателями
void add(My_Double * A, const My_Double * B, int * h);
//прибавляет *В к *А
//и присваивает h = -1 если происходит переполнение
//Результат сложения записывается в первый параметр
//Ограничения на параметры: A и B не должны быть пустыми указателями
void rest(My_Double * A, const My_Double * B);
//находит остаток от деления A на B
//то есть вычитаем В из А пока В не станет больще А
//ограничения на параметры: А и В не должны быть пустыми указателями
//и должны быть положительными
int quotient(My_Double A, My_Double B);
//считает сколько раз В помещается в А (подобие целочисленного деления)
//ограничения на параметры: А и В должны быть одного знака и B не равнр нулю
//возвращаемое значение: -1 если параметры не удовлетворяют условию
//в остальных случаях - неотрицательное частное от деления
#endif
2.2 Файл mul_div.cpp
#include "stdafx.h"
#include <limits.h>
#include <stdlib.h>
#include "my_double.h"
#include "adduction.h"
#include "mdb_manipule.h"
#include "mdb_string.h"
////////////////////////////////////////////////////////////
void div(My_Double * A, const My_Double * B, int * h)
{
if (A == 0 || B == 0 || B->mantiss == 0) //попытка деления на ноль
return; //или замиси в нудквой указатель
My_Double Bcopy(*B);
while ((A->mantiss >= 0 && A->mantiss <= MANTISS_MAX / 2) || (A->mantiss < 0 && A->mantiss <= -MANTISS_MAX / 2))
{
if (A->exp != -EXP_MAX) //приводим делимое к максимально
{ //допустимой мантиссе, чтобы не
A->exp--; //происходило потери точности при
A->mantiss *= 2; //делении
}
else break;
}
while (A->mantiss / Bcopy.mantiss == 0)
{
if (Bcopy.exp != EXP_MAX && Bcopy.mantiss / 2 != 0)
{
Bcopy.exp++;
Bcopy.mantiss /= 2;
}
else break;
}
if (A->exp >= 0 && Bcopy.exp < 0) //возможно переполнение сверху при
if (A->exp < EXP_MAX + Bcopy.exp) //вычитании отрицательного числа из
{ //положительного
A->exp -= Bcopy.exp;
A->mantiss /= Bcopy.mantiss;
}
else {(*h) = -1; return;}
else if (A->exp < 0 && Bcopy.exp >= 0) //возможно переполнение снизу при
if (A->exp > Bcopy.exp - EXP_MAX) //вычитании положительного числа из
{ //отрицательного
A->exp -= Bcopy.exp;
A->mantiss /= Bcopy.mantiss;
}
else {(*h) = -1;return;}
else //при вычитании однознаковых чисел
{ //переполнение маловероятно
A->exp -=Bcopy.exp;
A->mantiss /= Bcopy.mantiss;
}
}
/////////////////////////////////////////////////////////
void mult(My_Double * A, const My_Double * B, int * h)
{
// printf("A.mantiss %d, A.exp %d, B.mantiss %d, B.exp %d\n", A->mantiss, A->exp, B->mantiss, B->exp);
if (A == 0 || B == 0)
return;
My_Double Bcopy(*B);
if (Bcopy.mantiss == 0)
{
A->mantiss = 0;
return;
}
while (abs(A->mantiss) > MANTISS_MAX / abs(Bcopy.mantiss))
{
if (A->exp != EXP_MAX)
{
A->exp++;
A->mantiss /= 2;
}
else break;
}
if (A->exp > 0 && Bcopy.exp > 0)
{
if (A->exp < EXP_MAX - Bcopy.exp)
{
A->mantiss *= Bcopy.mantiss;
A->exp += Bcopy.exp;
}
else
{
while (abs(A->mantiss) <= MANTISS_MAX / abs(Bcopy.mantiss))
{
A->mantiss *= 2;
A->exp--;
}
if (A->exp < EXP_MAX - Bcopy.exp)
{
A->exp += Bcopy.exp;
A->mantiss *= Bcopy.mantiss;
}
else
{
*h = -1;
return;
}
}
}
else if (A->exp < 0 && Bcopy.exp < 0)
{
if (A->exp > -EXP_MAX - Bcopy.exp)
{
A->mantiss *= Bcopy.mantiss;
A->exp += Bcopy.exp;
}
else
{
while (abs(A->mantiss) / 2 != 0)
{
A->exp++;
A->mantiss /= 2;
}
if (A->exp > -EXP_MAX - Bcopy.exp)
{
A->exp += Bcopy.exp;
A->mantiss *= Bcopy.mantiss;
}
else
{
(*h) = -1;
return;
}
}
}
else
{
A->mantiss *= Bcopy. mantiss;
A->exp += Bcopy.exp;
}
}
/////////////////////////////////////////////////
void add(My_Double * A, const My_Double * B, int * h)
{
if (A == NULL || B == NULL)
return;
My_Double Bcopy(*B);
if (A->mantiss == 0)
A->exp = B->exp;
int quarter = 0;
reduction(A, &Bcopy);
if (A->mantiss >= 0) //В какой координатной четверти находится это число?
{
if (Bcopy.mantiss >= 0)
quarter = 1; //1-ая четверть A >= 0, B >= 0
else
quarter = 2; //2-ая четверть A >= 0, B < 0
}
else
if (Bcopy.mantiss < 0)
quarter = 3; //3-я четверть A < 0, B < 0
else
quarter = 4; //4-ая четверть A < 0, B >= 0
switch (quarter)
{
case 1:if (A->mantiss < MANTISS_MAX - Bcopy.mantiss) //переполнение возникает из-за суммы //двух больших полож чисел
A->mantiss += Bcopy.mantiss;
else
{
if (A->exp == EXP_MAX) //если может произойти переполнение порядка
{
*h = -1; //устанавливаем флаг
return; //и прекращаем всякие дальнейшие действия
}
else //в противном случае
{
A->exp++; //увеличиваем порядок
A->mantiss /= 2; //уменьшаем мантиссу
Bcopy.mantiss /= 2; //уменьшаем мантиссу второго слагаемого
A->mantiss += Bcopy.mantiss; //складываем мантиссы
}
}
break;
case 2: if (A->mantiss + Bcopy.mantiss < MANTISS_MAX && A->mantiss + Bcopy.mantiss > -MANTISS_MAX)
A->mantiss += Bcopy.mantiss;
else
{
if (A->exp == EXP_MAX) //если может произойти переполнение порядка
{
*h = -1; //устанавливаем флаг
return; //и прекращаем всякие дальнейшие действия
}
else //в противном случае
{
A->exp++; //увеличиваем порядок
A->mantiss /= 2; //уменьшаем мантиссу
Bcopy.mantiss /= 2; //уменьшаем мантиссу второго слагаемого
A->mantiss += Bcopy.mantiss; //складываем мантиссы
}
}
break;
case 3: if (A->mantiss > -MANTISS_MAX - Bcopy.mantiss) //может произойти переполнение //снизу т е выход за нижнюю границу диапазона целых
A->mantiss += Bcopy.mantiss;
else
{
if (A->exp != EXP_MAX) //Если не происходит переполнения порядка
{
A->exp++; //увеличиваем порядок
A->mantiss /= 2; //уменьшаем мантиссу
Bcopy.mantiss /= 2; //уменьшаем мантиссу второго слагаемого
A->mantiss += Bcopy.mantiss; //складываем мантиссы
}
else //в противном случае
{
*h = -1; //устанавливаем флаг
return; //и прекращаем всякие дальнейшие действия
}
}
break;
case 4: if (A->mantiss + Bcopy.mantiss < MANTISS_MAX
&& A->mantiss + Bcopy.mantiss > -MANTISS_MAX)
A->mantiss += Bcopy.mantiss;
else
{
if (A->exp != EXP_MAX) //Если не происходит переполнения порядка
{
A->exp++; //увеличиваем порядок
A->mantiss /= 2; //уменьшаем мантиссу
Bcopy.mantiss /= 2; //уменьшаем мантиссу второго слагаемого
A->mantiss += Bcopy.mantiss; //складываем мантиссы
}
else
{
*h = -1; //устанавливаем флаг
return; //и прекращаем всякие дальнейшие действия
}
}
break;
default: return;
}
}
///////////////////////////////////////////////////////////////////
void sub(My_Double * U, const My_Double * V, int * h)
{
if (U == NULL || V == NULL)
return;
My_Double Vcopy(*V);
Vcopy.mantiss *= -1;
// printf("U.mantiss %d, U.exp %d\n", U->mantiss, U->exp);
// printf("Vcopy.mantiss %d, Vcopy.exp %d\n", Vcopy.mantiss, Vcopy.exp);
add(U, &Vcopy, h);
}
///////////////////////////////////////////////////////////////////
void rest (My_Double * A, const My_Double * B)
{
if (A == NULL || B == NULL)
return;
int h = 1;
My_Double Acopy(*A);
if (B->mantiss <= 0) //не существует остатка от деления на ноль
return; //а отрицательные числа мы просто не рассматриваем
if (A->mantiss < 0 && B->mantiss > 0) //отрицательные числа можно временно заменить положительными
{
h = 0;
A->mantiss *= -1;
}
while (dblcmp(*A, *B) == 1) //пока уменьшаемое ьольше вычитаемого
{
mdbcpy(&Acopy, A); //копируем уменьшаемое
sub(A, B, &h); //производим вычитание
if (dblcmp(Acopy, *A) == 0) //если уменьшаемое не изменилось значит точность слишком мала и
break; //нужно избежать бесконечного цикла
}
if (dblcmp(*A, *B) == 0) //если уменьшаемое сравнялось с вычитаемым остаток будет равен 0
sub(A, B, &h);
if (h == 0) //восстановим знак минус если им пренебрегли ранее
A->mantiss *= -1;
}
///////////////////////////////////////////////////////////////
int quotient(My_Double A, My_Double B)
{
int q = 0;
int h = 1;
int i = 0;
My_Double Acopy(A);
if (B.mantiss == 0) {
return -1;}
if ((A.mantiss > 0 && B.mantiss < 0) || (A.mantiss < 0 && B.mantiss > 0))
{
return -1;
}
// printf("B.mantiss %d, B.exp %d\n", B.mantiss, B.exp);
while (dblcmp(A, B) == 1)
{
mdbcpy(&Acopy, &A);
sub(&A, &B, &h);
if (dblcmp(Acopy, A) == 0)
break;
else q++;
// printf("Acopy.mantiss %d, Acopy.exp %d\n", Acopy.mantiss, Acopy.exp);
}
if (h == -1)
return h;
h = 1;
if (dblcmp(A, B) == 0)
{
sub(&A, &B, &h);
q++;
}
if (h == -1)
return h;
return q;
}
/////////////////////////////////////////////////////////////////////
3.1 Файл mdb_manipule.h
#ifndef MDB_MANIPULE_H
#define MDB_MANIPULE_H
#include "my_double.h"
void init(int a, My_Double *number);
//Функция инициализирует переменную number целым числом a
//Возможна потеря точности
//Ограничения на входные параметры: number не может быь пустым указателем
int dblcmp(My_Double example1, My_Double example2);
//Функция сравнивает две переменные типа My_Double
//возвращаемое значение: 0, если числа равны, 1, если example1 больше example2
// и -1, если example1 меньше example2
void mdbcpy(My_Double * duplicate, const My_Double * pattern);
//Функция копирует содержимое указателя pattern в duplicate
//ограничения : duplicate и pattern не должны бытьравными NULL
#endif
3.2 Файл mdb_manipule.cpp
#include "my_double.h"
#include "adduction.h"
#include "mdb_string.h"
#include <stdio.h>
/////////////////////////////////////////////////
void init(int a, My_Double *number)
{
if (number == NULL)
return;
int i = 0;
if (a == 0)
{
number->mantiss = 0;
number->exp = 0;
return;
}
number->exp = 0;
while (a > MAX_MANTISS) //Если мантисса превышает максимально допустимое
{ //значение, укорачиваем её, одновременно увеличивая
a = a / 2; //порядок
number->exp++;
}
while(a % 2 == 0) //Мантисса должна быть нечетным числом
{
a = a / 2; //Все степени 2 заносятся в порядок
i++;
}
number->mantiss = a;
number->exp += i;
}
////////////////////////////////////////////////////////////////
int dblcmp(My_Double example, My_Double example2)
{
if (example.mantiss > 0 && example2.mantiss < 0)
return 1;
if (example.mantiss < 0 && example2.mantiss > 0)
return -1;
reduction(&example, &example2);
if (example.mantiss == example2.mantiss)
return 0;
else if (example.mantiss > example2.mantiss)
return 1;
else return -1;
}
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
void mdbcpy(My_Double * duplicate, const My_Double * pattern)
{
if (pattern == NULL || duplicate == NULL)
return;
duplicate->exp = pattern->exp;
duplicate->mantiss = pattern->mantiss;
}
4.1 Файл mdb_string.h
#ifndef MDB_STRING_H
#define MDB_STRING_H
#include "my_double.h"
#include "adduction.h"
int member(char * bank, char symbol, int Radix);
//функция проверяет, является ли переменная symbol элементом массива bank с индексом
//не большим Radix
//Ограничения: Указатель bank не может быть равен NULL и Radix неотрицательно
//Возвращаемое значение: 1 - если такой элемент найден
//0 - если элемент не найден
//-1 - если неправильно были переданы параметры
int pos(char * bank, char symbol, int size);
//Функция ищет символ symbol в массиве символов bank и возвращает его индекс в массиве
//Ограничения на параметры: bank не может быть пустым указателем а size неотрицательно
//Возвращаемое значение : от 0 до size если элемент найден, -1, если элемент не найден, -2 если параметры неправильные
int asc2dbl(My_Double * pDbl, const char * str, int Radix);
//Функция преобразует строку символов str в число типа My_Double в системе счисления
//с основанием Radix
//Ограничения на параметры:
// 1. pDbl - указатель на число типа My_Double - не должен быть пустым
// 2. указатель str тоже не должен быть пустым
// 3. Основание Radix системы счисления должно быть целым числом от 2 до 16
//Возвращаемое значение функции:
// -3 - если параметры не удовлетворяют вышеуказанным ограничениям
// -2 - если в строке встретился символ не используемый в данной системе счисления,
// либо более двух десятичных точек либо более двух символов "-" либо перед
// десятичной точки нет других символов, либо перед минусом есть другие символы
// -1 - если в строке настолько много символов, что при попытке преобразовать их в
// число происходит переполнение данного типа
//Во всех остальных случаях функция возвращает то количество символов,
//которое она использовала для записи из строки в число
int dblasc2(My_Double example, char * str, int size, int Radix);
//Функция формирует строковое представление числа example типа My_Double в системе счисленя
//с основанием Radix и записывает его в строку str, но заполняя приэтом не более чем size символов
//Ограничения на параметры:указатель str не должен быть пустым
//Основание системс счисления numeration должно быть от 2 до 16
//Возвращаемое значение: -1 - если параметры не удовлетворяют условиям
//0 - если заданный размер строки size слишком мал для того,
//чтобы в него можно было записать число с заданной точностью
//Во всех остальных случаях функция возвращает кол - во символов, записанных в строку
//без знака '-' и десятичной точки
int mdbasc2(My_Double example, char * str, int size, int Radix);
//Функция выполняет ту же операцию, что и dblasc2, с теми же ограничениями и возвращаемыми значениями,
//но делает это немножко по-другому
#endif
#ifndef TESTS_H
#define TESTS_H
4.2 Файл mdb_string.cpp
#include "my_double.h"
#include "adduction.h"
#include "mul_div.h"
#include "mdb_manipule.h"