转换变量类型(或解决方法)

发布于 2024-11-29 06:50:51 字数 772 浏览 2 评论 0原文

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

The class below is supposed to represent a musical note. I want to be able to store the length of the note (e.g. 1/2 note, 1/4 note, 3/8 note, etc.) using only integers. However, I also want to be able to store the length using a floating point number for the rare case that I deal with notes of irregular lengths.

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;
    }
}

The reason it is important for me to be able to use integers rather than doubles to store the length is that in my past experience with floating point numbers, sometimes the values are unexpectedly inaccurate. For example, a number that is supposed to be 16 occasionally gets mysteriously stored as 16.0000000001 or 15.99999999999 (usually after enduring some operations) with floating point, and this could cause problems when testing for equality (because 16!=15.99999999999).

Is it possible to convert a variable from int to double (the variable, not just its value)? If not, then what else can I do to be able to store the note's length using either an integer or a double, depending on the what I need the type to be?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

跨年 2024-12-06 06:50:51

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

当你有更多时间的时候,你还应该看看"什么每个计算机科学家都应该了解浮点算术”,以理解为什么 16 偶尔会“神秘地”存储为 16.0000000001 或15.99999999999。

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

If your only problem is comparing floats for equality, then I'd say to use floats, but read "Comparing floating point numbers" / Bruce Dawson first. It's not long, and it explains how to compare two floating numbers correctly (by checking the absolute and relative difference).

When you have more time, you should also look at "What Every Computer Scientist Should Know About Floating Point Arithmetic" to understand why 16 occasionally gets "mysteriously" stored as 16.0000000001 or 15.99999999999.

Attempts to use integers for rational numbers (or for fixed point arithmetic) are rarely as simple as they look.

清晰传感 2024-12-06 06:50:51

我看到了几种可能的解决方案:第一个就是使用 double。它是
确实,扩展计算可能会导致结果不准确,但
在这种情况下,你的除数通常是 2 的幂,这将给出精确的
结果(至少在我见过的所有机器上);你只冒风险
除以一些不寻常的值(即
无论如何你都必须使用 double 的情况)。

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

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

I see several possible solutions: the first is just to use double. It's
true that extended computations may result in inaccurate results, but in
this case, your divisors are normally powers of 2, which will give exact
results (at least on all of the machines I've seen); you only risk
running into problems when dividing by some unusual value (which is the
case where you'll have to use double anyway).

You could also scale the results, e.g. representing the notes as
multiples of, say 64th notes. This will mean that most values will be
small integers, which are guaranteed exact in double (again, at least
in the usual representations). A number that is supposed to be 16 does
not get stored as 16.000000001 or 15.99999999 (but a number that is
supposed to be .16 might get stored as .1600000001 or .1599999999).
Before the appearance of long long, decimal arithmetic classes often
used double as a 52 bit integral type, ensuring at each step that the
actual value was exactly an integer. (Only division might cause a problem.)

Or you could use some sort of class representing rational numbers.
(Boost has one, for example, and I'm sure there are others.) This would
allow any strange values (5th notes, anyone?) to remain exact; it could
also be advantageous for human readable output, e.g. you could test the
denominator, and then output something like "3 quarter notes", or the
like. Even something like "a 3/4 note" would be more readable to a
musician than "a .75 note".

不顾 2024-12-06 06:50:51

无法将变量从 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 形式访问它,就会发生不好的事情。您最好在课堂上进行此操作。

It is not possible to convert a variable from int to double, it is possible to convert a value from int to double. I'm not completely certain which you are asking for but maybe you are looking for a union

union DoubleOrInt
{
  double d;
  int i;
};

DoubleOrInt length_numerator;
DoubleOrInt length_denominator;

Then you can write

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;
}

The problem with this approach is that you absolutely must keep track of whether you are currently storing ints or doubles in your unions. Bad things will happen if you store an int and then try to access it as a double. Preferrably you would do this inside your class.

过气美图社 2024-12-06 06:50:51

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

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

This is normal behavior for floating point variables. They are always rounded and the last digits may change valued depending on the operations you do. I suggest reading on floating points somewhere (e.g. http://floating-point-gui.de/) - especially about comparing fp values.

I normally subtract them, take the absolute value and compare this against an epsilon, e.g. if (abs(x-y)

吲‖鸣 2024-12-06 06:50:51

假设您有一个 set_length(double d),我的猜测是您实际上需要双打。请注意,从双精度数到整数分数的转换是脆弱且复杂的,并且很可能无法解决您的等式问题(0.24999999 等于 1/4 吗?)。您最好选择始终使用分数或始终使用双倍。然后,只需学习如何使用它们即可。我必须说,对于音乐来说,有分数是有意义的,因为它甚至是音符的描述方式。

Given you have a set_length(double d), my guess is that you actually need doubles. Note that the conversion from double to a fraction of integer is fragile and complexe, and will most probably not solve your equality problems (is 0.24999999 equal to 1/4 ?). It would be better for you to either choose to always use fractions, or always doubles. Then, just learn how to use them. I must say, for music, it make sense to have fractions as it is even how notes are being described.

花之痕靓丽 2024-12-06 06:50:51

如果是我,我只会使用枚举。使用这个系统将某些东西变成笔记也非常简单。您可以采用以下方法:

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 检索

If it were me, I would just use an enum. To turn something into a note would be pretty simple using this system also. Here's a way you could do it:

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;
}

Those 'Irregular' notes you speak of can be retrieved using TieNotes

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文