NOTICE: This version of the NSF Unidata web site (archive.unidata.ucar.edu) is no longer being updated.
Current content can be found at unidata.ucar.edu.

To learn about what's going on, see About the Archive Site.

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