klog
klog

Reputation: 486

Double value that is NaN output as a string as "nan"

I have a C/C++ application that writes numeric values into redis as a string. I also have a Java application that will read these values out of redis. On occasion for valid reasons, we end up with a floating-point value that has a value of NaN or -NaN. However, when using sprintf(charPtr, "%e", dblVal);, the value is output as nan. I also tried the same statement with %E, and it resulted in NAN. When Java attempts to parse via Float.parseFloat() or Double.parseDouble it throws a NumberFormatException.

This application has been ported from Solaris to Linux and subsequently gone through some Linux upgrades and at some point "NaN" became "nan". I can't say for certain on what upgrade ultimately introduced this behavior.

I have gone through an excercise utilizing cmath's std::isfinite() and std::isnan() and can ultimately define my own valid (-)NaN or (-)Infinity strings, but this doesn't seem like I need to be reinventing normalized NaN and Infinity strings. Infinity may be a bit different as the C side outputs inf yet Java wants Infinity.

Ultimately I need to be able to decode data written by the C++ app in Java. nan and inf cannot be decoded by Java as written. My focus was on NaN, but Infinity is a case that should be covered.

Upvotes: 0

Views: 2098

Answers (3)

klog
klog

Reputation: 486

A potentially more universal option would be to convert the floating-point number into hex when written to redis and then converted back when read. This would ensure that there wouldn't be any precision loss by converting it to a decimal string, but also cover the +/-Inf and NaN cases.

This is possible since both sides are using IEEE floating-point numbers.

Upvotes: 0

The Linux case is (a) correct behaviour according to ISO/IEC 9899:2001:

[%e, %E]

[...] A double argument representing an infinity or NaN is converted in the style of an f or F conversion specifier.

and the text for

A double argument representing an infinity is converted in one of the styles [-]inf or [-]infinity -- which style is implementation-defined. A double argument representing a NaN is converted in one of the styles [-]nan or [-]nan(n-char-sequence) -- which style, and the meaning of any n-char-sequence, is implementation-defined. The F conversion specifier produces INF, INFINITY, or NAN instead of inf, infinity, or nan, respectively.277)

I.e. the safest in Java would be to try to parse the double, and if that fails, lowercase the String and test the existence of - as the first character and the remainder after the possible sign with .startsWith("nan") and .startsWith("inf")

Of course this doesn't help if you need to support the non-standards-conforming Windows C runtimes.

Upvotes: 1

Shawn
Shawn

Reputation: 52364

Why not just use something like this in your java code for parsing a double?

double parseDouble(String s) throws NumberFormatException {
  try {
    return Double.valueOf(s);
  } catch (NumberFormatException e) {
    if (s.equalsIgnoreCase("nan")) {
      return Double.NaN;
    } else if (s.equalsIgnoreCase("inf") || s.equalsIgnoreCase("+inf")) {
      return Double.POSITIVE_INFINITY;
    } else if (s.equalsIgnoreCase("-inf")) {
      return Double.NEGATIVE_INFINITY;
    } else {
      throw e; // Invalid string
    }
  }
}

Upvotes: 1

Related Questions