Search code examples
c++printfzeronegative-number

Get printf to ignore the negative sign on values of zero


I'm trying to write a (mostly)* C program that sorts numerical results and eliminates duplicates. The results are stored as STRUCTS that contain a string, an integer, and 4 doubles. The doubles are what is relevant for determining if two results are duplicates.

To do this, I sprintf a string using the 4 doubles to some precision i.e.

    #define PRECISION 5
sprintf(hashString, "%.*lf %.*lf %.*lf %.*lf", PRECISION, result.v1, PRECISION, result.v2, PRECISION, result.v3, PRECISION, result.v4);

I then use this as a hashkey for a tr1::unordered_map<string, ResultType>. Then the program checks to see if the hashtable already contains an entry for that key, if so, the result is a duplicate and can be discarded. Otherwise, it gets added to the hashtable.

The problem is that sometimes one of my values will be rounded to zero from, for example, -10E-9, by sprintf; As a result, the string will contain "-0.00000" rather than "0.00000". These two values will obviously generate different hashkeys, despite representing the same result.

Is there something built into sprintf or even the C language that will allow me to deal with this? I've come up with a bit of a work around (see post below) -- but if there's something built in, I would much rather use that.

*the program is written in C because that's the language I'm most comfortable in, but I'll end up compiling with g++ in order to use the unordered_map.

I've come up with the following workaround. But A) I'm hoping there's a builtin solution and B) I don't have a very deep understanding of atof or floating point math, so I'm not sure if the condition if(doubleRepresentation == 0.0) will always trip when it should.

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #define PRECISION 5
    #define ACCURACY 10E-6
    double getRidOfNegZeros (double number)
    {

            char someNumAsStr[PRECISION + 3]; // +3 accounts for a possible minus sign, the leading 0 or 1, and the decimal place.
            sprintf(someNumAsStr, "%.*lf", PRECISION, number);

            double doubleRepresentation = atof(someNumAsStr);
            if((doubleRepresentation < ACCURACY) && (doubleRepresentation > -ACCURACY))
            {
                    doubleRepresentation = 0.0;
            }

            return doubleRepresentation;
    }

    int main()
    {
            printf("Enter a number: \n");
            double somenum;
            scanf("%lf",&somenum);

            printf("The new representation of double \"%.*lf\" is \"%.*lf\"\n", PRECISION, somenum, PRECISION, getRidOfNegZeros(somenum));
            return 0;
    }

Solution

  • #include <string>
    
    #define PRECISION 5
    #define LIMIT 5e-6
    
    std::string string_rep (double x) {
       char buf[32];
       double xtrunc = ((x > -LIMIT) && (x < LIMIT)) ? 0.0 : x;
       std::sprintf (buf, "%.*f", PRECISION, xtrunc);
       return std::string(buf);
    }
    
    std::string make_key (double x, double y, double z, double w) {
       std::string strx = string_rep (x);
       std::string stry = string_rep (y);
       std::string strz = string_rep (z);
       std::string strw = string_rep (w);
       return strx + " " + stry + " " + strz + " " + strw;
    }