下面的课程应该代表音符。我希望能够仅使用整数来存储音符的长度(例如1/2音符,1/4音符,3/8音符等)。但是,我还希望能够使用浮点数存储长度,以处理我处理不规则长度音符的罕见情况。
class note{
string tone;
int length_numerator;
int length_denominator;
public:
set_length(int numerator, int denominator){
length_numerator=numerator;
length_denominator=denominator;
}
set_length(double d){
length_numerator=d; // unfortunately truncates everything past decimal point
length_denominator=1;
}
}
能够使用整数而不是双精度来存储长度对我来说很重要的原因是,在我过去使用浮点数时,有时值会出乎意料地不准确。例如,一个应该是16的数字偶尔被神秘地存储为16.0000000001或15.99999999999(通常在经历一些操作之后)具有浮点,这可能在测试相等时导致问题(因为16!= 15.99999999999)。
是否可以将变量从int转换为double(变量,而不仅仅是其值)?如果没有,那么我还能做些什么来使用整数或双精度来存储音符的长度,具体取决于我需要的类型?
答案 0 :(得分:4)
如果您唯一的问题是比较浮点数是否相等,那么我会说要使用浮点数,但请先阅读"Comparing floating point numbers" / Bruce Dawson。它不长,它解释了如何正确比较两个浮点数(通过检查绝对和相对差异)。
当你有更多时间时,你也应该看"What Every Computer Scientist Should Know About Floating Point Arithmetic"来理解为什么16偶尔被“神秘地”存储为16.0000000001或15.99999999999。
尝试对有理数(或定点算术)使用整数很少像看起来那么简单。
答案 1 :(得分:3)
我看到了几种可能的解决方案:第一种是使用double。它的 确实,扩展计算可能会导致不准确的结果,但是 在这种情况下,你的除数通常是2的幂,这将给出精确的 结果(至少在我见过的所有机器上);你只冒风险 在划分一些不寻常的值时遇到问题(即 无论如何你必须使用双倍的情况。)
您还可以缩放结果,例如代表笔记为
第64个音符的倍数。这意味着大多数价值观都是如此
小整数,在double
中保证精确(至少再次
在通常的陈述中)。一个应该是16的数字
不存储为16.000000001或15.99999999(但是数字是
应该是.16可能存储为.1600000001或.1599999999)。
在出现long long
之前,经常会出现十进制算术类
使用double
作为52位整数类型,确保每一步都是
实际值恰好是整数。 (只有分裂可能会导致问题。)
或者您可以使用某种代表有理数的类。 (例如,Boost有一个,我相信还有其他人。)这样会 允许任何奇怪的值(第五个音符,任何人?)保持准确;它可能 对于人类可读输出也是有利的,例如,你可以测试一下 分母,然后输出像“3季票”,或者 喜欢。即使像“3/4音符”这样的东西也会更具可读性 音乐家比“.75音符”。
答案 2 :(得分:2)
无法将变量从int转换为double,可以将值从int转换为double。我不完全确定你要求的是什么,但也许你正在寻找一个工会
union DoubleOrInt
{
double d;
int i;
};
DoubleOrInt length_numerator;
DoubleOrInt length_denominator;
然后你可以写
set_length(int numerator, int denominator){
length_numerator.i=numerator;
length_denominator.i=denominator;
}
set_length(double d){
length_numerator.d=d;
length_denominator.d=1.0;
}
这种方法的问题在于,您绝对必须跟踪您当前是否在工会中存储整数或双精度数。如果存储int然后尝试以double形式访问它,则会发生错误。你最好在课堂上这样做。
答案 3 :(得分:0)
这是浮点变量的正常行为。它们总是四舍五入,最后的数字可能会根据您的操作而改变。我建议在某处读取浮点数(例如http://floating-point-gui.de/) - 特别是关于比较fp值。
我通常会减去它们,取绝对值并将其与epsilon进行比较,例如: if(abs(x-y)
答案 4 :(得分:0)
鉴于你有一个set_length(double d)
,我的猜测是你真的需要双打。请注意,从double到整数分数的转换是脆弱且复杂的,并且很可能无法解决您的等式问题(0.24999999等于1/4?)。你最好选择总是使用分数,或者总是加倍。然后,只学习如何使用它们。我必须说,对于音乐来说,分数是有意义的,因为它甚至是如何描述音符。
答案 5 :(得分:0)
如果是我,我会使用枚举。要将转换为,使用此系统也会非常简单。这是你可以做到的一种方式:
class Note {
public:
enum Type {
// In this case, 16 represents a whole note, but it could be larger
// if demisemiquavers were used or something.
Semiquaver = 1,
Quaver = 2,
Crotchet = 4,
Minim = 8,
Semibreve = 16
};
static float GetNoteLength(const Type ¬e)
{ return static_cast<float>(note)/16.0f; }
static float TieNotes(const Type ¬e1, const Type ¬e2)
{ return GetNoteLength(note1)+GetNoteLength(note2); }
};
int main()
{
// Make a semiquaver
Note::Type sq = Note::Semiquaver;
// Make a quaver
Note::Type q = Note::Quaver;
// Dot it with the semiquaver from before
float dottedQuaver = Note::TieNotes(sq, q);
std::cout << "Semiquaver is equivalent to: " << Note::GetNoteLength(sq) << " beats\n";
std::cout << "Dotted quaver is equivalent to: " << dottedQuaver << " beats\n";
return 0;
}
您可以使用TieNotes