2

そのため、私は、その特性と仮数を long と unsigned long に丁重に格納する正確な Decimal 構造の作成に取り組んでいます。私はこれを行っているため、独自の減算関数と加算関数を考え出す必要がありました。

関数をテストしているときに、「負のゼロ」という厄介な問題に遭遇しました。基本的に、-0.1 から -0.9 までを表すことはできません。これは、何らかのフラグ値を使用しない限り、0 にマイナス記号を付ける方法がないためです。これは背景情報であり、コードを投稿して、私がどのように計算を行っているかを確認できるようにします。ただし、奇妙な動作は、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

ULONG_MAX よりも大きいのは奇妙ですdiff->rightが、これがなぜなのか知っている人はいますか? さらに情報が必要な場合はお知らせください。質問を更新するために最善を尽くします。

4

1 に答える 1

1

「ULONG_MAX 以上の数値」と間違えました。

一見diff->right、「18446744073699551616」という値は、ULONG_MAX (「18446744073709551615」) よりも大きいように見えます。しかし、9999999 少ないです。(@おじさん)


OPはコメントで、「1の位が少しおかしくなる理由は何か考えはありますか?仮数部の動作方法のために、数値は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)ます。

「 ... 結果の符号なし整数型で表現できない結果は、結果の型で表現できる最大値よりも 1 大きい数値を法として減らされます。」C11 6.2.5 8

于 2013-09-30T16:19:30.953 に答える