【问题标题】:Floating-point inaccuracy with math operations and integers [duplicate]数学运算和整数的浮点不准确性[重复]
【发布时间】:2014-07-07 00:25:31
【问题描述】:

嘿,所以我正在尝试学习 Java,我知道浮点数不准确等等,但我现在感觉很盲目。我知道我做错了什么,电脑没有错,但我不知道在哪里。

我将数学返回值转换为整数,然后从变量 cash 中减去特定的整数值;当我从浮点数中减去整数时,浮点数似乎丢失了数据。我的问题如下:在不减慢程序速度的情况下避免数据损坏的最明智方法是什么?

<!-- language: lang-java -->
private static void calculateChange()
{
    System.out.print("Please enter any amount of cash in dollars: ");
    float cash = scanner.nextFloat();

    int ten_dollars = (int) Math.floor(cash/10);
    cash -= 10 * ten_dollars;

    System.out.println(cash);

    int five_dollars = (int) Math.floor(cash/5);
    cash -= 5 * five_dollars;
    int dollars = (int) Math.floor(cash);
    cash -= dollars;
    int quarters = (int) Math.floor(cash/0.25);
    cash -= 0.25 * quarters;
    int dimes = (int) Math.floor(cash/0.1);
    cash -= 0.1 * dimes;
    int nickels = (int) Math.floor(cash/0.05);
    cash -= 0.05 * nickels;
    int pennies = (int) Math.floor(cash/0.01);

    System.out.println("This is equal to:\n" + ten_dollars + " ten dollar bills\n"
            + five_dollars + " five dollar bills\n"
            + dollars + " one dollar bills\n"
            + quarters + " quarters\n"
            + dimes + " dimes\n"
            + nickels + " nickels\n"
            + pennies + " pennies");
}

运行:

请输入任意金额的美元现金:135,04

5.0399933

这等于:

13 十美元的钞票

一张五美元的钞票

0 一美元的钞票

0 个季度

0 角钱

0 镍

3 便士

构建成功(总时间:6 秒)

【问题讨论】:

  • 不要使用float来存储货币,就是这样。它们不适合那个目的。
  • "floating-points not accuracy" 浮点 is 符合 IEEE 754 的规范。它们只是不像我们期望的那样“准确”是。正如 Jack 和无数其他人所说:不要使用浮点变量来表示货币值。使用int/long(一旦你通过了解析部分,这可能会让你的程序更快),或者如果你真的需要,使用BigDecimal(这会更慢并且比浮点更尴尬)
  • 您需要了解的第二件事是您的大多数 常量, 例如0.01,不能用浮点数精确表示。所以程序在执行之前并不精确:编译的常量是不精确的。您需要使用模块运算符。
  • 错误,取模运算,而不是乘法。
  • 我尝试将所有值乘以 100,然后将变量“cash”从 float 更改为 int 以查看它是否有效。然而它得到了错误的值。如果我使用所有整数并将数学返回为整数,为什么我得到错误数量的便士?

标签: java casting floating-point integer floating-accuracy


【解决方案1】:

我认为您将所有内容乘以 100 并将您的美元金额表示为 int 表示便士的数量是正确的。但是,如果您首先使用 getFloat 输入浮点值,则需要在乘以 100 后使用 Math.round 对值进行四舍五入

这行得通:

System.out.print("Please enter any amount of cash in dollars: ");
float cash = scanner.nextFloat();
int cashInPennies = Math.round(cash*100);
System.out.println(cashInPennies);
// If you say cashInPennies = (int)(cash*100), the result will be 13503 instead of
// 13504.

int ten_dollars = cashInPennies/1000;
cashInPennies %= 1000;

System.out.println(cashInPennies);

int five_dollars = cashInPennies/500;
cashInPennies %= 500;
int dollars = cashInPennies/100;
cashInPennies %= 100;
int quarters = cashInPennies/25;
cashInPennies %= 25;
int dimes = cashInPennies/10;
cashInPennies %= 10;
int nickels = cashInPennies/5;
cashInPennies %= 5;
int pennies = cashInPennies;
System.out.println("This is equal to:\n" + ten_dollars + " ten dollar bills\n"
        + five_dollars + " five dollar bills\n"
        + dollars + " one dollar bills\n"
        + quarters + " quarters\n"
        + dimes + " dimes\n"
        + nickels + " nickels\n"
        + pennies + " pennies");

【讨论】:

  • 我更喜欢这个,因为它不使用名为 BigDecimal 的锤子。
【解决方案2】:

你可以像这样使用BigDecimal

// Pass the amount in
private static void calculateChange(BigDecimal cash) {
  BigDecimal TEN = new BigDecimal(10);
  BigDecimal FIVE = new BigDecimal(5);
  BigDecimal QUARTER = new BigDecimal("0.25");
  BigDecimal TENTH = new BigDecimal("0.1");
  BigDecimal FIVE100ths = new BigDecimal("0.05");

  int ten_dollars = cash.divide(TEN).intValue();
  cash = cash.subtract(TEN.multiply(new BigDecimal(ten_dollars)));

  System.out.println(cash);

  int five_dollars = cash.divide(FIVE).intValue();
  cash = cash.subtract(FIVE.multiply(new BigDecimal(five_dollars)));
  int dollars = cash.divide(BigDecimal.ONE).intValue();
  cash = cash.subtract(new BigDecimal(dollars));
  int quarters = cash.divide(QUARTER).intValue();
  cash = cash.subtract(QUARTER.multiply(new BigDecimal(quarters)));
  int dimes = cash.divide(TENTH).intValue();
  cash = cash.subtract(TENTH.multiply(new BigDecimal(dimes)));
  int nickels = cash.divide(FIVE100ths).intValue();
  cash = cash.subtract(FIVE100ths.multiply(new BigDecimal(nickels)));
  int pennies = cash.multiply(new BigDecimal(100)).intValue();

  System.out.println("This is equal to:\n"
      + ten_dollars + " ten dollar bills\n"
      + five_dollars + " five dollar bills\n"
      + dollars + " one dollar bills\n" + quarters
      + " quarters\n" + dimes + " dimes\n" + nickels
      + " nickels\n" + pennies + " pennies");
}

【讨论】:

  • 感谢您抽出宝贵时间帮助我,但我试图避免使用 BigDecimal,因为它们会对性能产生重大影响。但是我肯定从您的代码示例中学到了关于 BigDecimals 的一两件事。谢谢。
  • @XMLParsing 它们确实会影响性能,但 premature optimization 是万恶之源 - Knuth.
  • @ElliottFrisch:为什么我只在发帖人实际上没有进行“过早优化”时才看到引用的内容?
  • @tmyklebu 在这里使用原语能提高多少性能?一个数量级,两个数量级?我们执行了多少次计算2025?你的手机有多少 MIPS?另外,有一个扫描仪正在使用,所以我们必须考虑 IO 等待。在那里花费了多少运行时间?
  • @ElliottFrisch:性能不是问题。在 Java 代码中使用 BigDecimal 很丑陋,而且阅读起来也没有乐趣。您还必须使用舍入模式和其他任何东西。基元让您可以在中缀中编写操作以便于理解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 2017-06-08
  • 2015-09-07
  • 2013-06-12
相关资源
最近更新 更多