`cin` 错误地解析了 Mac 上的 `double` 输入

`cin` incorrectly parses `double` input on Mac

一些背景知识 - 我是学习 C++ 的新手。我正在研究 Accelerated C++ 并进行后续工作。在第 4 章中,我得到的代码并不像书上建议的那样有效,我已经仔细检查了我所有的代码以确保我没有从他们的示例中输入错误的东西(我不能看到任何错误,无论如何)。


我有以下结构和两个函数:

struct Student {
  std::string        name;
  double             midtermGrade, finalGrade;
  std::vector<double>homework;
};

std::istream& read(std::istream& is, Student& s) {
  is >> s.name >> s.midtermGrade >> s.finalGrade;

  std::cout << std::endl << "Name: " << s.name << std::endl
            << " - Md: " << s.midtermGrade << std::endl
            << " - Fn: " << s.finalGrade << std::endl;

  read_hw(is, s.homework);

  return is;
}

std::istream& read_hw(std::istream& in, std::vector<double>& hw) {
  if (in) {
    hw.clear();

    double h;

    while (in >> h) {
      std::cout << " - Hw: " << h << std::endl;
      hw.push_back(h);
    }

    in.clear();
  }

  return in;
}

int main() 中,我这样调用该代码:

Student record;
while (read(std::cin, record)) {

如果我编译并 运行,然后粘贴(或键入)以下输入,我得到以下输出:

// Input
Johnny 70 80 50 Fred 95 90 100 Joe 40 40 50

// Output
Name: Johnny
 - Md: 70
 - Fn: 80
 - Hw: 50

Name: red
 - Md: 95
 - Fn: 90
 - Hw: 100

Name: Joe
 - Md: 40
 - Fn: 40
 - Hw: 50

如您所见,"Fred" 变成了 "red"。我已经尝试了一大堆名字;以 A-F 开头的任何内容都受到相同问题的影响,这让我怀疑它可能是十六进制问题。但是,"Nick" 变成了 "k" 而 "Xander" 变成了 "r"。

我完全莫名其妙。你知道发生了什么事吗?


根据要求,这是一个 MCVE:

#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

struct Student {
  std::string        name;
  double             midtermGrade, finalGrade;
  std::vector<double>homework;
};

std::istream& read_hw(std::istream& in, std::vector<double>& hw) {
  if (in) {
    hw.clear();

    double h;

    while (in >> h) {
      std::cout << " - Hw: " << h << std::endl;
      hw.push_back(h);
    }

    in.clear();
  }

  return in;
}

std::istream& read(std::istream& is, Student& s) {
  is >> s.name >> s.midtermGrade >> s.finalGrade;

  std::cout << std::endl << "Name: " << s.name << std::endl
            << " - Md: " << s.midtermGrade << std::endl
            << " - Fn: " << s.finalGrade << std::endl;

  read_hw(is, s.homework);

  return is;
}

int main()
{
  Student record;

  std::vector<Student> students;

  while (read(std::cin, record)) {}

  return 0;
}

进一步研究后,libc++ 似乎没有按照您希望的方式从 cin 读取双精度值。我不确定它是否只是专门的双打,但我认为它可能是。它似乎只影响 OSX 上的 Clang 编译器,而且 真的 烦人:P

对于 TL;DR 版本,请参阅我已接受的 David 的回答。

我目前不知道有任何好的修复它,但是如果你添加编译器标志-stdlib=libstdc++你可以强制它使用一个旧的,弃用的stdlib 将按预期工作。显然它的功能较少(在我的示例中,缺少 std::sortstd::domain_error)。其他选项包括不使用 double 和对输入进行进一步解析(尽管我是 C++ 的新手,恐怕我没有关于那个的史酷比)。

相关阅读:

official bug report3.5年前开的,至今还没解决

如果您对如何决定某事物是否 double 有特定要求,则必须编写实现这些要求的代码。该标准允许丢弃可能属于有效浮点表示的任何前导字符。 "e"、"i"、"n" 和 "f" 等字母可以是有效浮点表示的一部分。

为什么这种编码从来都不是一个好主意,请参阅 Falsehoods Programmers Believe About Names,尤其是数字 15。