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

Converting variable type (or workaround)

本文关键字:方法 变量 类型 转换      更新时间:2023-10-16

下面的类应该表示一个音符。我希望能够仅使用整数存储音符的长度(例如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型(变量,而不仅仅是它的值)?如果不是,那么根据我需要的类型,我还可以做些什么来使用整数或双精度来存储笔记的长度?

如果您唯一的问题是比较浮点数是否相等,那么我建议使用浮点数,但请先阅读"比较浮点数"/Bruce Dawson。它不长,它解释了如何正确比较两个浮点数(通过检查绝对和相对差)。

当你有更多的时间,你也应该看看"每个计算机科学家都应该知道的浮点算术"来理解为什么16偶尔会被"神秘地"存储为16.0000000001或15.99999999999。

在有理数(或定点算术)中使用整数的尝试很少像看起来那么简单。

我看到了几种可能的解决方案:第一种是使用double。这是诚然,扩展计算可能导致不准确的结果,但在在这种情况下,除数通常是2的幂,也就是正数结果(至少在我见过的所有机器上);你只会冒险当除以一些不寻常的值(也就是当你必须使用double时)。

您还可以缩放结果,例如将音符表示为64个音符的倍数。这将意味着大多数值都将是小整数,保证在double(至少)中是精确的在通常的表示中)。一个应该是16的数字就可以不是被存储为16.000000001或15.99999999(但这是一个数字)应该是。16可能会被存储为。1600000001或。1599999999)。在long long出现之前,十进制算术类经常出现采用double作为52位整型,保证了在每一步实际值是一个整数。(只有除法才会产生问题。)

或者你可以使用某种表示有理数的类。(比如Boost就有一个,我相信还有其他的。)这将允许任何奇怪的值(5音符,有人吗?)保持准确;它可以也有利于人类可读的输出,例如,您可以测试分母,然后输出类似"3个四分音符"的东西,或者喜欢的。即使像"3/4音符"这样的东西对a来说也更容易读懂音乐家比"一个0.75的音符"。

不能将一个变量从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;
}

这种方法的问题是,你绝对必须跟踪当前在联合中存储的是整型还是双精度。如果您存储一个整型,然后试图将其作为双精度体访问,就会发生不好的事情。最好是在自己的类中进行。

这是浮点变量的正常行为。它们总是四舍五入,最后一位数字的值可能会根据您所做的操作而改变。我建议在某个地方阅读浮点数(例如http://floating-point-gui.de/) -特别是关于比较fp值。

我通常减去它们,取绝对值并将其与epsilon进行比较,例如,如果(abs(x-y)

给定您有set_length(double d),我的猜测是您实际上需要双元。请注意,从双精度到整数小数的转换是脆弱和复杂的,并且很可能不会解决您的等式问题(0.24999999是否等于1/4 ?)。你最好选择总是使用分数,或者总是使用双精度。然后,学习如何使用它们。我必须说,对于音乐来说,分数是有意义的,就像音符是如何被描述的一样。

如果是我,我会直接使用enum。使用这个系统将一些转换为注释也非常简单。你可以这样做:

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) << " beatsn";
    std::cout << "Dotted quaver is equivalent to: " << dottedQuaver << " beatsn";
    return 0;
}

你所说的那些"不规则"笔记可以使用TieNotes检索