2013-09-30 29 views
2

因此,我正在創建一個精確的十進制結構,將它的特徵和尾數存儲在長和無符號的長整數內。因爲我正在這樣做,所以我必須拿出我自己的減法和加法函數。無符號數學運算期間ULONG_MAX以上的數字

在測試我的函數時,我遇到了「負零」的麻煩問題。本質上,我不能表示-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 

,代碼:

助手/ 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 

然後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; 

} 

最後來電代碼:

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

而且這塊我生成文件的編譯:

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 

很奇怪的是,diff->right比ULONG_MAX大,沒有人知道這可能是爲什麼?如果您需要更多信息,請告訴我,我會盡我所能來更新問題。

+2

稍微靠近一點。 diff->右側較小。 18446744073^6^99551616 18446744073^7^09551615 – UncleO

+0

哦!你完全正確。我只是看着那個地方,並沒有注意到其餘的數字。唷。謝謝! – EdgeCaseBerg

+0

你有什麼想法爲什麼那個地方有點翹首以待?由於尾數的作用方式,該數字應該只有1000000。但它取而代之的是10000001。 @UncleO – EdgeCaseBerg

回答

1

錯誤的「超過ULONG_MAX的數字」。

乍一看diff->right值「18446744073699551616」似乎大於ULONG_MAX(「18446744073709551615」)。但是9999999少了。 (@UncleO)


OP在評論斷言「爲什麼那些地方去有點荒唐任何想法?數只能由1000000由於尾數的工作方式被關閉,但它通過10000001的關閉,而不是」 。建議這是不正確的。

// 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

+0

感謝無符號減法部分和ULONG_MAX + 1部分,我認爲這會在以後再次幫助我在此工作(現在只能在此線程上發帖)。我將sprintf尾數值轉換爲%08lu以便st將它們放入一個mySQL數據庫Decimal(11,8)。由於格式化程序,我得到我需要的額外前導零,然後我將.1表示爲10000000,.01表示1000000等。我這樣做是因爲我只關心8位數的精度。如果「10.0」=> 0尾數我需要解決這個問題。 @chux當我這方面的工作後,並把它固定起來使用ULONG_MAX +1我會接受這個作爲答案 – EdgeCaseBerg

+0

@EJEHardenberg如果您想可移植性和因爲你有8個小數位尾數,可以考慮使用類型'uint32_t'。你的機器上的64位長度是32位。 – chux

+0

太棒了。使用:diff-> right =(ULONG_MAX + 1) - diff-> right;爲我找到了尾數的正確值,我只是要處理特徵爲0的邊緣情況,並帶有某種標誌。謝謝@chux。我會考慮使用uint32_t作爲類型而不是long。謝謝一堆。 – EdgeCaseBerg