C语言二进制除法实现:Windows正常Linux输出全零问题排查
跨平台大二进制数除法的Windows/Linux差异问题
我实现了一个大二进制数除法的代码,在Windows下能输出正确结果:60000000 7948AECA D269AE4A 6B41DD10 64209812 293E346 1C0000,但在Linux下输出全为0。已经排除了打印方式的问题,怀疑是变量大小导致的跨平台差异,请问要修改哪里?是否需要更换除法实现方式?
核心除法实现代码
#define SIZE_BIG_DECIMAL 7 typedef struct { uint64_t bits[SIZE_BIG_DECIMAL]; uint16_t scale; uint16_t sign; } s21_big_decimal; void binary_div(s21_big_decimal *value_1, s21_big_decimal value_2, s21_big_decimal *result) { init_s21_decimal(result); s21_big_decimal Q; s21_big_decimal R; int scaleDiv = -1; do { init_s21_decimal(&R); // fill with 0 the array init_s21_decimal(&Q); scaleDiv++; int lastBit = (SIZE_BIG_DECIMAL - 1) * 32 - 1; int currentBit = get_bit_big(*value_1, lastBit); while (currentBit == 0 && lastBit >= 0) { currentBit = get_bit_big(*value_1, lastBit); if (currentBit == 0) lastBit--; } for (int i = lastBit; i >= 0; i--) { change_left_big(&R, 1); set_bit_big(&R, 0, get_bit_big(*value_1, i)); if (is_less_or_equal_big(value_2, R)) { s21_big_decimal res = { 0 }; init_s21_decimal(&res); s21_sub_big(R, value_2, &res); init_s21_decimal(&R); copy_bits(res, &R); set_bit_big(&Q, i, 1); } } if (scaleDiv > 0) { mul_10_big(result); } s21_big_decimal res; init_s21_decimal(&res); add_big(*result, Q, &res); copy_bits(res, result); if (!is_zero_big(R)) { init_s21_decimal(value_1); mul_10_big(&R); copy_bits(R, value_1); } } while (!is_zero_big(R) && scaleDiv < 28); set_scale_big(result, scaleDiv); for (int i = 0; i < 7; i++) { printf("%lX ", result->bits[i]); } printf("\n"); }
相关位操作辅助函数
int mul_10_big(s21_big_decimal *dec) { s21_big_decimal temp = *dec; for (int i = 0; i < SIZE_BIG_DECIMAL - 1; i++) { temp.bits[i] *= 10; } temp.scale++; int overflowed = 0; if (getoverflow(&temp)) { overflowed = 1; } else { *dec = temp; } return overflowed; } int div_10_big(s21_big_decimal *dec) { uint64_t remained = 0; for (int i = SIZE_BIG_DECIMAL - 2; i >= 0; i--) { dec->bits[i] += remained << 32; remained = dec->bits[i] % 10; dec->bits[i] /= 10; } dec->scale--; return remained; } int getoverflow(s21_big_decimal *dec) { int overflow = 0; int count = 0; for (int i = 0; i < SIZE_BIG_DECIMAL - 1; i++) { dec->bits[i] += overflow; overflow = (dec->bits[i] >> 32); dec->bits[i] &= MAX4BITE; count++; } int result = 0; if (overflow != 0 && count == 6) { result = 1; } return result; } void set_scale_big(s21_big_decimal *big_dec, int scale_value) { big_dec->bits[SIZE_BIG_DECIMAL - 1] &= MINUS; big_dec->bits[SIZE_BIG_DECIMAL - 1] |= (scale_value << 16) & SCALE; }
问题定位与修改方案
1. 核心位索引错误(最关键)
代码中bits数组是uint64_t类型,但位索引计算按32位元素处理:
int lastBit = (SIZE_BIG_DECIMAL - 1) * 32 - 1;
这会导致程序一开始找不到有效最高位,后续循环完全不执行,最终结果全为0。修改为:
int lastBit = (SIZE_BIG_DECIMAL - 1) * 64 - 1;
同时,get_bit_big、set_bit_big、change_left_big等位操作函数,必须全部适配64位元素:计算位所属数组下标用n / 64,位偏移用n % 64,而不是32。
2. mul_10_big进位逻辑错误
当前实现先直接对所有元素乘10再处理溢出,会导致64位元素溢出后丢失高位数据。正确的大整数乘10应该从低位到高位逐位处理进位:
int mul_10_big(s21_big_decimal *dec) { s21_big_decimal temp = *dec; uint64_t carry = 0; for (int i = 0; i < SIZE_BIG_DECIMAL - 1; i++) { uint64_t product = temp.bits[i] * 10 + carry; temp.bits[i] = product & 0xFFFFFFFF; // 保留低32位(和原有逻辑对齐) carry = product >> 32; // 提取进位 } temp.scale++; int overflowed = (carry != 0) ? 1 : 0; if (!overflowed) { *dec = temp; } return overflowed; }
3. 宏定义跨平台一致性检查
确保MAX4BITE、MINUS、SCALE的定义在Windows和Linux下一致:
MAX4BITE必须是0xFFFFFFFF(32位全1),用于保留低32位数据MINUS必须是0x8000000000000000(64位最高位,符号位)SCALE必须是0x00FF000000000000(对应scale的16位存储区域)
是否需要更换除法实现?
不需要更换核心长除法逻辑,上述修改即可解决跨平台差异问题。当前的长除法逻辑是正确的,问题仅出在细节的位索引、进位处理和宏定义上。
内容的提问来源于stack exchange,提问作者laptop22
相关产品推荐
相关产品推荐

