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

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

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

Министерство образования и науки РФ

Санкт-Петербургский государственный электротехнический университет «ЛЭТИ»

Кафедра МО ЭВМ

Отчет

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

Выполнил: студент гр. 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"