Recovering formatted double

  • Thread starter Richard F.L.R.Snashall
  • Start date
R

Richard F.L.R.Snashall

In application code, I have code to format a double into
a String:

double db = 12.34;

java.text.DecimalFormat fourdigits =
new java.text.DecimalFormat( "0.0000" );

string db_in_string = " " + fourdigits.format( db );

and later, to recover the double:

double recovered_db;

try
{
recovered_db = Double.parseDouble( db_in_string );
}
catch( NumberFormatException numberFormatException )
{
// Generate error message
...

return;
}


I have a client in another country, however, that is having
trouble getting this code to function. Either they have to
initiate overriding the locale with English, or hand-correct
any values that are set (changing "," to ".", I believe).

How do I get this to function? It was my understanding that
Double.parseDouble was locale insensitive and would trim
any leading and trailing spaces.
 
D

Dimitri Maziuk

Richard F.L.R.Snashall sez:
In application code, I have code to format a double into
a String:

double db = 12.34;

java.text.DecimalFormat fourdigits =
new java.text.DecimalFormat( "0.0000" );

string db_in_string = " " + fourdigits.format( db );

and later, to recover the double:

double recovered_db;

try
{
recovered_db = Double.parseDouble( db_in_string );
}
catch( NumberFormatException numberFormatException )
{
// Generate error message
...

return;
}


I have a client in another country, however, that is having
trouble getting this code to function. Either they have to
initiate overriding the locale with English, or hand-correct
any values that are set (changing "," to ".", I believe).

How do I get this to function? It was my understanding that
Double.parseDouble was locale insensitive and would trim
any leading and trailing spaces.

The obvious answer would be to pass around a Double instead
of a String.

Dima
 
S

Stefan Ram

Richard F.L.R.Snashall said:
java.text.DecimalFormat fourdigits =
new java.text.DecimalFormat( "0.0000" );

You could fix the locale to a specific local such as
»java.util.Locale.US« by using something like:

public class Main
{ public static void main( final java.lang.String[] args )
throws java.lang.Throwable
{ final java.text.NumberFormat numberFormat =
java.text.NumberFormat.getInstance
( java.util.Locale.US );
numberFormat.setMinimumFractionDigits( 4 );
numberFormat.setMaximumFractionDigits( 4 );
final java.lang.String text = numberFormat.format( 1.2345 );
final double number = numberFormat.parse( text ).doubleValue();
java.lang.System.out.println( number ); }}
 
T

Thomas G. Marshall

Stefan Ram said something like:
You could fix the locale to a specific local such as
»java.util.Locale.US« by using something like:

That is not what he wants. If his code were to use the default locale:

NumberFormat nf = NumberFormat.getInstance(); // no param

Then the code would work properly no matter /where/ it was. If it were run
in England, it would allow

3,14159

for example.

NOTE: I've said this a million times in the past. If you are formatting or
parsing something that a user might enter, then you should always use
NumberFormat (or a derivative).

You will never know when a user might enter in 1,000 for 1000, or any number
of other digit grouping rules, many of which you might never guess at; they
are that weird.



public class Main
{ public static void main( final java.lang.String[] args )
throws java.lang.Throwable
{ final java.text.NumberFormat numberFormat =
java.text.NumberFormat.getInstance
( java.util.Locale.US );
numberFormat.setMinimumFractionDigits( 4 );
numberFormat.setMaximumFractionDigits( 4 );
final java.lang.String text = numberFormat.format( 1.2345 );
final double number = numberFormat.parse( text ).doubleValue();
java.lang.System.out.println( number ); }}
 
R

Richard F.L.R.Snashall

Dimitri said:
Richard F.L.R.Snashall sez:


The obvious answer would be to pass around a Double instead
of a String.

Not applicable. The string is sent to a user-modifiable text
field and recovered later (if not modified, of course).
 
R

Richard F.L.R.Snashall

Thomas said:
Stefan Ram said something like:



That is not what he wants. If his code were to use the default locale:

NumberFormat nf = NumberFormat.getInstance(); // no param

Then the code would work properly no matter /where/ it was. If it were run
in England, it would allow

3,14159

for example.

NOTE: I've said this a million times in the past. If you are formatting or
parsing something that a user might enter, then you should always use
NumberFormat (or a derivative).

This is where I started to get lost. The manual page says the method is
"static NumberFormat getInstance( )". Does that mean that the same
static value is always passed back? If so, how do I then make my own
copies? In the example I gave, I used four digits, but I also needed
other numbers of digits.

If I may, another question; why not getNumberInstance, since my
application is numeric only.
 
F

Filip Larsen

Richard F.L.R.Snashall wrote
In application code, I have code to format a double into
a String. [...]
I have a client in another country, however, that is having
trouble getting this code to function.

Use could your own DecimalFormat to format double string and use a
NumberFormat.getNumberInstance() to parse it back in. Or perhaps better,
round the double internally to the number of decimals you want and then
use NumberFormat.getNumberInstance() to both format and parse the
string.


Regards,
 
D

Dimitri Maziuk

Richard F.L.R.Snashall sez:
Not applicable. The string is sent to a user-modifiable text
field and recovered later (if not modified, of course).

In which case you want to make sure you're printing it out
in the default locale and reading it back in in the default
locale. Probably using DecimalFormat for both printing and
parsing.

IIRC Double.parseDouble() is pretty dumb: it won't read
scientific notation and I don't think it'll trim whitespace
either.

Dima
 
R

Richard F.L.R.Snashall

Stefan said:
Richard F.L.R.Snashall said:
java.text.DecimalFormat fourdigits =
new java.text.DecimalFormat( "0.0000" );


You could fix the locale to a specific local such as
»java.util.Locale.US« by using something like:

public class Main
{ public static void main( final java.lang.String[] args )
throws java.lang.Throwable
{ final java.text.NumberFormat numberFormat =
java.text.NumberFormat.getInstance
( java.util.Locale.US );
numberFormat.setMinimumFractionDigits( 4 );
numberFormat.setMaximumFractionDigits( 4 );
final java.lang.String text = numberFormat.format( 1.2345 );
final double number = numberFormat.parse( text ).doubleValue();
java.lang.System.out.println( number ); }}

I chose close to this:

// test version

final java.util.Locale myLocale = new java.util.Locale( "de" );

// final version

final java.util.Locale myLocale = java.util.Locale.getDefault( );

then, for formatting:

private java.text.NumberFormat digsFormat =
java.NumberFormat.getInstance( myLocale );

and:

digsFormat.setMinimumFractionDigits( 1 );
digsFormat.setMaximumFractionDigits( 4 );

and finally:

db_in_string = digsFormat( db );

For recovery, I mimicked Double.parseDouble with another function:

public java.text.NumberFormat DFormat =
java.text.NumberFormat.getInstance( myLocale );
public java.text.ParsePosition DPosition =
new java.text.ParsePosition( 0 );

public double DparseDouble( String S ) throws NumberFormatException
{
String T = S.trim( );
DPosition.setIndex( 0 );
Number parsedNumber = DFormat.parse( T, DPosition );

if( DPosition.getIndex( ) < T.length( ) )
{
throw new NumberFormatException( );
}

return parsedNumber.doubleValue( );
}

My thanks to all for your help.
 
T

Thomas Hawtin

Thomas said:
Stefan Ram said something like:

That is not what he wants. If his code were to use the default locale:

NumberFormat nf = NumberFormat.getInstance(); // no param

Then the code would work properly no matter /where/ it was. If it were run
in England, it would allow

3,14159

We use comma as a thousand separator over here in the UK locale. So
that, as in the US locale, is interpreted as 314,159.0.

In the UK, we do use a decimal point (a dot raised to the middle level
of a digit character) as a decimal point. However, localisation tends to
ignore that and go for what I assume is a vulgar Americanism.

Tom Hawtin
 
R

Roedy Green

In the UK, we do use a decimal point (a dot raised to the middle level
of a digit character) as a decimal point. However, localisation tends to
ignore that and go for what I assume is a vulgar Americanism.

does localisation know about that?

One problem with implementing that is raised dot has less font support
than period.
 
R

Richard F.L.R. Snashall

Stefan said:
Richard F.L.R.Snashall said:
java.text.DecimalFormat fourdigits =
new java.text.DecimalFormat( "0.0000" );


You could fix the locale to a specific local such as
»java.util.Locale.US« by using something like:

public class Main
{ public static void main( final java.lang.String[] args )
throws java.lang.Throwable
{ final java.text.NumberFormat numberFormat =
java.text.NumberFormat.getInstance
( java.util.Locale.US );
numberFormat.setMinimumFractionDigits( 4 );
numberFormat.setMaximumFractionDigits( 4 );
final java.lang.String text = numberFormat.format( 1.2345 );
final double number = numberFormat.parse( text ).doubleValue();
java.lang.System.out.println( number ); }}

(My ISP may get around to sending the original of this later;-)

I chose close to this:

// test version

final java.util.Locale myLocale = new java.util.Locale( "de" );

// final version

final java.util.Locale myLocale = java.util.Locale.getDefault( );

then, for formatting:

private java.text.NumberFormat digsFormat =
java.NumberFormat.getInstance( myLocale );

and:

digsFormat.setMinimumFractionDigits( 1 );
digsFormat.setMaximumFractionDigits( 4 );

and finally:

db_in_string = digsFormat( db );

For recovery, I mimicked Double.parseDouble with another function:

public java.text.NumberFormat DFormat =
java.text.NumberFormat.getInstance( myLocale );
public java.text.ParsePosition DPosition =
new java.text.ParsePosition( 0 );

public double DparseDouble( String S ) throws NumberFormatException
{
String T = S.trim( );
DPosition.setIndex( 0 );
Number parsedNumber = DFormat.parse( T, DPosition );

if( DPosition.getIndex( ) < T.length( ) )
{
throw new NumberFormatException( );
}

return parsedNumber.doubleValue( );
}

My thanks to all for your help.
 
R

Richard F.L.R.Snashall

Richard said:
For recovery, I mimicked Double.parseDouble with another function:

public java.text.NumberFormat DFormat =
java.text.NumberFormat.getInstance( myLocale );
public java.text.ParsePosition DPosition =
new java.text.ParsePosition( 0 );

public double DparseDouble( String S ) throws NumberFormatException
{
String T = S.trim( );
DPosition.setIndex( 0 );
Number parsedNumber = DFormat.parse( T, DPosition );

if( DPosition.getIndex( ) < T.length( ) )
{
throw new NumberFormatException( );
}

return parsedNumber.doubleValue( );
}

Oops! It needs an additional check for a zero length trimmed
string.
 
T

Thomas G. Marshall

Richard F.L.R.Snashall said something like:
This is where I started to get lost. The manual page says the method is
"static NumberFormat getInstance( )". Does that mean that the same
static value is always passed back? If so, how do I then make my own
copies? In the example I gave, I used four digits, but I also needed
other numbers of digits.

No, no, the format itself that you're using changes as you specify it.
Ok, here's the way it was designed, which I'm not particularly thrilled
with, but it's "eh, ok".

NumberFormat is an abstract class. It contains the factory methods for
instantiating it's various subclasses.

The various getInstance() methods defined with NumberFormat (see some below)
all call a private getInstance() which takes the desired locale and format
style (see below as well). The private getInstance() employs a cache of
number patterns. Don't ask me why this was considered so critical: I can't
imagine that any of this would have been trouble without the cache.

I would avoid direct instantiation of the various number formats, however,
only for maintainability reasons. Most coders are used to seeing one of the
NumberFormat factory methods used...

If I may, another question; why not getNumberInstance, since my
application is numeric only.

More factory method hooey. Just getInstance() will be fine, it is
equivalent.

Here, this is what the code actually looks like for those factory methods
within NumberFormat (jdk 5.0). Note that NUMBERFORMAT is one of the "type"
constants:

<elsewhere in NumberFormat.java>
// Constants used by factory methods to specify a style of format.
private static final int NUMBERSTYLE = 0;
private static final int CURRENCYSTYLE = 1;
private static final int PERCENTSTYLE = 2;
private static final int SCIENTIFICSTYLE = 3;
private static final int INTEGERSTYLE = 4;
</elsewhere>

<factory methods>
/**
* Returns a general-purpose number format for the current default
locale.
* This is the same as calling
* {@link #getNumberInstance() getNumberInstance()}.
*/
public final static NumberFormat getInstance() {
return getInstance(Locale.getDefault(), NUMBERSTYLE);
}

/**
* Returns a general-purpose number format for the specified locale.
* This is the same as calling
* {@link #getNumberInstance(java.util.Locale)
getNumberInstance(inLocale)}.
*/
public static NumberFormat getInstance(Locale inLocale) {
return getInstance(inLocale, NUMBERSTYLE);
}

/**
* Returns a general-purpose number format for the current default
locale.
*/
public final static NumberFormat getNumberInstance() {
return getInstance(Locale.getDefault(), NUMBERSTYLE);
}

/**
* Returns a general-purpose number format for the specified locale.
*/
public static NumberFormat getNumberInstance(Locale inLocale) {
return getInstance(inLocale, NUMBERSTYLE);
}
</factory>


...............more hooey..............and here is the private method I was
talking about:


private static NumberFormat getInstance(Locale desiredLocale,
int choice) {
/* try the cache first */
String[] numberPatterns =
(String[])cachedLocaleData.get(desiredLocale);
if (numberPatterns == null) { /* cache miss */
ResourceBundle resource =
LocaleData.getLocaleElements(desiredLocale
);
numberPatterns = resource.getStringArray("NumberPatterns");
/* update cache */
cachedLocaleData.put(desiredLocale, numberPatterns);
}

DecimalFormatSymbols symbols = new
DecimalFormatSymbols(desiredLocale);
int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice;
DecimalFormat format = new DecimalFormat(numberPatterns[entry],
symbols)
;

if (choice == INTEGERSTYLE) {
format.setMaximumFractionDigits(0);
format.setDecimalSeparatorAlwaysShown(false);
format.setParseIntegerOnly(true);
} else if (choice == CURRENCYSTYLE) {
format.adjustForCurrencyDefaultFractionDigits();
}

return format;
}
 
R

Richard F.L.R.Snashall

Thomas said:
Richard F.L.R.Snashall said something like:
DecimalFormat format = new DecimalFormat(numberPatterns[entry],
symbols)
;

That's the first piece I was missing... the alternate formulation
of the DecimalFormat constructor with the DecimalFormatSymbols...
and then using a DecimalFormat method to parse the input as well,
so the input format matched the output format.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top