Получить printf, чтобы игнорировать знак минус при нулевых значениях - PullRequest
1 голос
/ 07 июля 2011

Я пытаюсь написать (в основном) * C программу, которая сортирует числовые результаты и удаляет дубликаты.Результаты сохраняются как STRUCTS, которые содержат строку, целое число и 4 двойных числа.Двойные значения - это то, что важно для определения того, являются ли два результата дубликатами.

Для этого я бегу строку с четырьмя двойными точками с некоторой точностью, т.е.

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

Затем я использую это какхеш-ключ для tr1::unordered_map<string, ResultType>.Затем программа проверяет, содержит ли хеш-таблица запись для этого ключа, если это так, результат является дубликатом и может быть отброшен.В противном случае он добавляется в хеш-таблицу.

Проблема в том, что иногда одно из моих значений будет округлено до нуля, например, от -10E-9, до sprintf;В результате строка будет содержать «-0,00000», а не «0,00000».Эти два значения, очевидно, будут генерировать разные хеш-ключи, несмотря на то, что они представляют один и тот же результат.

Есть ли что-то встроенное в sprintf или даже язык C, который позволит мне разобраться с этим?Я немного поработал (см. Пост ниже) - но если что-то встроено, я бы скорее использовал это.

* программа написана на C, потому что это языкМне удобнее всего, но в итоге я скомпилирую с g ++, чтобы использовать unordered_map.

Я нашел следующий обходной путь.Но А) Я надеюсь, что есть встроенное решение, и Б) У меня нет глубокого понимания математики atof или с плавающей запятой, поэтому я не уверен, что условие if(doubleRepresentation == 0.0) всегда будет срабатывать, когда должно.

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

Ответы [ 4 ]

2 голосов
/ 07 июля 2011

Вместо того, чтобы sprintf () превращал двойники в большую строку и использовал это как ключ на карте, почему бы просто не поместить свои структуры в карту? Вы можете сделать это достаточно легко, если вы просто напишите оператор меньше чем для своих структур, который рассматривает значения с плавающей запятой, которые вы хотите использовать в качестве ключа. Примерно так:

bool operator <(const MyStruct &lhs, const MyStruct &rhs)
{
    return lhs.v1 < rhs.v1 ||
        (lhs.v1 == rhs.v1 && lhs.v2 < rhs.v2); // ...
}

Тогда вы можете заменить tr1::unordered_map<string, ResultType> на std::map<ResultType> и избежать всего бизнеса струнной печати. Если хотите, вы можете добавить эпсилон в функцию сравнения, чтобы почти одинаковые числа были устойчиво отсортированы.

1 голос
/ 07 июля 2011

Если вы знаете, что вам важны различия только в 0,00001 (исходя из вашего определения PRECISION), вы можете сначала округлить значения до целых чисел.Примерно так может работать:

#include <math.h>
#include <stdio.h>

#define SCALE 1e5 // instead of PRECISION 5
sprintf(hashString, "%d %d %d %d",
    (int)round(result.v1 * SCALE),
    (int)round(result.v2 * SCALE),
    (int)round(result.v3 * SCALE),
    (int)round(result.v4 * SCALE));

Это также требует ограничения величины значений с плавающей запятой.Вы не хотите переполнять целочисленные значения.

Вы также можете обойти форматирование строк и просто выполнить вычисления округления как часть хэша структурного уровня, как предлагали другие.

0 голосов
/ 07 июля 2011
#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;
}
0 голосов
/ 07 июля 2011

Если вы используете это только для хэширования двойных значений, не беспокойтесь о преобразовании их в строку - просто хешируйте двойные значения напрямую.Любая хеш-библиотека, достойная своей соли, будет иметь возможность хэшировать произвольные двоичные двоичные объекты данных.

Если по какой-то странной причине ваша хеш-библиотека поддерживает только строки C с нулевым символом в конце, распечатайте необработанные байты double значение:

// Alias the double value as a byte array
unsigned char *d = (unsigned char *)&result.v1;
// Prefer snprintf to sprintf!
spnrintf(hashString, hashStringLength, "%02x%02x%02x%02x%02x%02x%02x%02x",
         d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]);
// ...and so on for each double value

Это гарантирует, что неравные значения определенно получат неравные строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...