短篇小说。我制作了一个对二进制整数进行加法的程序。我需要使其适用于二进制实数(例如1010.1010(binary)= 10.625(decimal) 输入以二进制字符串形式给出。 我做了很多尝试,却找不到简单的方法来做。请帮助创建这样的程序。
示例:{输入:1010.1010(十进制为10.625)0.1(十进制为0.5) 输出:1011.001(十进制的11.125)} 代码:
#include <stdio.h>
#include <string.h>
void bin_add(int c[400], int d[400])
{
int car[400]; //carry
int i = 199;
car[i] = 0;
while (i >= 0)
{
//find carry and shift it left
//find the sum
car[i - 1] = (c[i] & d[i]) | (c[i] & car[i]) | (d[i] & car[i]);
c[i] = (c[i] ^ d[i]) ^ car[i];
printf("car[i-1]=%d c[i]=%d\n", car[i - 1], c[i]);
i--;
}
// printf("\n");
}
int main()
{
int l, l1, i;//l and l1 are lengths
char a[200], b[200]; //a and b are the inputs
int c[200], d[200]; //c and d are used for processing
for (i = 0; i < 200; i++)
{
c[i] = 0;
d[i] = 0;
}
gets(a);
gets(b);
l = strlen(a);
l1 = strlen(b);
for (int i = 0; i < l; i++)
{
c[200 - l + i] = a[i] - 48;
}
////////////////////////////////////////////
for (int i = 0; i < l1; i++)
{
d[200 - l1 + i] = b[i] - 48;
}
////////////////////////////////
bin_add(c, d);
for (i = 0; i < 200; i++)
printf("%d", c[i]);
return 0;
}
答案 0 :(得分:2)
您真正想做的是按重要性递增的顺序处理每个数字。为了简化操作,您应该实现以下功能:
/* Return the number of fractional bits in bs */
int bs_fractbits(const char *bs);
/* Return the number of integer bits in bs */
int bs_intbits(const char *bs);
/* Return the bit in bs corresponding to value 2**i,
0 if outside the bit string */
int bs_bit(const char *bs, int i);
/* Return -1 if bs is negative,
0 if bs is zero or NULL,
+1 if bs is positive */
int bs_sign(const char *bs);
/* Return -1 if bs1 < bs2,
0 if bs1 == bs2,
+1 if bs1 > bs2. */
int bs_cmp(const char *bs1, const char *bs2);
要支持负值,您需要实现加法和减法(“无符号”位字符串):
加法:结果的小数位与具有最多小数位的项一样多,并且可能比具有最多整数位的项多一个整数位。从任一术语中的最低有效位开始,然后逐步上升至任一术语中的最高有效位,将每一位相加,并保持“进位”,就像执行by hand一样。如果最后的进位非零,您将再得到一位。
减法:始终从大中减去小。如果那改变了条款的顺序,则取反结果。结果最多具有与具有最多小数位的项一样多的小数位,并且具有与具有最多整数位的项一样多的整数位。这与加法一样,只是减去了位,而使用了“借位”代替“进位”。因为您要从较大的无符号值中减去较小的无符号值,所以“借位”最后将为零。
乘法:整数部分具有整数个的位数以及小数位数的数量,如各项的总和(总和)一样。您可以像将两个无符号整数值相乘一样实现该操作,只需在最后插入该位即可。 (因此,结果的分数位数与输入项的总数相同。)这通常涉及一个双循环,就像手动操作long multiplication一样。
请注意,如果您使用更大的基数而不是2,则同样的逻辑也适用。“进位” /“借位”是一个数字,比基数小零至一。
我个人很想使用一种结构来描述每个数字字符串:
typedef struct {
int idigits; /* Number of integral digits before point */
int fdigits; /* Number of fractional digits after point */
int size; /* Number of chars dynamically allocated at data */
char *point; /* Location of decimal point */
char *data; /* Dynamically allocated buffer */
} digitstring;
#define DIGITSTRING_INIT { 0, 0, 0, NULL, NULL }
如果要支持负数字字符串,则带有附加标志。
数值为 D × B i 的数字 D ,其中 B 是基数(使用的唯一数字的位数), i 是所述数字的位置,如果point[-i]
(和i < 0
)位于-i <= fdigits
,或者如果point[-i-1]
(和i >= 0
)在i < idigits
处。 point[0]
本身就是小数点所在的位置(如果有的话)。
例如,如果我们有字符串0100.00
,则idigits = 4
,fdigits = 2
,唯一的非零数字位于位置2
。 (位置0位于小数点的左侧,位置-1位于小数点的左侧。)
size
和data
字段允许重新使用动态分配的缓冲区。由于没有初始化函数,因此必须初始化数字字符串的每个声明digitstring value = DIGITSTRING_INIT;
。这样,您不太可能泄漏内存(除非您在不再需要时忘记释放数字串):
/* Free the specified digit string. */
static inline void digitstring_free(digitstring *ds)
{
if (ds) {
if (ds->data)
free(ds->data);
ds->idigits = 0;
ds->fdigits = 0;
ds->size = 0;
ds->point = NULL;
ds->data = NULL;
}
}
要将数字字符串用作C字符串,请使用辅助函数来获取指向数字字符串中最高有效数字的指针:
/* Return a pointer to a printable version of the digit string. */
static const char *digitstring_str(const digitstring *ds, const char *none)
{
if (ds && ds->point)
return (const char *)(ds->point - ds->idigits);
else
return none;
}
我发现传递一个额外的参数而不是崩溃通常是有用的,该参数仅在返回值未定义的情况下才用于返回值。例如,如果您有一个没有任何内容的初始化数字字符串foo
,则digitstring_str(&foo, "0")
返回字符串文字"0"
。
数字字符串结构的要点是具有访问器功能,该功能可以获取并设置每个数字:
/* Get the value of a specific digit. */
static inline unsigned int digitstring_get(const digitstring *ds, const int position)
{
if (ds) {
if (position < 0) {
if (-position <= ds->fdigits)
return digit_to_value(ds->point[-position]);
else
return 0;
} else {
if (position < ds->idigits)
return digit_to_value(ds->point[-position-1]);
else
return 0;
}
} else
return 0;
}
/* Set the value of a specific digit. */
static inline void digitstring_set(digitstring *ds, const int position, const unsigned int value)
{
if (!ds) {
fprintf(stderr, "digitstring_set(): NULL digitstring specified.\n");
exit(EXIT_FAILURE);
} else
if (position < 0) {
if (-position > ds->fdigits) {
fprintf(stderr, "digitstring_set(): Digit position underflow (in fractional part).\n");
exit(EXIT_FAILURE);
}
ds->point[-position] = value_to_digit(value);
} else {
if (position >= ds->idigits) {
fprintf(stderr, "digitstring_set(): Digit position overflow (in integer part).\n");
exit(EXIT_FAILURE);
}
ds->point[-position-1] = value_to_digit(value);
}
}
上面,value_to_digit()
是一个辅助函数,它将数字值转换为相应的字符,而digit_to_value()
是将字符转换为相应的数值。
所有操作(从解析到算术运算符)实际上都需要一个“构造函数”,该构造函数将创建一个具有足够位数的新数字字符串。 (对于每个操作,位数是预先知道的,并且仅取决于术语中的有效位数。)为此,我创建了一个构造所需大小零的函数:
/* Clear the specified digit string to zero. */
static inline void digitstring_zero(digitstring *ds, int idigits, int fdigits)
{
int size;
char *data;
if (!ds) {
fprintf(stderr, "digitstring_zero(): No digitstring specified.\n");
exit(EXIT_FAILURE);
}
/* Require at least one integral digit. */
if (idigits < 1)
idigits = 1;
if (fdigits < 0)
fdigits = 0;
/* Total number of chars needed, including decimal point
and string-terminating nul char. */
size = idigits + 1 + fdigits + 1;
/* Check if dynamically allocated buffer needs resizing. */
if (ds->size < size) {
if (ds->data)
data = realloc(ds->data, size);
else
data = malloc(size);
if (!data) {
fprintf(stderr, "digitstring_zero(): Out of memory.\n");
exit(EXIT_FAILURE);
}
ds->data = data;
ds->size = size;
} else {
data = ds->data;
size = ds->size;
}
/* Fill it with zeroes. */
memset(data, value_to_digit(0), idigits + 1 + fdigits);
/* Pad the unused space with nul chars, terminating the string. */
memset(data + idigits + 1 + fdigits, '\0', size - idigits - 1 - fdigits);
/* Assign the decimal point. */
ds->point = data + idigits;
/* If there are no decimals, no need for a decimal point either. */
if (fdigits > 0)
ds->point[0] = decimal_point;
else
ds->point[0] = '\0';
/* After setting the desired digits, use digitstring_trim(). */
ds->idigits = idigits;
ds->fdigits = fdigits;
}
这将确保数字字符串为指定的数字位数留有足够的空间,并在必要时重新分配其动态分配的缓冲区,如果已经足够大则重新使用它。
这个想法是,要执行一个运算,您首先要找到结果可以具有的最大整数和小数位数。您可以使用上述代码创建结果数字字符串,然后使用digitstring_set()
将每个数字设置为其各自的值。通常,您将以递增的数字重要性来操作,这意味着递增的数字“位置”。
如果我们还有其他辅助函数int digits(const char *src)
,则返回从src
开始的连续有效数字字符的数目;如果int decimal_points(const char *src)
指向src
,则返回1。小数点,否则为0,我们可以使用
/* Parse a string into a digit string, returning the pointer
to the first unparsed character, or NULL if an error occurs. */
static const char *digitstring_parse(digitstring *ds, const char *src)
{
const int zero = value_to_digit(0);
const char *idigit, *fdigit;
int idigits, fdigits, fextra, n;
/* Fail if nothing to parse. */
if (!src)
return NULL;
/* Skip leading whitespace. */
while (isspace((unsigned char)(*src)))
src++;
/* Fail if nothing to parse. */
if (*src == '\0')
return NULL;
/* Scan integer digits. */
idigit = src;
src += digits(src);
idigits = (int)(src - idigit);
/* Decimal point? */
fextra = 0;
n = decimal_points(src);
if (n > 0) {
src += n;
/* Scan fractional digits. */
fdigit = src;
src += digits(src);
fdigits = (int)(src - fdigit);
if (fdigits < 1)
fextra = 1;
} else {
fdigit = src;
fdigits = 0;
}
/* No digits? */
if (idigit == 0 && fdigit == 0)
return NULL;
/* Trim leading zeroes. */
while (idigits > 1 && *idigit == zero) {
idigits--;
idigit++;
}
/* Trim trailing zeroes. */
while (fdigits > 1 && fdigit[fdigits - 1] == zero)
fdigits--;
/* Create the necessary digit string, */
digitstring_zero(ds, idigits, fdigits + fextra);
/* copy the integer digits, if any, */
if (idigits > 0)
memcpy(ds->point - idigits, idigit, idigits);
/* and the fractional digits, if any. */
if (fdigits > 0)
memcpy(ds->point + 1, fdigit, fdigits);
/* Return a pointer to the first unparsed character. */
return src;
}
更新数字后,可以调用一个辅助函数来删除所有多余的前导零:
static inline void digitstring_ltrim(digitstring *ds)
{
if (ds && ds->point) {
const int zero = value_to_digit(0);
while (ds->idigits > 1 && ds->point[-ds->idigits] == zero)
ds->idigits--;
}
}
添加两个(无符号)数字字符串,可能会重用其中一项,现在很容易实现:
static void digitstring_add(digitstring *to, const digitstring *src1, const digitstring *src2)
{
digitstring result = DIGITSTRING_INIT;
unsigned int carry = 0;
int i, idigits, fdigits;
if (!to || !src1 || !src2) {
fprintf(stderr, "digitstring_add(): NULL digitstring specified.\n");
exit(EXIT_FAILURE);
}
/* For addition, the result has as many digits
as the longer source term. */
idigits = (src1->idigits >= src2->idigits) ? src1->idigits : src2->idigits;
fdigits = (src1->fdigits >= src2->fdigits) ? src1->fdigits : src2->fdigits;
/* Result needs possibly one more integer digit,
in case carry overflows. */
digitstring_zero(&result, idigits + 1, fdigits);
/* Addition loop, in order of increasing digit significance. */
for (i = -fdigits; i < idigits; i++) {
const unsigned int sum = digitstring_get(src1, i)
+ digitstring_get(src2, i)
+ carry;
digitstring_set(&result, i, sum % RADIX);
carry = sum / RADIX;
}
digitstring_set(&result, idigits, carry);
/* Trim leading zeroes. */
digitstring_ltrim(&result);
/* At this point, we can discard the target, even if it is actually
one of the sources, and copy the result to it. */
digitstring_free(to);
*to = result;
}
其中RADIX
是使用的基数(唯一数字的数目,二进制为2)。要特别注意数字循环。 -fdigits
是结果中的最低有效位,而idigits-1
是结果中的最高有效位。我们需要访问器函数,因为源术语可能根本不包含这些数字(逻辑上它们为零)。
这些功能已经过测试,可以在二进制和八进制数字字符串上使用。我喜欢这种实现方式,因为如果所有术语均为整数,则它会省略小数点(因此您得到12 + 33 = 45
),但是(由于fextra
中的digitstring_parse()
而定)中,如果任何一项具有小数点,则结果将至少有一个小数位(因此12. + 33 = 45.0
)。
答案 1 :(得分:0)
有两个答案,具体取决于您需要定点还是浮点运算。
第一个问题是读取号码。 strtol()
是您的朋友在这里:
char input[BUFFER_SIZE];
char * tmp;
long integral, fractional;
fgets(input, BUFFER_SIZE-1, stdin);
integral = strtol(input, &tmp, 2); /* Read BINARY integral part */
tmp++; /* Pass over the binary point. You may want to check that it is actually a dot */
fractional = strtol(tmp, null, 2); /* Read BINARY fractional part */
下一个问题是弄清楚如何进行算术运算。 fractional
必须根据所提供的点之后的位数和所需的精度进行位移。定点算法很简单:fractional <<= FRAC_BITS - strlen(tmp)
然后将小数部分加在一起。用((1<<FRAC_BITS)-1)
屏蔽总和的小数部分,移位剩余的位,并将它们加到总和的整数部分的整数部分。浮点数有点挑剔,但难度不大。
答案 2 :(得分:0)
在Animals' answer中提出了所有美好的想法之后,我感到奇怪的冲动,提出了自己的解决方案:
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define MAX(x, y) ((x) > (y) ? (x) : (y))
size_t gpp(char const *s)
{
char *n = strchr(s, '.');
return n ? n - s + 1 : 0;
}
char* bin_add(char const *a, char const *b)
{
char const *inp[] = { a, b };
size_t ll[] = { strlen(a), strlen(b) };
size_t pp[] = { gpp(a), gpp(b) };
size_t OO[2], off[2];
for (size_t i = 0; i < 2; ++i) {
OO[i] = pp[i] ? pp[i] - 1 : ll[i];
pp[i] = pp[i] ? ll[i] - pp[i] : 0;}
for (size_t i = 0; i < 2; ++i)
off[i] = OO[i] < OO[!i] ? OO[!i] - OO[i] : 0;
size_t ML = MAX(OO[0], OO[1]) + MAX(pp[0], pp[1]) + (!!pp[0] || !!pp[1]);
char *Ol = calloc(ML + 2, 1);
if(!Ol) return Ol;
char ops[2];
int xc = 0;
size_t lO = ML;
unsigned cc[2] = { 0 };
for (size_t i = ML; i; --i) {
bool pt = false;
for (size_t l = 0; l < 2; ++l) {
ops[l] = i <= ll[l] + off[l] && i - off[l] - 1
< ll[l] ? inp[l][i - off[l] - 1] : '0';
if (ops[l] == '.') {
if (cc[l]) {
free(Ol);
return NULL;
}
pt = true;
++cc[l];
}
ops[l] -= '0';
}
if (pt) {
Ol[i] = '.';
continue;
}
if ((Ol[i] = ops[0] + ops[1] + xc) > 1) {
Ol[i] = 0;
xc = 1;
}
else xc = 0;
lO = (Ol[i] += '0') == '1' ? i : lO;
}
if((Ol[0] = '0' + xc) == '1') return Ol;
for (size_t i = 0; i <= ML - lO + 1; ++i)
Ol[i] = Ol[lO + i];
return Ol;
}
int main(void)
{
char a[81], b[81];
while (scanf(" %80[0.1] %80[0.1]", a, b) & 1 << 1) {
char *c = bin_add(a, b);
if (!c && errno == ENOMEM) {
fputs("Not enough memory :(\n\n", stderr);
return EXIT_FAILURE;
}
else if (!c) {
fputs("Input error :(\n\n", stderr);
goto clear;
}
char* O[] = { a, b, c };
size_t lO[3], Ol = 0;
for (size_t i = 0; i < 3; ++i) {
lO[i] = gpp(O[i]);
lO[i] = lO[i] ? lO[i] : strlen(i[O]) + 1;
Ol = lO[i] > Ol ? lO[i] : Ol;
}
putchar('\n');
for (size_t i = 0; i < 3; ++i) {
for (size_t l = 0; l < Ol - lO[i]; ++l, putchar(' '));
puts(O[i]);
}
putchar('\n');
free(c);
clear :{ int c; while ((c = getchar()) != '\n' && c != EOF); }
}
}
11001001 .11001001
11001001
.11001001
11001001.11001001
11001001 1010
11001001
1010
11010011
111111 1
111111
1
1000000
1010101 010111001.0101110101010
1010101
010111001.0101110101010
100001110.0101110101010
1001001.010111010101 10100101100.10010111101
1001001.010111010101
10100101100.10010111101
10101110101.111000001111
. .
.
.
0
.. .
Input error :(
A
Press any key to continue . . .
我考虑过应该在codereview上要求进行审查。但我认为我宁可不要。
答案 3 :(得分:-2)
对于实数,将非小数和小数部分转换为十进制,进行加法并将其打印为二进制。这将需要函数将数字转换为二进制字符串。请注意,实数是C中的浮点数,它们以2t ^ 3之类的mantessa形式用二进制表示,即2乘以3的幂的指数。