Search code examples
c++typesfractions

Converting variable type (or workaround)


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?


Solution

  • 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".