Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposed update for the printFloat code of print.cpp #247

Closed
RobTillaart opened this issue May 12, 2013 · 6 comments
Closed

Proposed update for the printFloat code of print.cpp #247

RobTillaart opened this issue May 12, 2013 · 6 comments
Assignees

Comments

@RobTillaart
Copy link

The printing of floats has an error in it as it prints floats larger than maxlong as "ovf" or overflow as the current printFloat() does not support scientific notation.

In - http://forum.arduino.cc/index.php?topic=166041.0 - I discuss / propose a change to support printing of floats larger than maxlong and printing of very small floats that otherwise would be rounded to 0. On the forum is also a small test sketch.

The interface of print() and println() is not changed so the user cannot force E notation, that is for the future, one step at the time ;)

Rob.

Code snippet

size_t Print::printFloat(double number, uint8_t digits) 
{ 
  size_t n = 0;
  int exponent = 0;

  if (isnan(number)) return print("nan");
  if (isinf(number)) return print("inf");

  if ( abs(number) > 1000000000.0)
  { 
    while (abs(number) > 10.0)
    {
      number /= 10.0;
      exponent++;
    }
  } 
  else if (abs(number) < 0.001) 
  { 
    while (abs(number) < 1.0)
    {
      number *= 10.0;
      exponent--;
    }
  }

  // Handle negative numbers
  if (number < 0.0)
  {
     n += print('-');
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;

  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  n += print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0) {
    n += print("."); 
  }

  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    n += print(toPrint);
    remainder -= toPrint; 
  } 

  if (exponent != 0)
  {
    n += print('E');
    if (exponent > 0) n += print('+');
    n += print(exponent);
  }

  return n;
}
@RobTillaart
Copy link
Author

Code has been improved and tested. Support for -infinity and both SCI and ENG formatting for floating point numbers. Used it for several weeks with no new problems.

Code see zipfile attached to - http://forum.arduino.cc/index.php?topic=166041.msg1241170#msg1241170 -

@PatrickTrentin88
Copy link

PatrickTrentin88 commented Feb 4, 2017

This issue is still present 4 years later, it would be nice to have an explanation as to why there are certain limitations embedded in the print function, and these in particular:

if (number > 4294967040.0) return print ("ovf"); // constant determined empirically 
if (number <-4294967040.0) return print ("ovf"); // constant determined empirically

Clearly the code following these lines has some limitations which justify these guards, however it is unclear the reason why it was decided to keep this undocumented behaviour instead of fixing the code in the official repository.

If there is no plan to fix this issue in the official upstream repository, the documentation (also this) should be updated to reflect the actual implementation and warn users about this inherent limitation, since it is a totally counter-intuitive behaviour.

@RobTillaart
Copy link
Author

RobTillaart commented Feb 4, 2017

Old thread woke up :)

probably too busy with new features as that is more fun?

looking at the printFloat code above I would now remove the abs() and probably replace the float division by a multiply to speed it up a bit.

size_t Print::printFloat(double number, uint8_t digits) 
{ 
  size_t n = 0;
  int exponent = 0;

  if (isnan(number)) return print("nan");
  if (isinf(number)) return print("inf");

  // Handle negative numbers
  if (number < 0.0)
  {
     n += print('-');
     number = -number;
  }

  if (number > 1000000000.0)
  { 
    while (number > 10.0)
    {
      number /= 10.0;  //  number *= 0.1;
      exponent++;
    }
  } 
  else if (number < 0.001) 
  { 
    while (number < 1.0)
    {
      number *= 10.0;
      exponent--;
    }
  }


  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i = 0; i < digits; ++i)
    rounding /= 10.0;   //  number *= 0.1

  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  n += print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0) {
    n += print("."); 
  }

  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    n += print(toPrint);
    remainder -= toPrint; 
  } 

  if (exponent != 0)
  {
    n += print('E');
    if (exponent > 0) n += print('+');
    n += print(exponent);
  }

  return n;
}

@AcidPrank
Copy link

AcidPrank commented Feb 5, 2017

@RobTillaart
Dude, what's happens if number = 0.0 ? :p
You get an infinite loop :

else if (number < 0.001) 
  { 
    while (number < 1.0)
    {
      number *= 10.0;
      exponent--;
    }
  }

Corrected by :

else if (number < 0.001 && number != 0.0) 
  { 
    while (number < 1.0)
    {
      number *= 10.0;
      exponent--;
    }
  }

@RobTillaart
Copy link
Author

you're 100% right!

@sandeepmistry sandeepmistry transferred this issue from arduino/Arduino Sep 16, 2019
@RobTillaart
Copy link
Author

Created my own lib to solve this - https://github.com/RobTillaart/printHelpers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants