转换变量类型(或变通方法)

时间:2011-08-11 06:54:54

标签: c++ types fractions

下面的课程应该代表音符。我希望能够仅使用整数来存储音符的长度(例如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(变量,而不仅仅是其值)?如果没有,那么我还能做些什么来使用整数或双精度来存储音符的长度,具体取决于我需要的类型?

6 个答案:

答案 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 &note) 
        { return static_cast<float>(note)/16.0f; }

    static float TieNotes(const Type &note1, const Type &note2)
        { 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

检索您所说的“不规则”注释