How can I output localized Java floating point values in full precision?
Asked Answered
S

2

6

If I format a double using the toString method, then I get the full precision of the double. For example (using Scala syntax):

java.text.MessageFormat.format ("{0}", Math.PI.toString)

will result in a string containing:

3.141592653589793

However, this value is not localized. If I pass the double value as a boxed Double, that is as:

java.text.MessageFormat.format ("{0}", Math.PI.asInstanceOf[AnyRef])

(AnyRef is the Scala type that is equivalent to Object in Java), then the result is a localized string with truncated precision:

3.142

The result is the same even if I add more of a hint to the format string:

java.text.MessageFormat.format ("{0,number}", Math.PI.asInstanceOf[AnyRef])

I can get the full precision by specifying a custom format:

java.text.MessageFormat.format ("{0,number,#.###############}", Math.PI.asInstanceOf[AnyRef])

but this requires some knowledge of the double's magnitude and precision. If I'm printing any double value (not just Pi), and want to retain the full precision of the value, then this approach means that I have to dynamically change the format string - and that's not exactly convenient if (as in my case) the format string is actually obtained from a resource bundle.

For example, consider a double value of 1.0e37. Converting to a string first and using "{0}" as the format string results in the appropriate, but unlocalized, string value 1.0E37. If I pass this as a boxed Double with a format string of "{0,number}" then I get the ridiculous value 10,000,000,000,000,000,000,000,000,000,000,000,000.

In short, I can't seem to find a format string that preserves the full precision of a double without first converting it to a string - and that's something I would prefer to avoid like the plague. Is there a number format string that outputs the full precision of a double as a localized value?

UPDATE:

Just to clarify why I would prefer not to have to convert doubles to strings:

  • toString returns, so far as I'm able to tell, a valid Java double literal value. As a string, the result is not localized when passed to MessageFormat.format. For example, say I that I want to output the localized form of the value 1234.567. toString would return "1234.567", and MessageFormat.format would leave it at that. Fine. I have my precision output. However, in Germany, this would make more sense if it appeared as "1.234,567". Even in the US/UK, this would look nicer if it appeared as "1,234.567".
  • If I pass boxed Integer, Boolean, or any other non floating-point primitive type to MessageFormat.format (with a simple "{n}" format), then I get nicely localized, full precision output. I do not need to - and should not, as I'll lose localized output - convert these values to strings first. This means that I have to treat doubles (and floats and BigDecimals) differently and convert them to strings before passing them to MessageFormat.format. (Moreover, I have to remember to do that every time.)
Splenetic answered 23/9, 2014 at 3:49 Comment(2)
I don't need a BigDecimal - a regular double is just fine for my purposes. Converting to a BigDecimal is just as cumbersome for me as converting to a string.Splenetic
In fact, I just tried converting PI to a BigDecimal and outputting... ...and got 3.142 as the result, so that doesn't work either. ;-)Splenetic
S
0

MessageFormat uses an localized representation, with thousands separators, maybe decimal comma and so on. In your usage it uses the default locale of your platform.

Something one should keep separate from the universal programmer's notation, valueOf/toString.

Using double and precision is a bit of an oxymoron, as the decimal representation is an approximation of the sum of powers of 2 that a double is internally.

To keep the full information you could store the raw bits in a textual form, using Double.doubleToRawLongBits and on the way back longBitsToDouble.

double x = Math.PI;
String repr = String.format("%016x", Double.doubleToRawLongBits(x));
x = Double.longBitsToDouble(Long.valueOf(repr, 16));
Syncope answered 23/9, 2014 at 14:5 Comment(2)
Thanks for your comments. I understand how doubles are stored internally, and I understand that there is no 100% precise way to represent that value as a decimal string. However, I would hope that you'd agree that toString does a pretty good job of outputting the full content of a double value in human-readable form. I just want the same fidelity when using MessageFormat.format with a double. Your suggestion preserves the accuracy, but most humans would struggle to understand the value presented.Splenetic
I was afraid so. There was once a SO question on the max number of places in a double, like this one [how-many-decimal-places-in-a-double-java].(#3334668).Syncope
D
-1

Double format in Java is represented by 64 bits, 52 bits are used for the mantissa. log(252) = 15.65355977. This means that the precision of Double is roughly 15-16 digits.

So I think that the result 3.141592653589793 is pretty much everything you are able to get from Double.

Dyarchy answered 23/9, 2014 at 12:53 Comment(6)
The question was: Is there a number format string that outputs the full precision of a double? This is not a question about increasing the precision of a floating point value, but a question about outputting the value stored in a double with as much accuracy as possible. I have pointed out that you get full precision if you first convert the double value to a string, but there doesn't appear to be a way to do it using MessageFormat.Splenetic
@MikeAllen sorry, I missed the point of your question. So, back to the topic. It appears that there is no simple and clear way to make MessageFormat format Doubles as you want. I can just point that giving custom pattern like "#.##########################" will work (as you mentioned), but it doesn't actually require knowledge about Double's precision. You can specify any number of digits after point and it will work. Maybe you can consider making format constant with some extra digits in the tail and using it where appropriate.Dyarchy
Thanks for the reply. The problem I'm struggling to communicate is that the custom formats, one way or another, truncate the accuracy/precision of the outputted value. If I knew that the magnitude of the double was, say, in the range 1-10, then the format you specify would be fine. But what if the value was 1.2345E-37? Your format would output 0.000000000000000 - so I'd lose all precision!Splenetic
But I can get all the precision if I first convert double values to strings via toString. I was hoping that the functionality in toString was available as a format specification that MessageFormat.format could use to retain the full precision of any double value.Splenetic
Ok, you are right again. It seems that Message format doesn't have such functionality. Why are you trying to avoid converting Double to String?Dyarchy
Well, the most obvious problem is that of I18N/L10N: toString returns, so far as I can tell, simply a valid Java representation of a double (formatted so that Java would recognize it as a double literal). MessageFormat.format is supposed to allow more localization of the output format (eg. 1.234,567 is the German equivalent of English 1,234.567). Secondly, I don't want to have to treat double arguments to MessageFormat.format differently to other arguments, although I may have to reconsider that.Splenetic

© 2022 - 2024 — McMap. All rights reserved.