views:

169

answers:

6

I'm working with a database that has the limit that the only (numeric) datatype it can store is a double. What I want to do is pick the number for a certain row and put it into an HTTP request. The problem revolves around that I cannot know if this number should or should not have decimals.

For example, if the double is an ID, I cannot have any kind of formatting whatsoever, since the site that gets the HTTP request will be confused. Observe the following examples:

site.com/showid.php?id=12300000 // OK
site.com/showid.php?id=1.23E7 // Bad; scientific notation
site.com/showid.php?id=12300000.0 // Bad; trailing decimal

The solution to this would be to cast it to a long. Ignoring the problem of overflowing the long, it solves the scientific notation and (obviously) trailing decimal. This could be an acceptable solution but it would be nice if the code didn't assume it were IDs we were dealing with. What if, for example, I were to query a site that shows a map and the number are coordinates, where the decimals are very important? Then a cast to long is no longer acceptable.

In short;

  • If the double has no decimals, do not add a trailing decimal.
  • If it has decimals, keep them all.
  • Neither case should have scientific notation or thousand separators.

This solution will be ported to both C# and Java so I accept answers in both languages. (Oh, and I had no idea what to call this question, feel free to rename if you got something better.)

+2  A: 

Since it is safe to format the value with no trailing zeroes if it is integral (whether it represents an ID or a coordinate), why not just codify the logic you describe in your bullet points? For example (C#, but should translate readily to Java):

// Could also use Math.Floor, etc., to determine if it is integral
long integralPart = (long)doubleValue;
if ((double)integralPart == doubleValue)
{
  // has no decimals: format it as an integer e.g. integralPart.ToString("D") in C#
}
else
{
  // has decimals: keep them all e.g. doubleValue.ToString("F17")
}
itowlson
Isnt capital D used for dates?
mizipzor
In .NET, D or d, *when applied to a numeric value*, means "Integer digits with optional negative sign." See http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx for more info. It does mean something different when applied to DateTime values, but that won't affect you. And of course Java may use something different; sorry, not sufficiently familiar with Java.
itowlson
Yea, I thought it could discern between numeric and datetime values. But when running it I got a format string exception. Maybe made another mistake.
mizipzor
A: 

check whether num == round(num)

Itay
Part of the solution, but thats a nice way to check for decimals.
mizipzor
+1  A: 

How about encapsulating the number in a custom type?

public class IntelligentNumber
{
    private readonly double number;

    public IntelligentNumber(double number)
    {
        this.number = number;
    }

    public override string ToString()
    {
        long integralPart = (long)this.number;
        if((double)integralPart == this.number)
        {
            return integralPart.ToString();
        }
        else
        {
            return this.number.ToString();
        }
    }
}


See also Vilx-'s answer for a better algorithm than the one above.

Mark Seemann
While a custom type is a nice idea, the formatting happens in a very small module... double in, string out, nothing will change there about the interface. So a custom type would only be used for a short while inside that function. While it nicely wraps the logic, I think the codebase, on a while, will be clearer without yet another class.
mizipzor
Why the downvote?
Mark Seemann
A: 

In Java, you can do this with DecimalFormat.

static String format(double n) { 
    return new DecimalFormat("0.###########################").format(n); 
}

The # placeholders won't show up unless the number something other than zeros to put there, and the decimal point doesn't show up unless there's something following it.

gustafc
Any special reason for the number of #s? You dont happen to know the maximum number of decimals that can be printed to a string? Might as well go with that.
mizipzor
Just add a lot of them. The double hasn't got infinite precision anyway (nor do you need it), so about 16 decimal digits should be enough for all intents and purposes. But feel free to add more if you like them. :)
Vilx-
+3  A: 

To complement the answer of gustafc (who beat me by 1 minute), here's the relevant code line for C#:

MyDouble.ToString("0.################")

or

string.Format("{0:0.################}", MyDouble);
Vilx-
+1 Much better than my own answer
Mark Seemann
A: 

Heres my own conclusion:

  • Check if the double has decimals.
  • Depending on that, format the string accordingly.
  • And then something important; without specifying an invariant culture, the comma in the has-decimals case may be a "," instead of a "." which isnt liked by HTTP requests. Of course, this problem only crops up if your OS is set to a locale that prefers the comma.

    public static string DoubleToStringFormat(double dval)
    {
        long lval = (long)dval;
        if ((double)lval == dval)
        {
            // has no decimals: format as integer
            return dval.ToString("#.", CultureInfo.InvariantCulture);
        }
        else
        {
            // has decimals: keep them all
            return dval.ToString("0.##################", CultureInfo.InvariantCulture);
        }
    }
    
mizipzor