Question

Using bcdiv, i can't divide with small float using scientific notation :

Working code :

bcscale(30);
$a = '1' ;
$b = '0.00000001';
$result = bcdiv($a, $b);
var_dump($result);

Results in :

string(20) "100000000.0000000000"

Non-working code :

bcscale(30);
$a =  '1' ;
$b =  '1e-8';
$result = bcdiv($a, $b);
var_dump($result);

Results in :

Warning: bcdiv() [function.bcdiv]: Division by zero in C:\wamp\www\utilitaires\test_bcdiv.php on line XX NULL

How can i do this division properly, with the less precision loss ?

Was it helpful?

Solution

That is because, actually, bcmath doesn't support scientific notation. It isn't mentioned in manuals, but as you can see, in it's implementation argument conversion is used, it's named php_str2num:

static void php_str2num(bc_num *num, char *str TSRMLS_DC)
{
    char *p;

    if (!(p = strchr(str, '.'))) {
        bc_str2num(num, str, 0 TSRMLS_CC);
        return;
    }

    bc_str2num(num, str, strlen(p+1) TSRMLS_CC);
}

and so bc_str2num:

bc_str2num (bc_num *num, char *str, int scale TSRMLS_DC)
{
  int digits, strscale;
  char *ptr, *nptr;
  char zero_int;

  /* Prepare num. */
  bc_free_num (num);

  /* Check for valid number and count digits. */
  ptr = str;
  digits = 0;
  strscale = 0;
  zero_int = FALSE;
  if ( (*ptr == '+') || (*ptr == '-'))  ptr++;  /* Sign */
  while (*ptr == '0') ptr++;            /* Skip leading zeros. */
  while (isdigit((int)*ptr)) ptr++, digits++;   /* digits */
  if (*ptr == '.') ptr++;           /* decimal point */
  while (isdigit((int)*ptr)) ptr++, strscale++; /* digits */
  if ((*ptr != '\0') || (digits+strscale == 0))
    {
      *num = bc_copy_num (BCG(_zero_));
      return;
    }

  /* Adjust numbers and allocate storage and initialize fields. */
  strscale = MIN(strscale, scale);
  if (digits == 0)
    {
      zero_int = TRUE;
      digits = 1;
    }
  *num = bc_new_num (digits, strscale);

  /* Build the whole number. */
  ptr = str;
  if (*ptr == '-')
    {
      (*num)->n_sign = MINUS;
      ptr++;
    }
  else
    {
      (*num)->n_sign = PLUS;
      if (*ptr == '+') ptr++;
    }
  while (*ptr == '0') ptr++;            /* Skip leading zeros. */
  nptr = (*num)->n_value;
  if (zero_int)
    {
      *nptr++ = 0;
      digits = 0;
    }
  for (;digits > 0; digits--)
    *nptr++ = CH_VAL(*ptr++);

  /* Build the fractional part. */
  if (strscale > 0)
    {
      ptr++;  /* skip the decimal point! */
      for (;strscale > 0; strscale--)
    *nptr++ = CH_VAL(*ptr++);
    }
}

-not hard to see that it will fail on scientific notation (well-commented). Perhaps documentation needs to be updated (to mention that implicitly).

Possible solution will be to convert your string to plain view before applying bcmath functions

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top