简体   繁体   English

无符号算术期间大于ULONG_MAX的数字

[英]Number above ULONG_MAX during unsigned arithmetic

So I'm working on creating a precise Decimal structure that stores it's characteristic and mantissa within a long and an unsigned long respectfully. 因此,我正在创建一个精确的十进制结构,该结构将特征和尾数分别存储在较长和无符号的长度内。 Because I'm doing this I've had to come up with my own subtraction and addition functions. 因为我正在这样做,所以我不得不想出自己的减法和加法函数。

While testing my functions I ran across the troublesome problem of "negative zero". 在测试我的功能时,我遇到了“负零”这一麻烦的问题。 Essentially, I cannot represent -0.1 through -0.9 because there is no way for me to put a negative sign on my zero without just using a flag value of some kind. 本质上,我不能用-0.1到-0.9来表示,因为如果不使用某种类型的标记值,就没有办法在负数上加上负号。 This is background information and I'll post the code so you can see how I'm doing the arithmetic. 这是背景信息,我将发布代码,以便您了解我的算法。 The STRANGE behavior though is that I'm getting a number above ULONG_MAX. 但STRANGE的行为是我得到的数字高于 ULONG_MAX。 Specifically this is the output of my log: 具体来说,这是我的日志的输出:

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

And the code: 和代码:

helpers/decimal.h: 助手/ 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

And then decimal.c's relavant code: 然后是decimal.c的相关代码:

/* Subtract two decimals, a - b */
void subtract_decimals(Decimal* a, Decimal* b, Decimal* diff){
    diff->left = a->left - b->left;
    diff->right = a->right - b->right;
    fprintf(stderr, "diff->right: %lu\n", diff->right);
    fprintf(stderr, "b->right%lu\n", b->right);
    fprintf(stderr, "MANTISSA_LIMIT: %d\n", MANTISSA_LIMIT);
    fprintf(stderr, "ULONG_MAX: %lu\n", ULONG_MAX);
    if(diff->right > MANTISSA_LIMIT) { 
    if(diff->right != 18446744073699551616UL)
        diff->left -= 1;            
    else
        diff->left *= -1; /* This is where I might put a flag for -0*/
    diff->right = ULONG_MAX - diff->right + (18446744073699551616UL == diff->right ? 1 : 0);     /* +1 because of the wrap around, we have to 'count' 0. */
    }
}

void createDecimalFromString(Decimal * dec, const char * str){
    long left;
    unsigned long right;
    char * dotLocation;
    char rawLeft[9];
    char rawRight[9];
    int i;
    int dotPos;
    long leadingZeros;
    int numDetected;

    if(str == NULL)
         return;

    bzero(rawLeft,9);
    bzero(rawRight,9);

    dotLocation = strstr(str, ".");
    leadingZeros = numDetected = 0;
        if(dotLocation == NULL){
           left = atol(str);
           right = 0;
        }else{
        /* ghetto strncpy */
        for(i=0; i != 9 && str[i] != *dotLocation; ++i)
            rawLeft[i] = str[i];
        rawLeft[i] = '\0';
        dotPos = i+1;
        left = atol(rawLeft);
        for(i=0; i != 9 && str[dotPos] != '\0'; ++i,++dotPos){
            if(str[dotPos] == '0' && numDetected == 0)
               leadingZeros++;
            else
               numDetected = 1;

            rawRight[i] = str[dotPos];
        }
        rawRight[i] = '\0';
        right = strtoul(rawRight,NULL,10);
        if(leadingZeros > 0)
            /* subtract the leading zeros, then also the powers of ten taken by the number  itself*/
            right = (right*(powlu(10,7-leadingZeros-(i-2))));
        else
            right = right*(powlu(10,(i > 1 ? 8-(i-1) : 7 ))); 
    }

    dec->left = left;
    dec->right = right;

}

And finally the calling code: 最后是调用代码:

#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;
}

And the piece of my makefile for compiling: 而我的makefile文件中的一部分用于编译:

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

It's strange that diff->right is larger than ULONG_MAX, does anyone know why this might be? 奇怪的是diff->right大于ULONG_MAX,有人知道为什么会这样吗? If you need anymore information let me know and I'll do my best to update the question. 如果您需要更多信息,请告诉我,我将尽力更新问题。

Mistaken "Number above ULONG_MAX". 错误输入“ ULONG_MAX以上的数字”。

At first glance diff->right with the value "18446744073699551616" appeared to be larger than ULONG_MAX ("18446744073709551615"). 乍看之下, diff->right的值为“ 18446744073699551616”似乎大于ULONG_MAX(“ 18446744073709551615”)。 But is 9999999 less. 但是少了99999999。 (@UncleO) (@UncleO)


OP's asserts in comment "any idea why the ones place goes a little cockeyed? The number should only be off by 1000000 because of the way the mantissa works. But it's off by 10000001 instead". OP在评论中断言“任何地方都为什么会有点开嘴?由于尾数的工作方式,该数字只应减少1000000。而相反,该数字应减少10000001”。 Suggest this is incorrect. 提示这是不正确的。

// 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 subtraction is well defined in C. In this case the difference oper1.right - oper2.right will mathematically result in oper1.right - oper1.right + (ULONG_MAX + 1) . C中很好地定义了unsigned减法。在这种情况下, oper1.right - oper2.right的差将在数学上导致oper1.right - oper1.right + (ULONG_MAX + 1)

" ... a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type." “……无法用结果无符号整数类型表示的结果的模数要比可以用结果类型表示的最大值大一个模。 C11 6.2.5 8 C11 6.2.5 8

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM