Pregunta

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. 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. 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:

#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:

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

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? If you need anymore information let me know and I'll do my best to update the question.

¿Fue útil?

Solución

Mistaken "Number above ULONG_MAX".

At first glance diff->right with the value "18446744073699551616" appeared to be larger than ULONG_MAX ("18446744073709551615"). But is 9999999 less. (@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". 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).

" ... 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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top