为什么Scanner.nextDouble()和System.out.println在语言环境方面表现不同?

时间:2015-10-06 16:03:59

标签: java locale

我向初学程序员讲授Java,我的许多荷兰学生在他们第一次使用Scanner输入浮点值时感到困惑。

nextDouble()的默认行为是查阅计算机的区域设置。当设置为荷兰语时,这意味着必须使用小数逗号。

另一方面,System.out.println似乎不考虑语言环境并使用小数点。

例如考虑以下输出(用户输入为粗体):

  

给出低于10.0的数字

     

10,5

     

抱歉,10.5不低于10.0

(如果输入10.5,将抛出InputMismatchException。)上面的输出由以下片段生成

    Scanner scanner = new Scanner( System.in );
    double limit = 10.0;
    System.out.println( "Give a number below "+limit );
    double x = scanner.nextDouble();
    if (x >= limit ) {
        System.out.println( "Sorry, "+x+" is not below "+limit );
    } 

这是库中的不一致还是以错误的方式使用它?

2 个答案:

答案 0 :(得分:2)

提供给此调用的参数

System.out.println( "Sorry, "+x+" is not below "+limit );

是一个String,它是连接一些String文字和一些double值的结果。

The JLS states

  

如果只有一个操作数表达式是String类型,则在另一个操作数上执行string conversion (§5.1.11)以生成   运行时的字符串。

然后

  

原始类型x的值T首先转换为参考值   好像通过将它作为参数提供给适当的类实例   创作表达式(§15.9):

     
      
  • 如果Tdouble,请使用new Double(x)
  •   

然后继续说

  

否则,转换就像通过调用一样执行   没有参数的引用对象的toString方法;但如果   调用toString方法的结果是null,然后是字符串"null"   而是用来代替。

Double#toString的javadoc解释了格式

  

返回此Double对象的字符串表示形式。原始人   此对象表示的double值将转换为字符串   就像一个参数的方法toString一样。

这是重载的toString(double)方法

  

返回double参数的字符串表示形式。所有人物   下面提到的是ASCII字符。

     
      
  • 如果参数为NaN,则结果为字符串"NaN"
  •   
  • 否则,结果是一个字符串,表示参数的符号和大小(绝对值)。如果标志是否定的,   结果的第一个字符是'-' ('\u002D');如果标志是   正面,结果中不显示任何符号字符。至于   幅度m:      
        
    • 如果m为无穷大,则由字符"Infinity"表示;因此,正无穷大产生结果"Infinity"和   负无穷大产生结果"-Infinity"
    •   
    • 如果m为零,则由字符"0.0"表示;因此,负零产生结果"-0.0"并产生正零   结果“0.0”。
    •   
    • 如果m大于或等于10^-3但小于10^7,则表示为m的整数部分,十进制形式,不含   前导零,后跟'.' ('\u002E'),后跟一个或多个   表示m的小数部分的十进制数字。
    •   
    • 如果m小于10^-3或大于或等于10^7,则表示为所谓的“计算机化科学记数法”。让   n是唯一的整数,10^n ≤ m < 10n+1;然后让一个人成为   数学上精确的m和10^n的商,以便1 ≤ a < 10。该   然后将幅度表示为a的整数部分,作为单个   十进制数字,后跟'.' ('\u002E'),后跟十进制数字   代表a的小数部分,后跟字母'E'   ('\u0045'),后跟n表示十进制整数,如   由方法Integer.toString(int)生成。
    •   
  •   
     

ma的小数部分必须打印多少位数?   必须至少有一个数字来表示小数部分,并且   超出那个数量,但只需要更多,更多的数字   唯一地将参数值与相邻的类型值区分开来   双。也就是说,假设x是精确的数学值   由此方法生成的十进制表示法表示   有限的非零参数d。然后d必须是最近的double值   到x;或者,如果两个double值同等接近x,那么d必须为d   其中之一,0必须具有最重要的意义   是NumberFormat

     

要创建浮点值的本地化字符串表示,   使用double的子类。

介绍了如何正确格式化printf

或者,使用($) :: (a -> b) -> a -> b并为浮点值提供适当的格式模式。

答案 1 :(得分:1)

  • String concatenation,String.format,printf,Double.toString,Double.valueOf都使用十进制 point 而没有千分隔符,与java源代码兼容。幸运的是,这是基本的非本地化表示,对于数据的文本传输非常有用

  • Scanner,NumberFormat,MessageFormat涉及本地化,有时有点尴尬。它们用于高级代码,用于用户交互。还有千分隔符。

一个人必须选择一个。使用最低限度的本地化数字是一项很好的练习。或者在懒惰时,可以使用Double.parseDouble代替Scanner或:

Scanner scanner = new Scanner(System.in).useLocale(Locale.US);

另一个格式/值问题是double的使用,它是一个近似值,并且没有精度,作为固定点BigDecimal。双精度应该用于快速计算,BigDecimal用于财务精确度。

new BigDecimal("0.20") // Precision of 2, no loss.
0.20 // Not exactly 0.20, no precision on printing