[udunits] Less pernickety version of ut_compare() ?

Hi,

    I found the ut_compare() function, as currently existing the the udunits2 
library,  to be too strict for my needs.

    In particular, for some units, the comparison between the original unit and 
a back-and-forth transformation to text expression fails.
    In other words :  for some values of unit, ut_compare(unit, 
(ut_parse(ut_format(unit))) != 0.

    I know that a decimal textual representation of a real number cannot be 
exact, but I believe that we can safely assume that two units whose relative 
difference is less than 10^-8 or 10^-10 are identical.
    So, I wrote my own, more tolerant, ut_compare function (see attached source 
code).

     My questions are :
                 - is this pernickety behaviour intended, or did someone just 
wrote the test "a==b" for floating-point values instead of the usual 
approximate test ? 
                 - Am I the only one interested in a more lenient version of 
ut_compare() ? Would it be a good idea to add a ut_tolerant_compare() function 
to the udunits2 library ? 

Regards,

Bruno.

-- 
Bruno Piguet 
Météo-France 
Équipe GMEI/TRAMM 
CNRM/GAME, UMR3589 CNRS/Météo-France 
Fixe : +33 561079659 
Fax : +33 561079627 
Postal : 42 Av. G. Coriolis 
31057 Toulouse CEDEX 1 


/**
 * Demonstration of the strict behaviour of ut_compare(), (for some values
 * of unit, ut_compare(unit, (ut_parse(ut_format(unit))) != 0), and 
 * of a more lenient version.
 * 
 * compile with :
 *  gcc -O  pb_compare.c -ludunits2  -o pb_compare
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <udunits2/udunits2.h>

static ut_system *unitSystem;


int
my_ut_compare_pragmatic (const ut_unit * unit1, const ut_unit * unit2)
{
  int CodRet;
  ut_unit *ratio_ut_unit;
  double ratio_double;
  char buf[120];
  char *endptr;

  CodRet = ut_compare (unit1, unit2);
  if (CodRet == 0)
    return CodRet;

  /* "This function returns a non-zero value if conversion is possible;
   *  otherwise, 0 is returned" */
  CodRet = ut_are_convertible (unit1, unit2);
  /* if conversion impossible, exit */
  if (CodRet == 0)
    return -5;

  /* let's see if conversion ratio is close to 1 */
  ratio_ut_unit = ut_divide (unit1, unit2);
  if (ratio_ut_unit == NULL)
    return -6;

  /* Don't know how tho get the ration from an ut_unit variable.
   * So, let's format it and parse the resulting string */
  CodRet = ut_format (ratio_ut_unit, buf, sizeof (buf), UT_ASCII);
  ut_free (ratio_ut_unit);
  ratio_ut_unit = NULL;
  if ((CodRet < 0) || (CodRet >= (int) sizeof (buf)))
    {
      return -7;
    }

  /* the end must be " 1" (meaning dimensionless) */
  if ((strlen (buf) > 2) && (!strcmp (buf + strlen (buf) - 2, " 1")))
    {
      buf[strlen (buf) - 2] = 0;
      ratio_double = strtod (buf, &endptr);
      if ((*endptr == 0) && (fabs (1.0 - ratio_double) < 1.0e-8))
        {
          /* Approximately equal --> OK ! */
          return 0;
        }
    }
  return -8;
}


void
test_back_forth (const char *unit_str)
{
  ut_unit *unit_1, *unit_2;
  char buf[120];
  int len, cmp_cod;

  unit_1 = ut_parse (unitSystem, unit_str, UT_ASCII);
  if (unit_1 == NULL)
    {
      fprintf (stderr, "Error %d parsing : %s\n", ut_get_status (), unit_str);
      return;
    }

  len = ut_format (unit_1, buf, sizeof (buf), UT_ASCII);
  if ((len < 0) || (len >= (int) sizeof (buf)))
    {
      fprintf (stderr, "Error %d writing unit\n", ut_get_status ());
      ut_free (unit_1);
      return;
    }
  fprintf (stdout, "%s formatted as : %s\n", unit_str, buf);

  unit_2 = ut_parse (unitSystem, buf, UT_ASCII);
  if (unit_2 == NULL)
    {
      fprintf (stderr, "Error %d parsing : %s\n", ut_get_status (), buf);
      ut_free (unit_1);
      return;
    }

  cmp_cod = ut_compare (unit_1, unit_2);
  fprintf (stdout, "    ut_compare(%s, %s) = %d\n", unit_str, buf, cmp_cod);
  cmp_cod = my_ut_compare_pragmatic (unit_1, unit_2);
  fprintf (stdout, "    my_ut_compare(%s, %s) = %d\n", unit_str, buf,
           cmp_cod);

  ut_free (unit_1);
  ut_free (unit_2);
}

int
main (int argc, char *argv[])
{
  const char *unit_tab[] = { "degree",
    "kt",
    "ft/min",
    "mm/h",
    "l/min"
  };

  unsigned long nb_unit = sizeof (unit_str) / sizeof (*unit_str);
  unsigned long i;

  unitSystem = ut_read_xml (NULL);
  if (unitSystem == NULL)
    return 1;

  for (i = 0; i < nb_unit; i++)
    test_back_forth (unit_tab[i]);

  ut_free_system (unitSystem);
  return 0;
}
  • 2015 messages navigation, sorted by:
    1. Thread
    2. Subject
    3. Author
    4. Date
    5. ↑ Table Of Contents
  • Search the udunits archives: