C 在无符号算术期间高于ULONG_MAX的数字

C 在无符号算术期间高于ULONG_MAX的数字,c,precision,C,Precision,所以我正在努力创建一个精确的十进制结构,将其特征和尾数分别存储在long和unsigned long中。因为我在做这件事,我必须想出我自己的减法和加法函数 在测试我的函数时,我遇到了“负零”这个棘手的问题。本质上,我不能表示-0.1到-0.9,因为如果不使用某种标志值,我无法在零上加负号。这是背景信息,我会发布代码,这样你就可以看到我是如何做算术的。但奇怪的是,我得到了一个高于ULONG_MAX的数字。具体来说,这是我日志的输出: diff->right: 1844674407369955

所以我正在努力创建一个精确的十进制结构,将其特征和尾数分别存储在long和unsigned long中。因为我在做这件事,我必须想出我自己的减法和加法函数

在测试我的函数时,我遇到了“负零”这个棘手的问题。本质上,我不能表示-0.1到-0.9,因为如果不使用某种标志值,我无法在零上加负号。这是背景信息,我会发布代码,这样你就可以看到我是如何做算术的。但奇怪的是,我得到了一个高于ULONG_MAX的数字。具体来说,这是我日志的输出:

diff->right: 18446744073699551616
b->right10000000
MANTISSA_LIMIT: 100000000
ULONG_MAX: 18446744073709551615
Subtracting 10.10000000 from 10.00000000
Test: tests/bin/decimal.out(subtractDecimalsWithCarry+0x79) [0x40109f]  Decimal: 0.10000000
以及守则:

helpers/decimal.h:

#ifndef __DECIMAL_H__   
#include <limits.h> 
#define MANTISSA_LIMIT 100000000
#define __DECIMAL_H__
typedef struct{          /* Calling them more convenient terms: */
    long left;           /* characteristic */
    unsigned long right; /* mantissa */
}Decimal;

void createDecimal(long left, unsigned long right, Decimal * dec);

/* Perform arithmetic operations on Decimal structures */
void add_decimals(Decimal* a, Decimal* b, Decimal* sum); 
void subtract_decimals(Decimal* a, Decimal* b, Decimal* diff); 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
void createDecimalFromString(Decimal * dec, const char * str);
#endif
最后是调用代码:

#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <unistd.h>

#include "helpers/decimal.h"

void traceAndPrintDecimal(Decimal testDec){
    int nptrs;
void *buffer[100];
    char **strings; 
    nptrs = backtrace(buffer, 100);
    strings = backtrace_symbols(buffer, nptrs);
    printf("Test: %s  Decimal: %ld.%08lu\n", strings[1], testDec.left, testDec.right);

    free(strings);
}

void subtractDecimalsWithCarry(){
    Decimal oper1;
    Decimal oper2;
    Decimal result;
    createDecimalFromString(&oper1, "10.0");
    createDecimalFromString(&oper2, "10.1");
    subtract_decimals(&oper1, &oper2, &result);
    printf("Subtracting %ld.%08lu from %ld.%08lu\n",oper2.left,oper2.right,oper1.left,oper1.right);
    traceAndPrintDecimal(result);
}


int main(){

subtractDecimalsWithCarry();
return 0;
}
奇怪的是,
diff->right
比ULONG_MAX大,有人知道为什么会这样吗?如果你需要更多的信息,请告诉我,我会尽力更新问题

错误的“ULONG_MAX上方的编号”

乍一看,值为“18446744073699551616”的
diff->right
似乎大于ULONG_MAX(“18446744073709551615”)。但是是9999999少。(@UncleO)


OP在评论中称,“你知道为什么一个地方有点歪吗?因为尾数的工作方式,这个数字应该只减少1000000。但是它减少了1000001。”。我认为这是不正确的

// from createDecimalFromString(&oper1, "10.0");
oper1.right = 0
// from createDecimalFromString(&oper2, "10.1");
oper1.right = 10000000
// from subtract_decimals(&oper1, &oper2, &result)
diff->right = oper1.right - oper2.right --> 18446744073699551616
unsigned
减法在C中有很好的定义。在这种情况下,差
oper1.right-oper2.right
将在数学上导致
oper1.right-oper1.right+(ULONG_MAX+1)


“…无法由结果无符号整数类型表示的结果将以大于结果类型可表示的最大值一的数字为模进行缩减。”C11 6.2.5 8

再仔细看一看。diff->右侧较小。18446744073^6^99551616 18446744073^7^09551615哦!你完全正确。我只是在看一个地方,没有注意到其余的号码。呸。谢谢你知道为什么这个地方有点歪吗?由于尾数的工作方式,这个数字应该只减少1000000。但它却减少了1000001@请在代码中详细说明魔法数字18446744073699551616UL的含义和用法。@chux魔法数字是我在日志中观察到的似乎不合适的数字。我打算把它拿出来,但这是一种检查它是否在-0.1到-0.9范围内的方法,因为我专门处理这些数字(我认为它不会处理像.11这样的东西,因为这会修改更多的位置),一个位置被关闭确实是一开始尝试使用该数字的原因。如果我能弄清楚为什么我的“一”的位置是关闭的,我就不需要任何带ULONG_MAX+1部分的无符号减法部分的神奇数字,我想这将在我以后处理这个问题时再次帮助我(现在只能在这个线程上发布)。我将尾数值sprint到%08lu,以便将它们存储到mySQL数据库十进制(11,8)。由于格式化程序,我得到了我需要的额外前导零,然后我将.1表示为10000000,01表示为1000000,等等。我这样做是因为我只关心8位精度。如果“10.0”=>0尾数,我需要修正它@chux当我稍后处理它并使用ULONG_MAX+1修复它时,我会接受它作为answer@EJEHardenberg如果您需要便携性,因为您有8个十进制数字尾数,请考虑使用类型<代码> UTIT32×T 。你的机器上64位的
在我的机器上是32位。太棒了。使用:diff->right=(ULONG_MAX+1)-diff->right;获取尾数的正确值,我将处理特征的边缘情况,即带有某种标志的0。谢谢@chux。我将研究使用uint32\u t作为类型,而不是long。非常感谢。尽管如此,如果uint32是C99编译器的可选选项,它如何可移植?(从这篇文章判断)@EJEHardenberg提出了一个关于可移植性的好观点。如果您想使用VisualStudio,那么其中一个甚至没有大的C99遵从性。那么,我们是否使用C89作为标准?我的建议最适用于最新的编译器。由于C11已经推出了几年,我的目标是。我发现新的适配器比技术曲线后端的适配器能赚更多的钱。你的经历可能不同。
decimal.o: src/helpers/decimal.c
    cc -I./headers -std=gnu99 -pedantic -Wall -Wextra -Werror -g -c src/helpers/decimal.c -o obj/decimal.o

test-decimal: tests/decimal-test.c decimal.o
    cc -I./headers -std=gnu99 -pedantic -Wall -Wextra -Werror -g tests/decimal-test.c obj/decimal.o -o tests/bin/decimal.out -lm -rdynamic
// from createDecimalFromString(&oper1, "10.0");
oper1.right = 0
// from createDecimalFromString(&oper2, "10.1");
oper1.right = 10000000
// from subtract_decimals(&oper1, &oper2, &result)
diff->right = oper1.right - oper2.right --> 18446744073699551616